11package net.thunderbird.gradle.plugin.app.versioning
22
33import org.gradle.api.DefaultTask
4+ import org.gradle.api.file.ConfigurableFileCollection
45import org.gradle.api.file.RegularFileProperty
56import org.gradle.api.provider.Property
67import org.gradle.api.tasks.Input
8+ import org.gradle.api.tasks.InputFile
79import org.gradle.api.tasks.InputFiles
810import org.gradle.api.tasks.Optional
911import org.gradle.api.tasks.OutputFile
10- import org.gradle.api.tasks.PathSensitive
11- import org.gradle.api.tasks.PathSensitivity
1212import org.gradle.api.tasks.TaskAction
13+ import java.io.File
14+ import javax.xml.parsers.DocumentBuilderFactory
15+ import javax.xml.xpath.XPathConstants
16+ import javax.xml.xpath.XPathFactory
1317
1418abstract class PrintVersionInfoTask : DefaultTask () {
1519 @get:Input
1620 abstract val applicationId: Property <String >
1721
18- @get:Input
19- abstract val applicationLabel: Property <String >
22+ @get:InputFile
23+ abstract val mergedManifest: RegularFileProperty
24+
25+ @get:InputFiles
26+ abstract val resourceFiles: ConfigurableFileCollection
2027
2128 @get:Input
2229 abstract val versionCode: Property <Int >
@@ -31,20 +38,16 @@ abstract class PrintVersionInfoTask : DefaultTask() {
3138 @get:Optional
3239 abstract val outputFile: RegularFileProperty
3340
34- @get:InputFiles
35- @get:Optional
36- @get:PathSensitive(PathSensitivity .RELATIVE )
37- abstract val stringsXmlFile: RegularFileProperty
38-
3941 init {
4042 outputs.upToDateWhen { false } // This forces Gradle to always re-run the task
4143 }
4244
4345 @TaskAction
4446 fun printVersionInfo () {
47+ val label = getApplicationLabel()
4548 val output = """
4649 APPLICATION_ID=${applicationId.get()}
47- APPLICATION_LABEL=${applicationLabel.get()}
50+ APPLICATION_LABEL=$label
4851 VERSION_CODE=${versionCode.get()}
4952 VERSION_NAME=${versionName.get()}
5053 VERSION_NAME_SUFFIX=${versionNameSuffix.get()}
@@ -57,4 +60,49 @@ abstract class PrintVersionInfoTask : DefaultTask() {
5760 outputFile.get().asFile.writeText(output + " \n " )
5861 }
5962 }
63+
64+ private fun getApplicationLabel (): String {
65+ val manifestFile = mergedManifest.get().asFile
66+ if (! manifestFile.exists()) return " Unknown"
67+
68+ val labelRaw = readManifestApplicationLabel(manifestFile) ? : return " Unknown"
69+
70+ // Return raw label if not a resource string
71+ val match = STRING_RESOURCE_REGEX .matchEntire(labelRaw.trim()) ? : return labelRaw
72+ val resourceName = match.groupValues[1 ]
73+
74+ val resolvedApplicationLabel = resourceFiles
75+ .map { it }
76+ .mapNotNull { dir -> File (dir, " values/strings.xml" ).takeIf { it.exists() } }
77+ .firstNotNullOfOrNull { stringResourceFile -> readStringResource(stringResourceFile, resourceName) }
78+
79+ return resolvedApplicationLabel ? : " Unknown"
80+ }
81+
82+ private fun readManifestApplicationLabel (manifest : File ): String? {
83+ val document = DocumentBuilderFactory .newInstance()
84+ .apply { isNamespaceAware = true }
85+ .newDocumentBuilder()
86+ .parse(manifest)
87+
88+ val apps = document.getElementsByTagName(" application" )
89+ if (apps.length == 0 ) return null
90+
91+ val appElement = apps.item(0 )
92+ return appElement.attributes?.getNamedItemNS(" http://schemas.android.com/apk/res/android" , " label" )?.nodeValue
93+ ? : appElement.attributes?.getNamedItem(" android:label" )?.nodeValue
94+ ? : appElement.attributes?.getNamedItem(" label" )?.nodeValue
95+ }
96+
97+ private fun readStringResource (stringResourceFile : File , resourceName : String ): String? {
98+ val xmlDocument = DocumentBuilderFactory .newInstance().newDocumentBuilder().parse(stringResourceFile)
99+ val xPath = XPathFactory .newInstance().newXPath()
100+ val expression = " /resources/string[@name='$resourceName ']/text()"
101+ val value = xPath.evaluate(expression, xmlDocument, XPathConstants .STRING ) as String
102+ return value.trim().takeIf { it.isNotEmpty() }
103+ }
104+
105+ private companion object {
106+ val STRING_RESOURCE_REGEX = " ^@string/([A-Za-z0-9_]+)$" .toRegex()
107+ }
60108}
0 commit comments