Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to define an Android string-array resource in Gradle?

In Gradle for Android, I'm trying to generate the equivalent of this string-array resource...

<resources>
    <string-array name="url_array">
       <item>http://www.url1.com</item>
       <item>http://www.url2.com</item>
       <item>http://www.url3.com</item>
    </string-array>
</resources>

...in my app's build.gradle config file. I don't want to hardcode these values in res/values/arrays.xml, since I want to be able to generate different array contents for different build variants (dev vs. prod). I know there are workarounds, but this would be the cleanest solution if it's possible.

I've tried things like the excerpt below with a resValue type of "string-array" or "array", but I get an error saying resValue() doesn't exist. Of course, a resValue type param of "string" works for single strings, but I need to generate a string array resource.

resValue "string-array", "url_array",
   ["http://www.url1.com",
    "http://www.url2.com",
    "http://www.url3.com"]

The Gradle for Android documentation doesn't help. It lists this method...

void resValue(String type, String name, String value)

...but it doesn't indicate the valid values for the type param. It simply has a link that says "See Resource Types" but that just points to the regular Android docs for resource types and doesn't describe how to express them in the Gradle Android DSL.

Does anyone have any guidance? I've looked all over online and haven't found anything.

like image 389
Devon Biere Avatar asked Jul 11 '16 21:07

Devon Biere


3 Answers

To be able to create array via resValue you shoul store all array elements inside item tag. So you should write in gradle something like this:

resValue('array', 'ver_2_skus', "<item>sku1</item><item>sku2</item>")

But there is an awful bug - all < symbols writes to res as &lt;. I spend some time to find how can I write < with gradle, but failed. So I made one hack - I replace < with some string and then, after build, replace it with <. I hate this hack, maybe I miss something, but it works. Here is code, that you must put to the end of gradle script (yes, code must be duplicated to work).

  1. While create an res array replace all < with some string, i.e. IWANTTOREPLACEITWITHLEFTARROW:
resValue('array', 'ver_2_skus', "IWANTTOREPLACEITWITHLEFTARROWitem>sku1IWANTTOREPLACEITWITHLEFTARROW/item>IWANTTOREPLACEITWITHLEFTARROWitem>sku2IWANTTOREPLACEITWITHLEFTARROW/item>")
  1. Add this to the end of gradle file:
android.applicationVariants.all { variant ->
    println("variant: "+variant.dirName)
    variant.mergeResources.doLast {
        try {
            println("3")
            ext.env = System.getenv()
            File valuesFile = file("${buildDir}/intermediates/res/merged/${variant.dirName}/values/values.xml")
            String content = valuesFile.getText('UTF-8')
            content = content.replaceAll(/IWANTTOREPLACEITWITHLEFTARROW/, '<')
            valuesFile.write(content, 'UTF-8')
        } catch (Exception e) {
            println("Exception = " + e)
        }
    }

    try {
        println("try")
        ext.env = System.getenv()
        File valuesFile = file("${buildDir}/intermediates/res/merged/${variant.dirName}/values/values.xml")
        String content = valuesFile.getText('UTF-8')
        content = content.replaceAll(/IWANTTOREPLACEITWITHLEFTARROW/, '<')
        valuesFile.write(content, 'UTF-8')
    } catch (Exception e) {
        println("Exception = " + e)
    }
}
like image 156
mohax Avatar answered Nov 20 '22 04:11

mohax


Here's the closest I got, with guidance from @schwiz. I still can't find a way to do this with resValue, but it's possible to define a buildConfigField that can accomplish the same goal:

buildConfigField "String[]", "URL_ARRAY",
        "{" +
                "\"http://www.url1.com\"," +
                "\"http://www.url2.com\"," +
                "\"http://www.url3.com\"" +
                "}"

That gives you access to the array, via BuildConfig:

public static final String[] URL_ARRAY = {
   "http://www.url1.com",
   "http://www.url2.com",
   "http://www.url3.com"}; // whitespace added for clarity

You can then override the buildConfigField value per buildType. So, unless you specifically need this to be in R.array.*, this will meet your needs. Leaving this open for now in case anyone else knows how to do this with resValue.

like image 17
Devon Biere Avatar answered Nov 20 '22 04:11

Devon Biere


Defining String[] of Urls in BuildConfig:

android {

    ...

    def tests1 = ["url-a1", "url-b1", "url-c1"]

    ext {
        tests2 = ["url-a2", "url-b2", "url-c2"]
    }

    // Specifies one flavor dimension.
    flavorDimensions "default"

    productFlavors {
        devel {
            dimension 'default'
        }
    }

    //Default values for all productFlavors
    productFlavors.all {
        ext.tests3 = ["url-a3", "url-b3", "url-c3"]
    } 

    android.applicationVariants.all { variant ->

        buildConfigField("String[]", "TESTS1", '{' + tests1.collect {
            "\"${it}\""
        }.join(",") + '}')

        buildConfigField("String[]", "TESTS2", '{' + android.ext.tests2.collect {
            "\"${it}\""
        }.join(",") + '}')

        //TEST3 works only when is some Flavor defined 
        buildConfigField("String[]", "TESTS3", '{' + variant.productFlavors.get(0).ext.tests3.collect {
            "\"${it}\""
        }.join(",") + '}')
    }
}
like image 2
maros136 Avatar answered Nov 20 '22 06:11

maros136