Search code examples
androidgradleandroid-resourcesandroid-buildgradle-kotlin-dsl

Unable to reference generated resource from code


I have a configuration template file which has placeholders to fill out based on the current build flavor (devDebug, prodRelease, etc.).

I've decided to create and inject a custom Gradle task into the build flow which properly runs and creates the correct configuration file into the main resources folder (main/res/raw).

My goal is to output the generated config file into the build folder's generated resources, e.g. $buildDir/generated/res/myconfig/dev/debug/raw/config.json. I can set the path for the output to be like it and the file gets created but I'm stuck when trying to access it from code. Also AS doesn't recognise the folder myconfig as resources root (even if explicitly marking it). What do I miss here?

Here's the Gradle task doing the work (it's Kotlin):

fun configureMyConfiguration(variant: ApplicationVariant) {
    getTasksByName(
        "generate${variant.name.capitalize()}Resources",
        true
    ).onEach { task ->
        task.dependsOn(
            task("${variant.name}ConfigureMyConfiguration") {
                group = "MyApp"
                description = "Configure my configuration"

                doLast {
                    val configRoot = File("$buildDir/generated/myconfig/${variant.flavorName}/${variant.buildType.name}/res")
                    val templateFile = "$rootDir/app/config_template.json"
                    val configFile = File(configRoot, "raw/my_config.json")
                    val config = File(templateFile).readText()
                        .replace("\${foo}", "bar")

                    configFile.ensureParentDirsCreated()
                    configFile.createNewFile()
                    configFile.writeText(config)

                    project.android.sourceSets.getByName(variant.name) {
                        resources.srcDir(configRoot)
                    }
                }
            }
        )
    }
}

The method to setup tasks is called when configuring application variants:

applicationVariants.all {
    outputs.forEach {
        configureMyConfiguration(this@all)
    }
}

As you can see folders and file gets created successfully in build folder:

generated resources

However referencing from code doesn't work I suppose because the generated sources are not recognised and assembled properly.

enter image description here

I tried to add the generated folder to the source sets in the android block of the gradle file - this way the folder got marked as resource root but referencing from code still not working.


Solution

  • While experimenting with Paul Ost's answer I came across a working solution.

    Basically all you need to do is calling variant.registerGeneratedResFolders(files(configRoot)) which is going to register the generated folder as resources root so that values there gonna be accessible through R..

    Important part is that this shouldn't be inside the task, but before the task injection and creation happen. I'm not sure why. Also, manually adding a source root is not needed, since it won't affect the outcome. See the corrected code for the task below.

    fun configureMyConfiguration(variant: ApplicationVariant) {
        val configRoot = File("$buildDir/generated/myconfig/${variant.flavorName}/${variant.buildType.name}/res")
    
        // Registering folder as recognized resourced root
        variant.registerGeneratedResFolders(files(configRoot))
    
        getTasksByName(
            "generate${variant.name.capitalize()}Resources",
            true
        ).onEach { task ->
            task.dependsOn(
                task("${variant.name}ConfigureMyConfiguration") {
                    group = "MyApp"
                    description = "Configure my configuration"
    
                    doLast {
                        val templateFile = "$rootDir/app/config_template.json"
                        val configFile = File(configRoot, "raw/my_config.json")
                        val config = File(templateFile).readText()
                            .replace("\${foo}", "bar")
    
                        configFile.ensureParentDirsCreated()
                        configFile.createNewFile()
                        configFile.writeText(config)
                    }
                }
            )
        }
    }