From dc0b00c7f522b22d420ca5841bdf76a8ae0a52e2 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Thu, 23 May 2024 23:11:14 +0530 Subject: [PATCH] fix(aaptcompiler): merge aaptcompiler from AOSP upstream --- .idea/compiler.xml | 34 +- build.gradle.kts | 1 + .../build-logic/plugins/build.gradle.kts | 2 +- gradle/libs.versions.toml | 3 +- shared/build.gradle.kts | 1 + subprojects/aaptcompiler/build.gradle.kts | 23 +- .../android/aaptcompiler/AaptResourceType.kt | 4 +- .../com/android/aaptcompiler/BlameLogger.kt | 66 +- .../android/aaptcompiler/ConfigDescription.kt | 8 + .../aaptcompiler/ConfigDescriptionParsers.kt | 465 +++++----- .../com/android/aaptcompiler/Container.kt | 0 .../android/aaptcompiler/FloatConstants.kt | 0 .../com/android/aaptcompiler/FloatParsing.kt | 2 +- .../com/android/aaptcompiler/LocaleValue.kt | 0 .../aaptcompiler/PseudolocaleGenerator.kt | 2 +- .../android/aaptcompiler/Pseudolocalizer.kt | 0 .../ResourceCompilationException.kt | 0 .../android/aaptcompiler/ResourceCompiler.kt | 240 +++-- .../com/android/aaptcompiler/ResourceFile.kt | 0 .../android/aaptcompiler/ResourcePathData.kt | 8 +- .../com/android/aaptcompiler/ResourceTable.kt | 303 +++--- .../com/android/aaptcompiler/ResourceUtils.kt | 120 ++- .../android/aaptcompiler/ResourceValues.kt | 0 .../com/android/aaptcompiler/SDKConstants.kt | 3 +- .../java/com/android/aaptcompiler/Source.kt | 0 .../com/android/aaptcompiler/StringPool.kt | 44 +- .../android/aaptcompiler/TableExtractor.kt | 859 +++++++----------- .../android/aaptcompiler/ToolFingerprint.kt | 0 .../com/android/aaptcompiler/UnicodeUtil.kt | 13 +- .../com/android/aaptcompiler/XmlProcessor.kt | 0 .../com/android/aaptcompiler/XmlResource.kt | 0 .../aaptcompiler/XmlResourceBuilder.kt | 0 .../android/aaptcompiler/XmlStringBuilder.kt | 14 +- .../java/com/android/aaptcompiler/XmlUtil.kt | 3 +- .../aaptcompiler/android/AConfiguration.kt | 26 + .../aaptcompiler/android/LocaleData.java | 0 .../aaptcompiler/android/LocaleDataTables.kt | 4 +- .../com/android/aaptcompiler/android/Ref.kt | 0 .../aaptcompiler/android/ResTableConfig.kt | 58 +- .../aaptcompiler/android/ResourceTypes.kt | 273 +++--- .../com/android/aaptcompiler/android/Util.kt | 1 + .../android/aaptcompiler/buffer/BigBuffer.kt | 0 .../aaptcompiler/proto/ProtoDeserialize.kt | 183 +++- .../aaptcompiler/proto/ProtoSerialize.kt | 236 +++-- .../com/android/aapt/Configuration.proto | 173 ++++ .../src/main/proto/com/android/aapt/README.md | 5 + .../proto/com/android/aapt/Resources.proto | 528 +++++++++++ .../com/android/aapt/ResourcesInternal.proto | 45 + 48 files changed, 2251 insertions(+), 1499 deletions(-) mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/AaptResourceType.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/BlameLogger.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescription.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescriptionParsers.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Container.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatConstants.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatParsing.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/LocaleValue.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/PseudolocaleGenerator.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Pseudolocalizer.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompilationException.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompiler.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceFile.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourcePathData.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceTable.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceUtils.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceValues.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/SDKConstants.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Source.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/StringPool.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/TableExtractor.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ToolFingerprint.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/UnicodeUtil.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlProcessor.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlResource.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlResourceBuilder.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlStringBuilder.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlUtil.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/AConfiguration.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleData.java mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleDataTables.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Ref.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResTableConfig.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Util.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/buffer/BigBuffer.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoDeserialize.kt mode change 100755 => 100644 subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoSerialize.kt create mode 100644 subprojects/aaptcompiler/src/main/proto/com/android/aapt/Configuration.proto create mode 100644 subprojects/aaptcompiler/src/main/proto/com/android/aapt/README.md create mode 100644 subprojects/aaptcompiler/src/main/proto/com/android/aapt/Resources.proto create mode 100644 subprojects/aaptcompiler/src/main/proto/com/android/aapt/ResourcesInternal.proto diff --git a/.idea/compiler.xml b/.idea/compiler.xml index e391a8312..972818729 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -121,6 +121,7 @@ + @@ -166,50 +167,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build.gradle.kts b/build.gradle.kts index 6f49ebe52..7c082ab8f 100755 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,6 +34,7 @@ plugins { alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.maven.publish) apply false alias(libs.plugins.gradle.publish) apply false + alias(libs.plugins.protobuf) apply false } buildscript { diff --git a/composite-builds/build-logic/plugins/build.gradle.kts b/composite-builds/build-logic/plugins/build.gradle.kts index 71f4e164a..e887b8c71 100644 --- a/composite-builds/build-logic/plugins/build.gradle.kts +++ b/composite-builds/build-logic/plugins/build.gradle.kts @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - `kotlin-dsl` + `kotlin-dsl` } repositories { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc7ba5ac7..46f2a34b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -110,7 +110,7 @@ google-auto-value-annotations = { module = "com.google.auto.value:auto-value-ann google-auto-value-ap = { module = "com.google.auto.value:auto-value", version = "1.10.4" } google-auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version = "1.1.1" } google-auto-service = { module = "com.google.auto.service:auto-service", version = "1.1.1" } -google-protobuf = { module = "com.google.protobuf:protobuf-java", version = "3.25.3" } +google-protobuf = { module = "com.google.protobuf:protobuf-javalite", version = "3.25.3" } google-java-format = { module = "com.google.googlejavaformat:google-java-format", version = "1.20.0" } google-flexbox = { module = "com.google.android.flexbox:flexbox", version = "3.0.0" } @@ -169,3 +169,4 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } maven-publish = { id = "com.vanniktech.maven.publish.base", version.ref = "maven-publish-plugin" } gradle-publish = { id = "com.gradle.plugin-publish", version = "1.2.1" } +protobuf = { id = "com.google.protobuf", version = "0.9.4" } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 87cabd9e4..bb8182dff 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -23,6 +23,7 @@ plugins { dependencies { implementation(libs.androidx.annotation) + implementation(libs.androidx.collection) implementation(libs.google.guava) implementation(libs.google.gson) implementation(projects.logger) diff --git a/subprojects/aaptcompiler/build.gradle.kts b/subprojects/aaptcompiler/build.gradle.kts index 13a4964c5..2fe30cb4c 100644 --- a/subprojects/aaptcompiler/build.gradle.kts +++ b/subprojects/aaptcompiler/build.gradle.kts @@ -15,11 +15,13 @@ * along with AndroidIDE. If not, see . */ +import com.google.protobuf.gradle.id import com.itsaky.androidide.build.config.BuildConfig plugins { id("com.android.library") id("kotlin-android") + id("com.google.protobuf") } android { @@ -32,15 +34,32 @@ android { } } +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:4.27.0" + } + + generateProtoTasks { + all().forEach { + it.builtins { + id("java") { + option("lite") + } + } + } + } +} + dependencies { implementation(libs.common.kotlin) implementation(libs.androidx.collection) - implementation(projects.logger) implementation(libs.composite.jaxp) + implementation(projects.logger) + implementation(projects.shared) + api(libs.aapt2.annotations) api(libs.aapt2.common) - api(libs.aapt2.proto) api(libs.google.protobuf) api(libs.composite.layoutlibApi) diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/AaptResourceType.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/AaptResourceType.kt old mode 100755 new mode 100644 index a6ed4a8e8..f4f8ef9cb --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/AaptResourceType.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/AaptResourceType.kt @@ -1,5 +1,7 @@ package com.android.aaptcompiler +import com.android.aaptcompiler.android.ResTableConfig + private const val ANIM_TAG = "anim" private const val ANIMATOR_TAG = "animator" private const val ARRAY_TAG = "array" @@ -29,7 +31,7 @@ private const val TRANSITION_TAG = "transition" private const val XML_TAG = "xml" fun resourceTypeFromTag(tag: String) = - when (tag) { + when(tag) { ANIM_TAG -> AaptResourceType.ANIM ANIMATOR_TAG -> AaptResourceType.ANIMATOR ARRAY_TAG -> AaptResourceType.ARRAY diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/BlameLogger.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/BlameLogger.kt old mode 100755 new mode 100644 index f187d866b..c9fd37b95 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/BlameLogger.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/BlameLogger.kt @@ -19,32 +19,36 @@ package com.android.aaptcompiler import com.android.ide.common.blame.SourceFilePosition import com.android.ide.common.blame.SourcePosition import com.android.utils.ILogger -import java.io.File import jaxp.xml.stream.Location +import java.io.File internal fun blameSource( source: Source, line: Int? = source.line, column: Int? = null -): BlameLogger.Source = BlameLogger.Source(source.path, line ?: -1, column ?: -1) +): BlameLogger.Source = + BlameLogger.Source(source.path, line ?: -1, column ?: -1) -internal fun blameSource(source: Source, location: Location): BlameLogger.Source = +internal fun blameSource( + source: Source, + location: Location +): BlameLogger.Source = BlameLogger.Source(source.path, location.lineNumber, location.columnNumber) -class BlameLogger -constructor( +class BlameLogger( val logger: ILogger, private val userVisibleSourceTransform: (String) -> String, val blameMap: (Source) -> Source = { it } ) { - @JvmOverloads - constructor( - logger: ILogger, - blameMap: (Source) -> Source = { it } - ) : this(logger, { it }, blameMap) + constructor(logger: ILogger, blameMap: (Source) -> Source = { it }) + : this(logger, { it }, blameMap) - data class Source(val sourcePath: String, val line: Int = -1, val column: Int = -1) { + data class Source( + val sourcePath: String, + val line: Int = -1, + val column: Int = -1 + ) { override fun toString(): String { var result = sourcePath @@ -58,7 +62,16 @@ constructor( } fun toSourceFilePosition() = - SourceFilePosition(File(sourcePath), SourcePosition(line, column, -1, line, column, -1)) + SourceFilePosition( + File(sourcePath), SourcePosition( + line, + column, + -1, + line, + column, + -1 + ) + ) companion object { fun fromSourceFilePosition(filePosition: SourceFilePosition) = @@ -79,27 +92,38 @@ constructor( } fun warning(message: String, source: Source? = null) { - if (source != null) logger.warning("${getOutputSource(source)}$message") - else logger.warning(message) + if (source != null) + logger.warning("${getOutputSource(source)}$message") + else + logger.warning(message) } fun info(message: String, source: Source? = null) { - if (source != null) logger.info("${getOutputSource(source)}$message") else logger.info(message) + if (source != null) + logger.info("${getOutputSource(source)}$message") + else + logger.info(message) } fun lifecycle(message: String, source: Source? = null) { - if (source != null) logger.lifecycle("${getOutputSource(source)}$message") - else logger.lifecycle(message) + if (source != null) + logger.lifecycle("${getOutputSource(source)}$message") + else + logger.lifecycle(message) } fun quiet(message: String, source: Source? = null) { - if (source != null) logger.quiet("${getOutputSource(source)}$message") - else logger.quiet(message) + if (source != null) + logger.quiet("${getOutputSource(source)}$message") + else + logger.quiet(message) } fun verbose(message: String, source: Source? = null) { - if (source != null) logger.verbose("${getOutputSource(source)}$message") - else logger.verbose(message) + if (source != null) + logger.verbose("${getOutputSource(source)}$message") + else + logger.verbose(message) } internal fun getOutputSource(source: Source): Source { diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescription.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescription.kt old mode 100755 new mode 100644 index 02e3795a8..def299ea0 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescription.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescription.kt @@ -63,6 +63,13 @@ fun parse(config: String): ConfigDescription { return configDescription } + if (parseGrammaticalInflection(parts[index], configDescription)) { + index++ + if (index == parts.size) { + return applyVersionForCompatibility(configDescription) + } + } + if(parseLayoutDirection(parts[index], configDescription)) { index++ if (index == parts.size) { @@ -221,6 +228,7 @@ private fun applyVersionForCompatibility(config: ConfigDescription): ConfigDescr config.layoutSize() != ResTableConfig.SCREEN_LAYOUT.SIZE_ANY || config.layoutLong() != ResTableConfig.SCREEN_LAYOUT.SCREENLONG_ANY || config.density != ResTableConfig.DENSITY.DEFAULT -> SDKConstants.SDK_DONUT + config.grammaticalInflection != ResTableConfig.GRAMMATICAL_GENDER.ANY -> SDKConstants.SDK_U else -> 0 } diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescriptionParsers.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescriptionParsers.kt old mode 100755 new mode 100644 index fb65b0cfd..8996c877b --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescriptionParsers.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ConfigDescriptionParsers.kt @@ -22,329 +22,326 @@ import com.android.aaptcompiler.android.isTruthy /** attempt to parse the Mobile Country Code */ fun parseMcc(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.mcc = 0 - return true - } + if (part == WILDCARD_NAME) { + config.mcc = 0 + return true + } - // mnc may have a dynamic sized value ranging from 2 to 3 digits. It is possible to have unique - // mcn such as 01 and 001 that are represented by the number digits. - if (!part.startsWith("mnc") || part.length !in 4..6) { - return false - } - val value = part.substring(3).toIntOrNull()?.toShort() ?: return false - config.mcc = value - return true + // The only acceptable format is "mccDDD" (where D is a digit). + if (!part.startsWith("mcc") || part.length != 6) { + return false + } + val value = part.substring(3).toIntOrNull()?.toShort() ?: return false + config.mcc = value + return true } /** attempt to parse the Mobile Network Code */ fun parseMnc(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.mnc = 0 + if (part == WILDCARD_NAME) { + config.mnc = 0 + return true + } + + // mnc may have a dynamic sized value ranging from 2 to 3 digits. It is possible to have unique + // mcn such as 01 and 001 that are represented by the number digits. + if (!part.startsWith("mnc") || part.length !in 4..6) { + return false + } + val value = part.substring(3).toIntOrNull()?.toShort() ?: return false + + // Zero is reserved, so we need to use MINC_ZERO instead. + config.mnc = if (value.isTruthy()) value else AConfiguration.ACONFIGURATION_MNC_ZERO.toShort() return true - } - - // The only acceptable format is "mncDDD" (where D is a digit). - if (!part.startsWith("mnc") || part.length != 6) { - return false - } - val value = part.substring(3).toIntOrNull()?.toShort() ?: return false - - // Zero is reserved, so we need to use MINC_ZERO instead. - config.mnc = if (value.isTruthy()) value else AConfiguration.ACONFIGURATION_MNC_ZERO.toShort() - return true } // TODO(@daniellabar): Make more like the aapt2 parser. -fun parseLayoutDirection(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT.DIR_ANY - "ldltr" -> ResTableConfig.SCREEN_LAYOUT.DIR_LTR - "ldrtl" -> ResTableConfig.SCREEN_LAYOUT.DIR_RTL - else -> return false +fun parseLayoutDirection(part: String, config: ConfigDescription) : Boolean { + val value = when(part) { + WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT.DIR_ANY + "ldltr" -> ResTableConfig.SCREEN_LAYOUT.DIR_LTR + "ldrtl" -> ResTableConfig.SCREEN_LAYOUT.DIR_RTL + else -> return false } - // AND existing config with the mask and then OR with the found layout direction. - config.screenLayout = - maskAndApply(config.screenLayout, ResTableConfig.SCREEN_LAYOUT.DIR_MASK, value) - return true + // AND existing config with the mask and then OR with the found layout direction. + config.screenLayout = + maskAndApply(config.screenLayout, ResTableConfig.SCREEN_LAYOUT.DIR_MASK, value) + return true } fun parseSmallestScreenWidthDp(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.smallestScreenWidthDp = 0 + if (part == WILDCARD_NAME) { + config.smallestScreenWidthDp = 0 + return true + } + if (!part.startsWith("sw") || !part.endsWith("dp")) { + return false + } + val value = part.substring(2, part.length-2).toIntOrNull() ?: return false + config.smallestScreenWidthDp = value return true - } - if (!part.startsWith("sw") || !part.endsWith("dp")) { - return false - } - val value = part.substring(2, part.length - 2).toIntOrNull() ?: return false - config.smallestScreenWidthDp = value - return true } fun parseScreenWidthDp(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.screenWidthDp = 0 + if (part == WILDCARD_NAME) { + config.screenWidthDp = 0 + return true + } + if (!part.startsWith('w') || !part.endsWith("dp")) { + return false + } + val value = part.substring(1, part.length-2).toIntOrNull() ?: return false + config.screenWidthDp = value return true - } - if (!part.startsWith('w') || !part.endsWith("dp")) { - return false - } - val value = part.substring(1, part.length - 2).toIntOrNull() ?: return false - config.screenWidthDp = value - return true } fun parseScreenHeightDp(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.screenHeightDp = 0 + if (part == WILDCARD_NAME) { + config.screenHeightDp = 0 + return true + } + if (!part.startsWith('h') || !part.endsWith("dp")) { + return false + } + val value = part.substring(1, part.length-2).toIntOrNull() ?: return false + config.screenHeightDp = value return true - } - if (!part.startsWith('h') || !part.endsWith("dp")) { - return false - } - val value = part.substring(1, part.length - 2).toIntOrNull() ?: return false - config.screenHeightDp = value - return true } fun parseScreenLayoutSize(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> 0 - "small" -> ResTableConfig.SCREEN_LAYOUT.SIZE_SMALL - "normal" -> ResTableConfig.SCREEN_LAYOUT.SIZE_NORMAL - "large" -> ResTableConfig.SCREEN_LAYOUT.SIZE_LARGE - "xlarge" -> ResTableConfig.SCREEN_LAYOUT.SIZE_XLARGE - else -> return false + val value = when (part) { + WILDCARD_NAME -> 0 + "small" -> ResTableConfig.SCREEN_LAYOUT.SIZE_SMALL + "normal" -> ResTableConfig.SCREEN_LAYOUT.SIZE_NORMAL + "large" -> ResTableConfig.SCREEN_LAYOUT.SIZE_LARGE + "xlarge" -> ResTableConfig.SCREEN_LAYOUT.SIZE_XLARGE + else -> return false } - // AND existing config with the mask and then OR with the found layout size. - config.screenLayout = - maskAndApply(config.screenLayout, ResTableConfig.SCREEN_LAYOUT.SIZE_MASK, value) - return true + // AND existing config with the mask and then OR with the found layout size. + config.screenLayout = + maskAndApply(config.screenLayout, ResTableConfig.SCREEN_LAYOUT.SIZE_MASK, value) + return true } fun parseScreenLayoutLong(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_ANY - "long" -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_YES - "notlong" -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_NO - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_ANY + "long" -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_YES + "notlong" -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_NO + else -> return false } - // AND existing config with the mask and then OR with the found layout size. - config.screenLayout = - maskAndApply(config.screenLayout, ResTableConfig.SCREEN_LAYOUT.SCREENLONG_MASK, value) - return true + // AND existing config with the mask and then OR with the found layout size. + config.screenLayout = + maskAndApply(config.screenLayout, ResTableConfig.SCREEN_LAYOUT.SCREENLONG_MASK, value) + return true } fun parseScreenRound(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_ANY - "round" -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_YES - "notround" -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_NO - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_ANY + "round" -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_YES + "notround" -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_NO + else -> return false } - config.screenLayout2 = - maskAndApply(config.screenLayout2, ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_MASK, value) - return true + config.screenLayout2 = + maskAndApply(config.screenLayout2, ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_MASK, value) + return true } fun parseWideColorGamut(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_ANY - "widecg" -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_YES - "nowidecg" -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_NO - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_ANY + "widecg" -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_YES + "nowidecg" -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_NO + else -> return false } - config.colorMode = - maskAndApply(config.colorMode, ResTableConfig.COLOR_MODE.WIDE_GAMUT_MASK, value) - return true + config.colorMode = + maskAndApply(config.colorMode, ResTableConfig.COLOR_MODE.WIDE_GAMUT_MASK, value) + return true } fun parseHdr(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.COLOR_MODE.HDR_ANY - "highdr" -> ResTableConfig.COLOR_MODE.HDR_YES - "lowdr" -> ResTableConfig.COLOR_MODE.HDR_NO - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.COLOR_MODE.HDR_ANY + "highdr" -> ResTableConfig.COLOR_MODE.HDR_YES + "lowdr" -> ResTableConfig.COLOR_MODE.HDR_NO + else -> return false } - config.colorMode = maskAndApply(config.colorMode, ResTableConfig.COLOR_MODE.HDR_MASK, value) - return true + config.colorMode = maskAndApply(config.colorMode, ResTableConfig.COLOR_MODE.HDR_MASK, value) + return true } fun parseOrientation(part: String, config: ConfigDescription): Boolean { - config.orientation = - when (part) { - WILDCARD_NAME -> ResTableConfig.ORIENTATION.ANY - "port" -> ResTableConfig.ORIENTATION.PORT - "land" -> ResTableConfig.ORIENTATION.LAND - "square" -> ResTableConfig.ORIENTATION.SQUARE - else -> return false + config.orientation = when (part) { + WILDCARD_NAME -> ResTableConfig.ORIENTATION.ANY + "port" -> ResTableConfig.ORIENTATION.PORT + "land" -> ResTableConfig.ORIENTATION.LAND + "square" -> ResTableConfig.ORIENTATION.SQUARE + else -> return false } - return true + return true } fun parseUiModeType(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.UI_MODE.TYPE_ANY - "desk" -> ResTableConfig.UI_MODE.TYPE_DESK - "car" -> ResTableConfig.UI_MODE.TYPE_CAR - "television" -> ResTableConfig.UI_MODE.TYPE_TELEVISION - "appliance" -> ResTableConfig.UI_MODE.TYPE_APPLIANCE - "watch" -> ResTableConfig.UI_MODE.TYPE_WATCH - "vrheadset" -> ResTableConfig.UI_MODE.TYPE_VR_HEADSET - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.UI_MODE.TYPE_ANY + "desk" -> ResTableConfig.UI_MODE.TYPE_DESK + "car" -> ResTableConfig.UI_MODE.TYPE_CAR + "television" -> ResTableConfig.UI_MODE.TYPE_TELEVISION + "appliance" -> ResTableConfig.UI_MODE.TYPE_APPLIANCE + "watch" -> ResTableConfig.UI_MODE.TYPE_WATCH + "vrheadset" -> ResTableConfig.UI_MODE.TYPE_VR_HEADSET + else -> return false } - config.uiMode = maskAndApply(config.uiMode, ResTableConfig.UI_MODE.TYPE_MASK, value) - return true + config.uiMode = maskAndApply(config.uiMode, ResTableConfig.UI_MODE.TYPE_MASK, value) + return true } fun parseUiModeNight(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.UI_MODE.NIGHT_ANY - "night" -> ResTableConfig.UI_MODE.NIGHT_YES - "notnight" -> ResTableConfig.UI_MODE.NIGHT_NO - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.UI_MODE.NIGHT_ANY + "night" -> ResTableConfig.UI_MODE.NIGHT_YES + "notnight" -> ResTableConfig.UI_MODE.NIGHT_NO + else -> return false } - config.uiMode = maskAndApply(config.uiMode, ResTableConfig.UI_MODE.NIGHT_MASK, value) - return true + config.uiMode = maskAndApply(config.uiMode, ResTableConfig.UI_MODE.NIGHT_MASK, value) + return true } fun parseDensity(part: String, config: ConfigDescription): Boolean { - if (part.isNotEmpty() && part[0].isLetter()) { - config.density = - when (part) { - WILDCARD_NAME -> ResTableConfig.DENSITY.DEFAULT - "anydpi" -> ResTableConfig.DENSITY.ANY - "nodpi" -> ResTableConfig.DENSITY.NONE - "ldpi" -> ResTableConfig.DENSITY.LOW - "mdpi" -> ResTableConfig.DENSITY.MEDIUM - "tvdpi" -> ResTableConfig.DENSITY.TV - "hdpi" -> ResTableConfig.DENSITY.HIGH - "xhdpi" -> ResTableConfig.DENSITY.XHIGH - "xxhdpi" -> ResTableConfig.DENSITY.XXHIGH - "xxxhdpi" -> ResTableConfig.DENSITY.XXXHIGH - else -> return false - } - return true - } else { - // we might be dealing with "dpi" - if (!part.endsWith("dpi")) { - return false + if (part.isNotEmpty() && part[0].isLetter()) { + config.density = when (part) { + WILDCARD_NAME -> ResTableConfig.DENSITY.DEFAULT + "anydpi" -> ResTableConfig.DENSITY.ANY + "nodpi" -> ResTableConfig.DENSITY.NONE + "ldpi" -> ResTableConfig.DENSITY.LOW + "mdpi" -> ResTableConfig.DENSITY.MEDIUM + "tvdpi" -> ResTableConfig.DENSITY.TV + "hdpi" -> ResTableConfig.DENSITY.HIGH + "xhdpi" -> ResTableConfig.DENSITY.XHIGH + "xxhdpi" -> ResTableConfig.DENSITY.XXHIGH + "xxxhdpi" -> ResTableConfig.DENSITY.XXXHIGH + else -> return false + } + return true + } else { + // we might be dealing with "dpi" + if (!part.endsWith("dpi")) { + return false + } + val value = part.substring(0, part.length-3).toIntOrNull() ?: return false + config.density = value + return true } - val value = part.substring(0, part.length - 3).toIntOrNull() ?: return false - config.density = value - return true - } } fun parseTouchscreen(part: String, config: ConfigDescription): Boolean { - config.touchscreen = - when (part) { - WILDCARD_NAME -> ResTableConfig.TOUCHSCREEN.ANY - "notouch" -> ResTableConfig.TOUCHSCREEN.NOTOUCH - "stylus" -> ResTableConfig.TOUCHSCREEN.STYLUS - "finger" -> ResTableConfig.TOUCHSCREEN.FINGER - else -> return false + config.touchscreen = when (part) { + WILDCARD_NAME -> ResTableConfig.TOUCHSCREEN.ANY + "notouch" -> ResTableConfig.TOUCHSCREEN.NOTOUCH + "stylus" -> ResTableConfig.TOUCHSCREEN.STYLUS + "finger" -> ResTableConfig.TOUCHSCREEN.FINGER + else -> return false } - return true + return true } fun parseKeysHidden(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_ANY - "keysexposed" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_NO - "keyshidden" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_YES - "keyssoft" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_SOFT - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_ANY + "keysexposed" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_NO + "keyshidden" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_YES + "keyssoft" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_SOFT + else -> return false } - config.inputFlags = - maskAndApply(config.inputFlags, ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_MASK, value) - return true + config.inputFlags = + maskAndApply(config.inputFlags, ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_MASK, value) + return true } fun parseKeyboard(part: String, config: ConfigDescription): Boolean { - config.keyboard = - when (part) { - WILDCARD_NAME -> ResTableConfig.KEYBOARD.ANY - "nokeys" -> ResTableConfig.KEYBOARD.NOKEYS - "qwerty" -> ResTableConfig.KEYBOARD.QWERTY - "12key" -> ResTableConfig.KEYBOARD.TWELVEKEY - else -> return false + config.keyboard = when (part) { + WILDCARD_NAME -> ResTableConfig.KEYBOARD.ANY + "nokeys" -> ResTableConfig.KEYBOARD.NOKEYS + "qwerty" -> ResTableConfig.KEYBOARD.QWERTY + "12key" -> ResTableConfig.KEYBOARD.TWELVEKEY + else -> return false } - return true + return true } fun parseNavHidden(part: String, config: ConfigDescription): Boolean { - val value = - when (part) { - WILDCARD_NAME -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_ANY - "navexposed" -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_NO - "navhidden" -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_YES - else -> return false + val value = when (part) { + WILDCARD_NAME -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_ANY + "navexposed" -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_NO + "navhidden" -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_YES + else -> return false } - config.inputFlags = - maskAndApply(config.inputFlags, ResTableConfig.INPUT_FLAGS.NAVHIDDEN_MASK, value) - return true + config.inputFlags = + maskAndApply(config.inputFlags, ResTableConfig.INPUT_FLAGS.NAVHIDDEN_MASK, value) + return true } fun parseNavigation(part: String, config: ConfigDescription): Boolean { - config.navigation = - when (part) { - WILDCARD_NAME -> ResTableConfig.NAVIGATION.ANY - "nonav" -> ResTableConfig.NAVIGATION.NONAV - "dpad" -> ResTableConfig.NAVIGATION.DPAD - "trackball" -> ResTableConfig.NAVIGATION.TRACKBALL - "wheel" -> ResTableConfig.NAVIGATION.WHEEL - else -> return false + config.navigation = when (part) { + WILDCARD_NAME -> ResTableConfig.NAVIGATION.ANY + "nonav" -> ResTableConfig.NAVIGATION.NONAV + "dpad" -> ResTableConfig.NAVIGATION.DPAD + "trackball" -> ResTableConfig.NAVIGATION.TRACKBALL + "wheel" -> ResTableConfig.NAVIGATION.WHEEL + else -> return false } + return true +} + +fun parseGrammaticalInflection(part: String, config: ConfigDescription): Boolean { + config.grammaticalInflection = when (part) { + WILDCARD_NAME -> ResTableConfig.GRAMMATICAL_GENDER.ANY + "neuter" -> ResTableConfig.GRAMMATICAL_GENDER.NEUTER + "feminine" -> ResTableConfig.GRAMMATICAL_GENDER.FEMININE + "masculine" -> ResTableConfig.GRAMMATICAL_GENDER.MASCULINE + else -> return false + } return true } fun parseScreenSize(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.screenWidth = 0 - config.screenHeight = 0 - return true - } + if (part == WILDCARD_NAME) { + config.screenWidth = 0 + config.screenHeight = 0 + return true + } - // Format is NxM - val width = part.substringBefore('x').toIntOrNull() ?: return false - val height = part.substringAfter('x').toIntOrNull() ?: return false - if (width < height) { - return false - } - config.screenWidth = width - config.screenHeight = height - return true + // Format is NxM + val width = part.substringBefore('x').toIntOrNull() ?: return false + val height = part.substringAfter('x').toIntOrNull() ?: return false + if (width < height) { + return false + } + config.screenWidth = width + config.screenHeight = height + return true } fun parseVersion(part: String, config: ConfigDescription): Boolean { - if (part == WILDCARD_NAME) { - config.sdkVersion = 0 + if (part == WILDCARD_NAME) { + config.sdkVersion = 0 + config.minorVersion = 0 + return true + } + + // Format is vN + if (!part.startsWith('v')) { + return false + } + val version = part.substring(1).toIntOrNull()?.toShort() ?: return false + config.sdkVersion = version config.minorVersion = 0 return true - } - - // Format is vN - if (!part.startsWith('v')) { - return false - } - val version = part.substring(1).toIntOrNull()?.toShort() ?: return false - config.sdkVersion = version - config.minorVersion = 0 - return true } fun maskAndApply(current: Byte, mask: Int, value: Byte) = - maskAndApply(current.toInt(), mask, value.toInt()).toByte() + maskAndApply(current.toInt(), mask, value.toInt()).toByte() +fun maskAndApply(current: Int, mask: Int, value: Int): Int = + (current and mask.inv()) or value -fun maskAndApply(current: Int, mask: Int, value: Int): Int = (current and mask.inv()) or value diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Container.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Container.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatConstants.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatConstants.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatParsing.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatParsing.kt old mode 100755 new mode 100644 index 58642a468..5bf5f109e --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatParsing.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/FloatParsing.kt @@ -676,4 +676,4 @@ fun parseFloatHex(lowerCaseText: String): Float? { val biasedExp = exponent + SINGLE_PRECISION_BIAS return Float.fromBits( (if (negative) FLOAT_NEGATIVE_MASK else 0) or currentMantissa or (biasedExp shl 23)) -} \ No newline at end of file +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/LocaleValue.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/LocaleValue.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/PseudolocaleGenerator.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/PseudolocaleGenerator.kt old mode 100755 new mode 100644 index 180a5438b..d5450e4b3 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/PseudolocaleGenerator.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/PseudolocaleGenerator.kt @@ -265,4 +265,4 @@ class PseudolocaleGenerator{ } } } -} \ No newline at end of file +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Pseudolocalizer.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Pseudolocalizer.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompilationException.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompilationException.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompiler.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompiler.kt old mode 100755 new mode 100644 index 6b37b72b1..4ff819b19 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompiler.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceCompiler.kt @@ -18,9 +18,9 @@ package com.android.aaptcompiler import com.android.aaptcompiler.proto.serializeTableToPb -import com.android.utils.FileUtils import com.itsaky.androidide.layoutlib.resources.ResourceType import com.itsaky.androidide.layoutlib.resources.ResourceVisibility +import com.android.utils.FileUtils import java.io.File private const val VALUES_DIRECTORY_PREFIX = "values" @@ -33,30 +33,29 @@ private const val PNG_EXTENSION = "png" * The different options available while compiling resources. * * @param generateSymbolsPathData Path where the R.txt file should be written to. If null, symbols - * will not be generated. + * will not be generated. * @param visibility The visibility of resources in the given file to process. If specified, then - * compilation of table files will fail if either the "public" or "public group" tags are - * encountered. + * compilation of table files will fail if either the "public" or "public group" tags are + * encountered. * @param requirePngCrunching whether PNG file crunching is enabled * @param pseudolocalize Whether or not the pseudolocales en-XA and ar-XB are generated for default - * string resources. - * @param partialRFile file to output symbols defined in the resource file (currently only supported - * for non-raw XML files (e.g. values or layouts) to match AAPT2's behaviour) + * string resources. + * @param partialRFile file to output symbols defined in the resource file (currently only + * supported for non-raw XML files (e.g. values or layouts) to match AAPT2's behaviour) * @param legacyMode Whether or not to error or warn on positional args not being specified in a - * translatable string with parameters. + * translatable string with parameters. * @param verbose Whether or not to show verbose logs. * @param sourcePath Specified source path to be written in the compiled resource. */ data class ResourceCompilerOptions( - val generateSymbolsPathData: ResourcePathData? = null, + val generateSymbolsPathData: ResourcePathData?= null, val visibility: ResourceVisibility? = null, val requirePngCrunching: Boolean = false, val pseudolocalize: Boolean = false, val partialRFile: File? = null, val legacyMode: Boolean = false, val verbose: Boolean = false, - val sourcePath: String? = null -) + val sourcePath: String? = null) /** * Shows whether the ResourceCompiler can compile the given Resource File. @@ -69,9 +68,8 @@ fun canCompileResourceInJvm(file: File, requirePngCrunching: Boolean): Boolean { if (file.isHidden) return true val pathData = extractPathData(file) - if ( - pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX && pathData.extension == XML_EXTENSION - ) { + if (pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX + && pathData.extension == XML_EXTENSION) { // file is a values table. return true } else { @@ -93,7 +91,7 @@ fun canCompileResourceInJvm(file: File, requirePngCrunching: Boolean): Boolean { * * @param file file to processed. How it is processed is determined by the resource compiler. * @param options the [ResourceCompilerOptions] that should be considered while compiling the - * resource. + * resource. * * Values files are compiled by extracting the all the resources and their values into a Resource * Table to be used in Linking. @@ -108,11 +106,7 @@ fun canCompileResourceInJvm(file: File, requirePngCrunching: Boolean): Boolean { * See [canCompileResourceInJvm] to see what is supported. */ fun compileResource( - file: File, - outputDirectory: File, - options: ResourceCompilerOptions, - logger: BlameLogger -) { + file: File, outputDirectory: File, options: ResourceCompilerOptions, logger: BlameLogger) { // Skip hidden files. if (file.isHidden) { @@ -129,28 +123,24 @@ fun compileResource( compileFunction(pathData, outputDirectory, options, logger) } catch (e: Exception) { logger?.info("Failed to compile file", blameSource(pathData.source)) - val message = - "Resource compilation failed (${e.message}. Cause: ${e.cause}). " + - "Check logs for more details." - throw ResourceCompilationException(message, e) + val message = + "Resource compilation failed (${e.message}. Cause: ${e.cause}). " + + "Check logs for more details." + throw ResourceCompilationException(message, e) } } -private fun getCompileMethod( - pathData: ResourcePathData, - logger: BlameLogger -): (ResourcePathData, File, ResourceCompilerOptions, BlameLogger) -> Unit { - if ( - pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX && pathData.extension == XML_EXTENSION - ) { +private fun getCompileMethod(pathData: ResourcePathData, logger: BlameLogger): + (ResourcePathData, File, ResourceCompilerOptions, BlameLogger) -> Unit { + if (pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX && + pathData.extension == XML_EXTENSION) { pathData.extension = RESOURCE_TABLE_EXTENSION return ::compileTable } else { val type = ResourceType.fromFolderName(pathData.resourceDirectory) if (type == null) { - val errorMsg = - "Invalid resource type '${pathData.resourceDirectory}'" + - " for file ${pathData.file.absolutePath}" + val errorMsg = "Invalid resource type '${pathData.resourceDirectory}'" + + " for file ${pathData.file.absolutePath}" logger?.warning(errorMsg) error(errorMsg) } @@ -168,45 +158,41 @@ private fun getCompileMethod( /** * Compiles a xml values file into a flattened [ResourceTable] proto. * - * After all resources are extracted from the given xml, if pseudolocalization is set, pseudolocale - * strings are created for each default string resource in the xml. The result is flattened to a - * file in the output directory. + * After all resources are extracted from the given xml, if pseudolocalization is set, + * pseudolocale strings are created for each default string resource in the xml. The result is + * flattened to a file in the output directory. * * @param pathData the file to be processed. * @param outputDirectory the directory to which the compiled file is to be placed. * @throws IllegalStateException A failure occurred in processing this resource. */ private fun compileTable( - pathData: ResourcePathData, - outputDirectory: File, - options: ResourceCompilerOptions, - logger: BlameLogger -) { + pathData: ResourcePathData, + outputDirectory: File, + options: ResourceCompilerOptions, + logger: BlameLogger) { val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename()) logger?.info("Compiling XML table ${pathData.file.absolutePath} to $outputFile") val table = ResourceTable(logger = logger) - val extractorOptions = - TableExtractorOptions( - translatable = !pathData.name.contains("donottranslate"), - errorOnPositionalArgs = !options.legacyMode, - visibility = options.visibility - ) + val extractorOptions = TableExtractorOptions( + translatable = !pathData.name.contains("donottranslate"), + errorOnPositionalArgs = !options.legacyMode, + visibility = options.visibility) val tableExtractor = TableExtractor(table, pathData.source, pathData.config, extractorOptions, logger) pathData.file.inputStream().use { - try { - tableExtractor.extract(it) - } catch (e: Exception) { - // For merged values there's no need to re-write as we don't know which line failed. The - // actual error will be raised by the table extractor. - throw ResourceCompilationException( - "Failed to compile values resource file ${pathData.file}", - e - ) - } + try { + tableExtractor.extract(it) + } catch (e: Exception) { + // For merged values there's no need to re-write as we don't know which line failed. The + // actual error will be raised by the table extractor. + throw ResourceCompilationException( + "Failed to compile values resource file ${pathData.file}", e + ) + } // Adds the fake locales: en-XA and ar-XB for each default-defined string resource. This is used // for debugging apps with long text (en-XA) or rtl (ar-XB) language support. @@ -232,23 +218,20 @@ private fun compileTable( pkg.groups.forEach { group -> // Macros do not define resources and therefore don't belong in the R.txt. if (group.type != AaptResourceType.MACRO) { - val javaType = if (group.type == AaptResourceType.STYLEABLE) "int[]" else "int" - group.entries.forEach { entry -> - val visibility = getVisibility(entry.value.values, group.type) - builder.appendLine( - "${visibility.getName()} $javaType ${group.type.tagName} ${entry.key}" - ) - if (group.type == AaptResourceType.STYLEABLE) { - // We also need to write down styleable children (entries) - group.getStyleable(entry).entries.forEach { - val childPackage = if (!it.name.pck.isNullOrEmpty()) "_${it.name.pck}" else "" - // styleables and their children are always public - builder.appendln( - "public int styleable ${entry.key}${childPackage}_${it.name.entry}" - ) - } + val javaType = if (group.type == AaptResourceType.STYLEABLE) "int[]" else "int" + group.entries.forEach { entry -> + val visibility = getVisibility(entry.value.values, group.type) + builder.appendln("${visibility.getName()} $javaType ${group.type.tagName} ${entry.key}") + if (group.type == AaptResourceType.STYLEABLE) { + // We also need to write down styleable children (entries) + group.getStyleable(entry).entries.forEach { + val childPackage = + if (!it.name.pck.isNullOrEmpty()) "_${it.name.pck}" else "" + // styleables and their children are always public + builder.appendln("public int styleable ${entry.key}${childPackage}_${it.name.entry}") + } + } } - } } } } @@ -257,24 +240,21 @@ private fun compileTable( } } -private fun getVisibility( - values: Collection, - type: AaptResourceType -): ResourceVisibility { - // all declare styleables need to be marked as public so they're visible in the R classes - if (type == AaptResourceType.STYLEABLE) return ResourceVisibility.PUBLIC - var foundPublic = false - var foundPrivate = false - values.forEach { - // We only care about public and private since only these can clash - if (it.visibility.level == ResourceVisibility.PUBLIC) foundPublic = true - else if (it.visibility.level == ResourceVisibility.PRIVATE) foundPrivate = true - } - if (foundPublic && foundPrivate) - error("Resource cannot be both public and private: ${type.tagName} ${values.first().name}") - if (foundPublic) return ResourceVisibility.PUBLIC - if (foundPrivate) return ResourceVisibility.PRIVATE - return ResourceVisibility.PRIVATE_XML_ONLY // default +private fun getVisibility(values: Collection, type: AaptResourceType): ResourceVisibility { + // all declare styleables need to be marked as public so they're visible in the R classes + if (type == AaptResourceType.STYLEABLE) return ResourceVisibility.PUBLIC + var foundPublic = false + var foundPrivate = false + values.forEach { + // We only care about public and private since only these can clash + if (it.visibility.level == ResourceVisibility.PUBLIC) foundPublic = true + else if (it.visibility.level == ResourceVisibility.PRIVATE) foundPrivate = true + } + if (foundPublic && foundPrivate) + error("Resource cannot be both public and private: ${type.tagName} ${values.first().name}") + if (foundPublic) return ResourceVisibility.PUBLIC + if (foundPrivate) return ResourceVisibility.PRIVATE + return ResourceVisibility.PRIVATE_XML_ONLY // default } /** @@ -284,21 +264,19 @@ private fun getVisibility( * @param outputDirectory the directory to which the compiled file is to be placed. */ private fun compileFile( - pathData: ResourcePathData, - outputDirectory: File, - options: ResourceCompilerOptions, - logger: BlameLogger? -) { + pathData: ResourcePathData, + outputDirectory: File, + options: ResourceCompilerOptions, + logger: BlameLogger?) { val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename()) logger?.info("Compiling file ${pathData.file.absolutePath} to $outputFile") pathData.file.inputStream().use { - val resourceFile = - ResourceFile( - ResourceName("", resourceTypeFromTag(pathData.resourceDirectory)!!, pathData.name), - pathData.config, - pathData.source, - ResourceFile.Type.Unknown - ) + val resourceFile = ResourceFile( + ResourceName("", resourceTypeFromTag(pathData.resourceDirectory)!!, pathData.name), + pathData.config, + pathData.source, + ResourceFile.Type.Unknown + ) val container = Container(outputFile.outputStream(), 1) container.addFileEntry(it, resourceFile) @@ -328,34 +306,34 @@ private fun compileFile( * @throws IllegalStateException A failure occurred in processing this resource. */ private fun compileXml( - pathData: ResourcePathData, - outputDirectory: File, - options: ResourceCompilerOptions, - logger: BlameLogger? -) { + pathData: ResourcePathData, + outputDirectory: File, + options: ResourceCompilerOptions, + logger: BlameLogger?) { val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename()) logger?.info("Compiling xml file ${pathData.file.absolutePath} to $outputFile") - val fileToProcess = - ResourceFile( - ResourceName("", pathData.type!!, pathData.name), - pathData.config, - pathData.source, - ResourceFile.Type.ProtoXml - ) + val fileToProcess = ResourceFile( + ResourceName("", pathData.type!!, pathData.name), + pathData.config, + pathData.source, + ResourceFile.Type.ProtoXml + ) pathData.file.inputStream().use { val xmlProcessor = XmlProcessor(source = pathData.source, logger = logger) try { - xmlProcessor.process(fileToProcess, it) + xmlProcessor.process(fileToProcess, it) } catch (e: Exception) { - throw ResourceCompilationException( - "Failed to compile resource file: " + logger?.getOutputSource(blameSource(pathData.source)), - e - ) + throw ResourceCompilationException( + "Failed to compile resource file: " + + logger?.getOutputSource(blameSource(pathData.source)), + e + ) } - val container = Container(outputFile.outputStream(), xmlProcessor.xmlResources.size) + val container = + Container(outputFile.outputStream(), xmlProcessor.xmlResources.size) for (resource in xmlProcessor.xmlResources) { container.addXmlEntry(resource) @@ -363,9 +341,9 @@ private fun compileXml( if (options.partialRFile != null) { val builder = StringBuilder() - builder.appendLine("default int ${pathData.type.tagName} ${pathData.name}") + builder.appendln("default int ${pathData.type.tagName} ${pathData.name}") fileToProcess.exportedSymbols.forEach { id -> - builder.appendLine("default int id ${id.name.entry}") + builder.appendln("default int id ${id.name.entry}") } FileUtils.writeToFile(options.partialRFile, builder.toString()) } @@ -381,18 +359,18 @@ private fun compileXml( * * If we are not running in debug mode, the png will undergo additional crunching. * - * Finally, the processed file is written to the output directory. It is possible that no processing - * is done to the png file, and if so it will just be written exactly as if it were a raw file. + * Finally, the processed file is written to the output directory. It is possible that no + * processing is done to the png file, and if so it will just be written exactly as if it were + * a raw file. * * @param pathData the file to process. * @param outputDirectory the directory in which the processed file will be placed. */ private fun compilePng( - pathData: ResourcePathData, - outputDirectory: File, - options: ResourceCompilerOptions, - logger: BlameLogger? -) { + pathData: ResourcePathData, + outputDirectory: File, + options: ResourceCompilerOptions, + logger: BlameLogger?) { logger?.info("Compiling image file ${pathData.file.absolutePath}") if (pathData.extension == PATCH_9_EXTENSION) { error("Patch 9 PNG processing is not supported with the JVM Android resource compiler.") diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceFile.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceFile.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourcePathData.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourcePathData.kt old mode 100755 new mode 100644 index 5363f9023..1b0079aca --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourcePathData.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourcePathData.kt @@ -60,17 +60,17 @@ fun extractPathData(file: File, sourcePath: String = file.absolutePath) : Resour else file.name val source = Source(sourcePath) // TODO(b/142481190): think about obfuscation - val parentName = file.parentFile!!.name + val parentName = file.parentFile.name val type = parentName.substringBefore("-") val config = if (type != parentName) { - file.parentFile!!.name.substringAfter(type).substring(1) + file.parentFile.name.substringAfter(type).substring(1) } else { "" } val configDescription = parse(config) return ResourcePathData( - source, extension.lowercase(), resName, type, config, file, configDescription) + source, extension.toLowerCase(), resName, type, config, file, configDescription) } /** @@ -103,4 +103,4 @@ internal fun compilationRename(f: File): String { } return parentName + "_" + fileName + ext + ".flat" -} \ No newline at end of file +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceTable.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceTable.kt old mode 100755 new mode 100644 index 52db36fc8..11ba0f13a --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceTable.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceTable.kt @@ -1,6 +1,6 @@ package com.android.aaptcompiler -import android.util.SparseArray +import androidx.collection.mutableIntObjectMapOf import com.android.aapt.Resources import com.itsaky.androidide.layoutlib.resources.ResourceVisibility import java.io.File @@ -17,10 +17,9 @@ import java.util.TreeMap * resources from libraries and are not validated. */ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLogger? = null) { - /** - * The string pool used by this resource table. Values that reference strings must use this pool - * to create their strings. + * The string pool used by this resource table. Values that reference strings must use + * this pool to create their strings. */ val stringPool = StringPool() @@ -31,7 +30,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog * Set of dynamic packages that this table may reference. Their package names get encoded into the * resources.arsc along with their compile-time assigned IDs. */ - private val includedPackages = SparseArray() + private val includedPackages = mutableIntObjectMapOf() enum class CollisionResult { KEEP_ORIGINAL, @@ -40,87 +39,64 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } data class SearchResult( - val tablePackage: ResourceTablePackage, - val group: ResourceGroup, - val entry: ResourceEntry - ) + val tablePackage: ResourceTablePackage, val group: ResourceGroup, val entry: ResourceEntry) /** * Adds a resource to the table with the given value. * * @param name The full name of this resource. This includes package name, resource type, and the - * entry name of the resource. + * entry name of the resource. * @param config The configuration this value for the given resource apply. * @param product The product for which this value applies. * @param value The value associated with the given resource. * @return Returns false if and only if the resource was not able to be added. This is caused if - * the resource name failed to be validated or there exists a conflict with a resource already in - * the table. + * the resource name failed to be validated or there exists a conflict with a resource already in + * the table. */ fun addResource( - name: ResourceName, - config: ConfigDescription, - product: String, - value: Value - ): Boolean = + name: ResourceName, config: ConfigDescription, product: String, value: Value): Boolean = addResourceImpl( - name, - 0, - config, - product, - value, - ::resourceNameValidator, - ::resolveValueCollision - ) + name, 0, config, product, value, ::resourceNameValidator, ::resolveValueCollision) + /** * Adds a resource to the table with the given id and value. * * @param name The full name of this resource. This includes package name, resource type, and the - * entry name of the resource. + * entry name of the resource. * @param id the id of the given resource. * @param config The configuration this value for the given resource apply. * @param product The product for which this value applies. * @param value The value associated with the given resource. * @return Returns false if and only if the resource was not able to be added. This is caused if - * the resource name failed to be validated, there exists a conflict with a resource already in - * the table, or the id is a dynamic id but does not match with the package, type, or entry. + * the resource name failed to be validated, there exists a conflict with a resource already in + * the table, or the id is a dynamic id but does not match with the package, type, or entry. */ fun addResourceWithId( name: ResourceName, id: Int, config: ConfigDescription, product: String, - value: Value - ): Boolean = + value: Value): Boolean = addResourceImpl( - name, - id, - config, - product, - value, - ::resourceNameValidator, - ::resolveValueCollision - ) + name, id, config, product, value, ::resourceNameValidator, ::resolveValueCollision) /** * Adds a file reference resource to the table with the given file path. * * @param name The full name of this resource. This includes package name, resource type and the - * entry name of the resource. + * entry name of the resource. * @param config The configuration this value for the given file reference applies. * @param source The place in source where the reference exists. * @param path The path name to the file being referenced. * @return Returns false if and only if the resource was not able to be added. This is caused if - * the resource name failed to be validated or there exists a conflict with a resource already in - * the table. + * the resource name failed to be validated or there exists a conflict with a resource already in + * the table. */ fun addFileReference( - name: ResourceName, - config: ConfigDescription, - source: Source, - path: String - ): Boolean = addFileReferenceImpl(name, config, source, path, null, ::resourceNameValidator) + name: ResourceName,config: ConfigDescription, source: Source, path: String): Boolean = + addFileReferenceImpl( + name, config, source, path, null, ::resourceNameValidator) /** * Same as [addFileReference], but doesn't verify the name of the file reference resource. This is @@ -128,65 +104,62 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog * resources. * * @param name The full name of this resource. This includes package name, resource type and the - * entry name of the resource. + * entry name of the resource. * @param config The configuration this value for the given file reference applies. * @param source The place in source where the reference exists. * @param path The path name to the file being referenced. * @param file The file object referenced by this resource. * @return Returns false if and only if the resource was not able to be added. This is caused if - * the resource name failed to be validated or there exists a conflict with a resource already in - * the table. + * the resource name failed to be validated or there exists a conflict with a resource already in + * the table. */ fun addFileReferenceMangled( name: ResourceName, config: ConfigDescription, source: Source, path: String, - file: File - ): Boolean = addFileReferenceImpl(name, config, source, path, file, ::skipNameValidator) + file: File): Boolean = + addFileReferenceImpl(name, config, source, path, file, ::skipNameValidator) /** * Same as [addResource], but doesn't verify the name of the resource. This is used when loading * resources from an existing binary resource table that may have mangled names. * * @param name The full name of this resource. This includes package name, resource type, and the - * entry name of the resource. + * entry name of the resource. * @param config The configuration this value for the given resource apply. * @param product The product for which this value applies. * @param value The value associated with the given resource. * @return Returns false if and only if the resource was not able to be added. This is caused if - * there exists a conflict with a resource already in the table. + * there exists a conflict with a resource already in the table. */ fun addResourceMangled( - name: ResourceName, - config: ConfigDescription, - product: String, - value: Value - ): Boolean = - addResourceImpl(name, 0, config, product, value, ::skipNameValidator, ::resolveValueCollision) + name: ResourceName, config: ConfigDescription, product: String, value: Value): Boolean = + addResourceImpl( + name, 0, config, product, value, ::skipNameValidator, ::resolveValueCollision) /** * Same as [addResourceWithId], but doesn't verify the name of the resource. This is used when * loading resources from an existing binary resource table that may have mangled names. * * @param name The full name of this resource. This includes package name, resource type, and the - * entry name of the resource. + * entry name of the resource. * @param id the id of the given resource. * @param config The configuration this value for the given resource apply. * @param product The product for which this value applies. * @param value The value associated with the given resource. * @return Returns false if and only if the resource was not able to be added. This is caused if - * there exists a conflict with a resource already in the table, or the id is a dynamic id but - * does not match with the package, type, or entry. + * there exists a conflict with a resource already in the table, or the id is a dynamic id but + * does not match with the package, type, or entry. */ fun addResourceWithIdMangled( name: ResourceName, id: Int, config: ConfigDescription, product: String, - value: Value - ) = - addResourceImpl(name, id, config, product, value, ::skipNameValidator, ::resolveValueCollision) + value: Value) = + addResourceImpl( + name, id, config, product, value, ::skipNameValidator, ::resolveValueCollision) /** * Sets the resource with the given name to the set visibility. If this resource has no value in @@ -235,13 +208,13 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } /** - * Returns the package struct with the given name, or null if such a package does not exist. The - * empty string is a valid package and typically is used to represent the 'current' package before - * it is known to the ResourceTable. + * Returns the package struct with the given name, or null if such a package does not + * exist. The empty string is a valid package and typically is used to represent the + * 'current' package before it is known to the ResourceTable. * * @param name the name of the package. - * @return the [ResourceTablePackage] with the requested name or {@code null} if that package does - * not exist in the table. + * @return the [ResourceTablePackage] with the requested name or {@code null} if that package + * does not exist in the table. */ fun findPackage(name: String): ResourceTablePackage? { return packages.find { it.name == name } @@ -260,7 +233,6 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog tablePackage.id = id return tablePackage } - tablePackage.id != id -> return null } } @@ -269,8 +241,8 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } /** - * Attempts to find a package having the specified name and ID. If not found, a new package of the - * specified parameters is created and returned. + * Attempts to find a package having the specified name and ID. If not found, a new package + * of the specified parameters is created and returned. */ fun createPackageAllowingDuplicateNames(name: String, id: Byte): ResourceTablePackage { val match = packages.find { it.name == name && it.id == id } @@ -291,15 +263,14 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog packages.add(newPackage) newPackage } - else -> tablePackage } } fun sort() { - packages.sortWith(compareBy({ it.name }, { it.id })) + packages.sortWith(compareBy({it.name}, {it.id})) for (pkg in packages) { - pkg.groups.sortWith(compareBy({ it.type.ordinal }, { it.id })) + pkg.groups.sortWith(compareBy({it.type.ordinal}, {it.id})) for (group in pkg.groups) { for (entryByName in group.entries.values) { for (entry in entryByName.values) { @@ -315,17 +286,13 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } private fun validateName( - nameValidator: (String) -> String, - name: ResourceName, - source: Source - ): Boolean { + nameValidator: (String) -> String, name: ResourceName, source: Source): Boolean { val badCodePoint = nameValidator.invoke(name.entry!!) if (badCodePoint.isNotEmpty()) { logError( blameSource(source), "Resource '$name' has invalid entry name '${name.entry}'. " + - "Invalid character '$badCodePoint'." - ) + "Invalid character '$badCodePoint'.") return false } return true @@ -338,8 +305,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog product: String, value: Value, nameValidator: (String) -> String, - conflictResolver: (Value, Value) -> CollisionResult - ): Boolean { + conflictResolver: (Value, Value) -> CollisionResult): Boolean { val source = value.source @@ -353,8 +319,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(value.source), "Failed to add resource '$name' with ID ${id.toString(16)} because " + - "package '${tablePackage.name}' already has ID ${packageId.toString(16)}." - ) + "package '${tablePackage.name}' already has ID ${packageId.toString(16)}.") return false } @@ -368,8 +333,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(value.source), "Failed to add resource '$name' with ID ${id.toString(16)} because type " + - "'${resourceGroup.type.tagName}' already has ID ${groupId.toString(16)}." - ) + "'${resourceGroup.type.tagName}' already has ID ${groupId.toString(16)}.") return false } @@ -380,8 +344,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(value.source), "Failed to add resource '$name' with ID ${id.toString(16)}, because resource already" + - " has ID ${resourceIdFromParts(packageId!!, groupId!!, entryId).toString(16)}." - ) + " has ID ${resourceIdFromParts(packageId!!, groupId!!, entryId).toString(16)}.") return false } @@ -399,11 +362,9 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(value.source), "Duplicate value for resource '$name' with config '$config' and product '$product'. " + - "Resource was previously defined here: $previousSource." - ) + "Resource was previously defined here: $previousSource.") return false } - CollisionResult.KEEP_ORIGINAL -> {} } } @@ -423,28 +384,19 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog source: Source, path: String, file: File?, - nameValidator: (String) -> String - ): Boolean { + nameValidator: (String) -> String): Boolean { val fileReference = FileReference(stringPool.makeRef(path)) fileReference.source = source fileReference.file = file return addResourceImpl( - name, - 0, - config, - "", - fileReference, - nameValidator, - ::resolveValueCollision - ) + name, 0, config, "", fileReference, nameValidator, ::resolveValueCollision) } private fun setVisibilityImpl( name: ResourceName, visibility: Visibility, id: Int, - nameValidator: (String) -> String - ): Boolean { + nameValidator: (String) -> String) : Boolean { val source = visibility.source if (!validateName(nameValidator, name, source)) { return false @@ -456,8 +408,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(source), "Failed to add resource '$name' with ID ${id.toString(16)} because package " + - "'${tablePackage.name}' already has ID ${packageId.toString(16)}." - ) + "'${tablePackage.name}' already has ID ${packageId.toString(16)}.") return false } @@ -465,14 +416,13 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog val useId = !validateResources && id.isValidDynamicId() val resourceGroup = - tablePackage.findOrCreateGroup(name.type, if (useId) id.getTypeId() else null) + tablePackage.findOrCreateGroup(name.type, if(useId) id.getTypeId() else null) val groupId = resourceGroup.id if (checkId && groupId != null && groupId != id.getTypeId()) { logError( blameSource(source), "Failed to add resource '$name' with ID ${id.toString(16)} because type " + - "'${resourceGroup.type.tagName}' already has ID ${groupId.toString(16)}." - ) + "'${resourceGroup.type.tagName}' already has ID ${groupId.toString(16)}.") return false } @@ -483,8 +433,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(source), "Failed to add resource '$name' with ID ${id.toString(16)}, because resource already " + - "has ID ${resourceIdFromParts(packageId!!, groupId!!, entryId).toString(16)}." - ) + "has ID ${resourceIdFromParts(packageId!!, groupId!!, entryId).toString(16)}.") return false } @@ -501,30 +450,25 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog when { visibility.level == ResourceVisibility.UNDEFINED && - resourceEntry.visibility.level != ResourceVisibility.UNDEFINED -> { + resourceEntry.visibility.level != ResourceVisibility.UNDEFINED -> { // We can't undefine a symbol. Ignore } - visibility.level == ResourceVisibility.PRIVATE && - resourceEntry.visibility.level == ResourceVisibility.PUBLIC -> { + resourceEntry.visibility.level == ResourceVisibility.PUBLIC -> { logError( - blameSource(source), - "Failed to add resource '$name' as private (java-symbol) because it was " + - "previously defined as public." - ) + blameSource(source), + "Failed to add resource '$name' as private (java-symbol) because it was " + + "previously defined as public.") return false } - visibility.level == ResourceVisibility.PUBLIC && - resourceEntry.visibility.level == ResourceVisibility.PRIVATE -> { + resourceEntry.visibility.level == ResourceVisibility.PRIVATE -> { logError( - blameSource(source), - "Failed to add resource '$name' as public because it was previously defined as " + - "private (java-symbol)." - ) + blameSource(source), + "Failed to add resource '$name' as public because it was previously defined as " + + "private (java-symbol).") return false } - else -> { // This symbol definition takes precedence. resourceEntry.visibility = visibility @@ -535,10 +479,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } private fun setAllowNewImpl( - name: ResourceName, - allowNew: AllowNew, - nameValidator: (String) -> String - ): Boolean { + name: ResourceName, allowNew: AllowNew, nameValidator: (String) -> String): Boolean { if (!validateName(nameValidator, name, allowNew.source)) { return false } @@ -551,10 +492,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } private fun setOverlayableImpl( - name: ResourceName, - overlayable: OverlayableItem, - nameValidator: (String) -> String - ): Boolean { + name: ResourceName, overlayable: OverlayableItem, nameValidator: (String) -> String): Boolean { if (!validateName(nameValidator, name, overlayable.source)) { return false } @@ -570,8 +508,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog logError( blameSource(overlayable.source), "Failed to add overlayable declaration for resource '$name', because resource already " + - "has an overlayable defined here: $previousSource." - ) + "has an overlayable defined here: $previousSource.") return false } entry.overlayable = overlayable @@ -579,23 +516,20 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog } companion object { - - fun resolveValueCollision(existing: Value, incoming: Value): CollisionResult { + fun resolveValueCollision(existing: Value, incoming: Value):CollisionResult { val existingAttr = existing as? AttributeResource val incomingAttr = incoming as? AttributeResource - incomingAttr - ?: return when { - incoming.weak -> CollisionResult.KEEP_ORIGINAL - existing.weak -> CollisionResult.TAKE_NEW - else -> CollisionResult.CONFLICT - } + incomingAttr ?: return when { + incoming.weak -> CollisionResult.KEEP_ORIGINAL + existing.weak -> CollisionResult.TAKE_NEW + else -> CollisionResult.CONFLICT + } - existingAttr - ?: return when { - existing.weak -> CollisionResult.TAKE_NEW - else -> CollisionResult.CONFLICT - } + existingAttr ?: return when { + existing.weak -> CollisionResult.TAKE_NEW + else -> CollisionResult.CONFLICT + } // Attribute specific handling. Since declarations and definitions of attributes can happen // almost anywhere, we need special handling to see which definition sticks. @@ -631,18 +565,21 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog class Visibility( val source: Source = Source(""), val comment: String = "", - val level: ResourceVisibility = ResourceVisibility.UNDEFINED -) + val level: ResourceVisibility = ResourceVisibility.UNDEFINED) /** Represents in an overlay */ -class AllowNew(val source: Source, val comment: String) +class AllowNew ( + val source: Source, + val comment: String) -class Overlayable(val name: String, val actor: String, val source: Source) { +class Overlayable ( + val name: String, + val actor: String, + val source: Source) { - constructor() : this("", "", Source("")) + constructor(): this("","", Source("")) companion object { - const val ACTOR_SCHEME = "overlay" const val ACTOR_SCHEME_URI = "$ACTOR_SCHEME://" } @@ -659,8 +596,7 @@ class OverlayableItem( val overlayable: Overlayable, val policies: Int = Policy.NONE, val comment: String = "", - val source: Source = Source("") -) { + val source: Source = Source("")) { override fun equals(other: Any?): Boolean { if (other is OverlayableItem) { @@ -671,27 +607,19 @@ class OverlayableItem( /** Represents the types of overlays that are allowed to overlay the resource. */ object Policy { - const val NONE = 0 - /** The resource can be overlaid by any overlay. */ const val PUBLIC = 1 shl 0 - /** The resource can be overlaid by any overlay on the system partition. */ const val SYSTEM = 1 shl 1 - /** The resource can be overlaid by any overlay on the vendor partition. */ const val VENDOR = 1 shl 2 - /** The resource can be overlaid by any overlay on the product partition. */ const val PRODUCT = 1 shl 3 - /** The resource can be overlaid by any overlay signed with the same signature as its actor. */ const val SIGNATURE = 1 shl 4 - /** The resource can be overlaid by any overlay on the odm partition. */ const val ODM = 1 shl 5 - /** The resource can be overlaid by any overlay on the oem partition. */ const val OEM = 1 shl 6 } @@ -704,21 +632,21 @@ class ResourceTablePackage(var name: String = "", var id: Byte? = null) { fun findGroup(type: AaptResourceType, groupId: Byte? = null) = if (groupId != null) { - groups.find { type == it.type && groupId == it.id } - ?: groups.find { type == it.type && it.id == null } + groups.find {type == it.type && groupId == it.id} ?: + groups.find {type == it.type && it.id == null} } else { - groups.find { type == it.type } + groups.find {type == it.type} } fun findOrCreateGroup(type: AaptResourceType, groupId: Byte? = null): ResourceGroup { - return when (val group = findGroup(type, groupId)) { + val group = findGroup(type, groupId) + return when(group) { null -> { val newGroup = ResourceGroup(type) newGroup.id = groupId groups.add(newGroup) newGroup } - else -> group } } @@ -728,8 +656,7 @@ class ResourceTablePackage(var name: String = "", var id: Byte? = null) { * Represents all resource entries grouped under a resource type (eg. string, drawable, layout, * etc.). */ -class ResourceGroup(val type: AaptResourceType) { - +class ResourceGroup(val type : AaptResourceType) { var id: Byte? = null var visibility = ResourceVisibility.UNDEFINED @@ -737,15 +664,15 @@ class ResourceGroup(val type: AaptResourceType) { // To get Styleable's children we need to reach the ResourceEntry's value first internal fun getStyleable(entry: Map.Entry>): Styleable { - // To get the actual value we need to find the correct Item that's nested deep in the map - // TODO: Keep children at map value level. - val mapEntry = entry.value - val styleableContainer = mapEntry.values.first().values - // Only one Item should be held in the container - if (styleableContainer.size != 1) - error("Too many resources in one entry: ${styleableContainer.size}") - // The children will be present under the Styleable Item. - return styleableContainer[0].value!! as Styleable + // To get the actual value we need to find the correct Item that's nested deep in the map + // TODO: Keep children at map value level. + val mapEntry = entry.value + val styleableContainer = mapEntry.values.first().values + // Only one Item should be held in the container + if (styleableContainer.size != 1) + error("Too many resources in one entry: ${styleableContainer.size}") + // The children will be present under the Styleable Item. + return styleableContainer[0].value!! as Styleable } fun findEntry(name: String, entryId: Short? = null): ResourceEntry? { @@ -759,29 +686,27 @@ class ResourceGroup(val type: AaptResourceType) { fun findOrCreateEntry(name: String, entryId: Short? = null): ResourceEntry { val entry = findEntry(name, entryId) - return when (entry) { + return when(entry) { null -> { val newEntry = ResourceEntry(name) newEntry.id = entryId entries.getOrPut(name) { TreeMap(nullsFirst()) }[entryId] = newEntry newEntry } - else -> entry } } } /** Represents a resource entry, which may have varying values for each defined configuration. */ -class ResourceEntry(val name: String) { - +class ResourceEntry(val name : String) { var id: Short? = null var visibility = Visibility(Source(""), "", ResourceVisibility.UNDEFINED) var allowNew: AllowNew? = null var overlayable: OverlayableItem? = null - val values = mutableListOf() + internal val values = mutableListOf() fun findValue(config: ConfigDescription, product: String = ""): ResourceConfigValue? { return values.find { it.config == config && it.product == product } @@ -795,7 +720,6 @@ class ResourceEntry(val name: String) { values.add(newConfigValue) newConfigValue } - else -> configValue } } @@ -810,6 +734,5 @@ class ResourceEntry(val name: String) { */ data class ResourceConfigValue( val config: ConfigDescription, - val product: String, - var value: Value? = null -) + val product : String, + var value: Value? = null) diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceUtils.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceUtils.kt old mode 100755 new mode 100644 index 1b713da2e..ca8c98d19 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceUtils.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceUtils.kt @@ -22,7 +22,7 @@ import com.android.aaptcompiler.android.parseHex import com.android.aaptcompiler.android.stringToFloat import com.android.aaptcompiler.android.stringToInt -fun tryParseBool(string: String): BinaryPrimitive? { +fun tryParseBool(string: String) : BinaryPrimitive? { val boolean = parseAsBool(string) boolean ?: return null @@ -30,14 +30,10 @@ fun tryParseBool(string: String): BinaryPrimitive? { return BinaryPrimitive(ResValue(ResValue.DataType.INT_BOOLEAN, data)) } -fun parseAsBool(string: String): Boolean? = +fun parseAsBool(string: String) : Boolean? = when (string.trim()) { - "true", - "True", - "TRUE" -> true - "false", - "False", - "FALSE" -> false + "true", "True", "TRUE" -> true + "false", "False", "FALSE" -> false else -> null } @@ -50,15 +46,15 @@ fun tryParseNullOrEmpty(value: String): Item? { } } -fun makeNull(): Reference { +fun makeNull() : Reference { return Reference() } -fun makeEmpty(): BinaryPrimitive { +fun makeEmpty() : BinaryPrimitive { return BinaryPrimitive(ResValue(ResValue.DataType.NULL, ResValue.NullFormat.EMPTY)) } -fun tryParseInt(value: String): BinaryPrimitive? { +fun tryParseInt(value: String) : BinaryPrimitive? { val trimmedValue = value.trim() val resValue = stringToInt(trimmedValue) return if (resValue != null) BinaryPrimitive(resValue) else null @@ -66,17 +62,15 @@ fun tryParseInt(value: String): BinaryPrimitive? { fun parseResourceId(value: String): Int? { val resValue = stringToInt(value) - if ( - resValue != null && - resValue.dataType == ResValue.DataType.INT_HEX && - resValue.data.isValidDynamicId() - ) { + if (resValue != null && + resValue.dataType == ResValue.DataType.INT_HEX && + resValue.data.isValidDynamicId()) { return resValue.data } return null } -fun tryParseFloat(value: String): BinaryPrimitive? { +fun tryParseFloat(value: String) : BinaryPrimitive? { val floatResource = stringToFloat(value) floatResource ?: return null @@ -142,12 +136,15 @@ fun tryParseColor(value: String): BinaryPrimitive? { } else -> return null } - return if (error) BinaryPrimitive(ResValue()) else BinaryPrimitive(ResValue(dataType, data)) + return if (error) + throw Exception("Unable to parse hex color '$value'.") + else + BinaryPrimitive(ResValue(dataType, data)) } data class ReferenceInfo(val reference: Reference, val createNew: Boolean = false) -fun tryParseReference(value: String): ReferenceInfo? { +fun tryParseReference(value: String) : ReferenceInfo? { val parsedReference = parseReference(value) if (parsedReference != null) { @@ -174,7 +171,7 @@ fun tryParseReference(value: String): ReferenceInfo? { * * [ReferenceInfo.createNew] will be false, as Attributes cannot be created. */ -fun parseAttributeReference(value: String): ReferenceInfo? { +fun parseAttributeReference(value: String) : ReferenceInfo? { val trimmedValue = value.trim() if (trimmedValue.isEmpty()) { return null @@ -200,10 +197,7 @@ fun parseAttributeReference(value: String): ReferenceInfo? { val reference = Reference() reference.name = ResourceName( - possibleResourceName.packageName, - AaptResourceType.ATTR, - possibleResourceName.entry - ) + possibleResourceName.packageName, AaptResourceType.ATTR, possibleResourceName.entry) reference.referenceType = Reference.Type.ATTRIBUTE return ReferenceInfo(reference, false) } @@ -214,7 +208,10 @@ fun parseAttributeReference(value: String): ReferenceInfo? { * *

The reference must be of one of the following forms: * - * + "@" + "@/" + "@:" + "@:/" + * + "@" + * + "@/" + * + "@:" + * + "@:/" * *

Optionally, the '@' symbol can be followed by one of '+' or '*'. '+' means that the resource * will be created and added to the resource table. This is only usable with id resources. The '*', @@ -225,10 +222,11 @@ fun parseAttributeReference(value: String): ReferenceInfo? { * @return A [ReferenceInfo] representing the reference, or {@code null} if the input was invalid. * * + [ReferenceInfo.createNew] will be set if and only if a '+' followed the '@' and the reference - * had a type of id. + [ReferenceInfo.reference] will be set as private if and only if a '*' - * followed the '@' in the input. + * had a type of id. + * + [ReferenceInfo.reference] will be set as private if and only if a '*' followed the '@' in the + * input. */ -fun parseReference(value: String): ReferenceInfo? { +fun parseReference(value: String) : ReferenceInfo? { val trimmedValue = value.trim() if (trimmedValue.isEmpty()) { return null @@ -271,16 +269,19 @@ data class ResourceNameInfo(val resourceName: ResourceName, val isPrivate: Boole * *

The resource name must be in one of the following forms: * - * + "" + "/" + ":" + ":/" + * + "" + * + "/" + * + ":" + * + ":/" * * The value may optionally be preceded by a '*' to represent that the resource name is private. * * @param value the value to be parsed. * @return If parsing was successful, this function returns the [ResourceNameInfo] holding both the - * valid [ResourceName] and whether the parsed name identifies a private resource. If the value - * failed to parsed {@code null} is returned instead. + * valid [ResourceName] and whether the parsed name identifies a private resource. If the value + * failed to parsed {@code null} is returned instead. */ -fun parseResourceName(value: String): ResourceNameInfo? { +fun parseResourceName(value: String) : ResourceNameInfo? { if (value.isEmpty()) { return null @@ -307,16 +308,12 @@ fun parseResourceName(value: String): ResourceNameInfo? { return ResourceNameInfo( ResourceName(possibleResourceName.packageName, resourceType, possibleResourceName.entry), - isPrivate - ) + isPrivate) } data class PossibleResourceName( - val packageName: String, - val typeName: String, - val entry: String, - val success: Boolean = true -) + val packageName: String, val typeName: String, val entry: String, val success: Boolean = true) + fun extractResourceName(value: String): PossibleResourceName { var packageName = "" @@ -347,8 +344,8 @@ fun extractResourceName(value: String): PossibleResourceName { } val entryName = value.substring(offsetCurrent) - val success = - !(hasPackageSeparator && packageName.isEmpty()) && !((hasTypeSeparator) && typeName.isEmpty()) + val success = !(hasPackageSeparator && packageName.isEmpty()) && + !((hasTypeSeparator) && typeName.isEmpty()) return PossibleResourceName(packageName, typeName, entryName, success) } @@ -356,8 +353,7 @@ fun extractResourceName(value: String): PossibleResourceName { fun tryParseItemForAttribute( value: String, resourceTypeMask: Int, - onCreateReference: ((name: ResourceName) -> Boolean)? = null -): Item? { + onCreateReference: ((name: ResourceName) -> Boolean)? = null): Item? { val nullOrEmpty = tryParseNullOrEmpty(value) if (nullOrEmpty != null) { @@ -369,8 +365,8 @@ fun tryParseItemForAttribute( reference.reference.typeFlags = resourceTypeMask if (reference.createNew) { val result = onCreateReference?.invoke(reference.reference.name) - if (result != null && !result) { - return null + if (result != null && !result) { + return null } } return reference.reference @@ -397,16 +393,13 @@ fun tryParseItemForAttribute( } } - val floatMask = - Resources.Attribute.FormatFlags.FLOAT_VALUE or - Resources.Attribute.FormatFlags.DIMENSION_VALUE or - Resources.Attribute.FormatFlags.FRACTION_VALUE + val floatMask = Resources.Attribute.FormatFlags.FLOAT_VALUE or + Resources.Attribute.FormatFlags.DIMENSION_VALUE or + Resources.Attribute.FormatFlags.FRACTION_VALUE if ((resourceTypeMask and floatMask) != 0) { val floatingPoint = tryParseFloat(value) - if ( - floatingPoint != null && - (androidTypeToAttributeTypeMask(floatingPoint.resValue.dataType) and resourceTypeMask) != 0 - ) { + if (floatingPoint != null && + (androidTypeToAttributeTypeMask(floatingPoint.resValue.dataType) and resourceTypeMask) != 0) { return floatingPoint } } @@ -578,16 +571,11 @@ fun parseStyleParentReference(str: String): ParsedParentInfo { val resourceName = ResourceName( - possibleResourceName.packageName, - AaptResourceType.STYLE, - possibleResourceName.entry - ) + possibleResourceName.packageName, AaptResourceType.STYLE, possibleResourceName.entry) - if ( - !hasLeadingIdentifiers && - resourceName.pck!!.isEmpty() && - possibleResourceName.typeName.isNotEmpty() - ) { + if (!hasLeadingIdentifiers && + resourceName.pck!!.isEmpty() && + possibleResourceName.typeName.isNotEmpty()) { val errorString = "Invalid parent reference '$str'" return ParsedParentInfo(null, errorString) } @@ -596,9 +584,11 @@ fun parseStyleParentReference(str: String): ParsedParentInfo { result.name = resourceName result.isPrivate = privateRef + return ParsedParentInfo(result, "") } + fun parseXmlAttributeName(str: String): Reference { val name = str.trim() @@ -612,10 +602,10 @@ fun parseXmlAttributeName(str: String): Reference { var packageName = "" var entryName = "" - for (i in startOffset until name.length) { + for (i in startOffset..(name.length - 1)) { if (name.codePointAt(i) == ':'.code) { packageName = name.substring(startOffset, i) - entryName = name.substring(i + 1) + entryName = name.substring(i+1) } } @@ -642,7 +632,7 @@ fun tryParseFlagSymbol(attribute: AttributeResource, value: String): BinaryPrimi // so we need to match against the 'entry' part of the identifier val flagResourceName = symbol.symbol.name if (trimmedPart == flagResourceName.entry) { - flagsData = flagsData or symbol.value + flagsData = flagsData or symbol.value flagSet = true break } diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceValues.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ResourceValues.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/SDKConstants.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/SDKConstants.kt old mode 100755 new mode 100644 index 431095096..ccab0fdbe --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/SDKConstants.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/SDKConstants.kt @@ -7,4 +7,5 @@ internal object SDKConstants { const val SDK_LOLLIPOP = 21.toShort() const val SDK_MARSHMALLOW = 23.toShort() const val SDK_O = 26.toShort() -} \ No newline at end of file + const val SDK_U = 34.toShort() +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Source.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/Source.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/StringPool.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/StringPool.kt old mode 100755 new mode 100644 index 5747ce92a..d37dc392f --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/StringPool.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/StringPool.kt @@ -175,9 +175,7 @@ class StringPool { reAssignIndices() } - private fun flatten(out: BigBuffer, utf8: Boolean, logger: ILogger?): Boolean { - var error = false - + private fun flatten(out: BigBuffer, utf8: Boolean, logger: ILogger?) { val header = ResStringPoolHeader( ResChunkHeader( ChunkType.STRING_POOL_TYPE.id.hostToDevice(), @@ -208,18 +206,14 @@ class StringPool { stringIndexBlock .writeInt((out.size - beginStringsIndex).hostToDevice(), currentStringIndex) currentStringIndex += 4 - - val stringError = !encodeString(entry.value, utf8, out, logger) - error = stringError || error + encodeString(entry.value, utf8, out) } // Strings come next for (entry in strings) { stringIndexBlock.writeInt((out.size - beginStringsIndex).hostToDevice(), currentStringIndex) currentStringIndex += 4 - - val stringError = !encodeString(entry.value, utf8, out, logger) - error = stringError || error + encodeString(entry.value, utf8, out) } out.align4() @@ -261,7 +255,6 @@ class StringPool { // now we can set the size of the output in the header header.header.size = (out.size - headerStart).hostToDevice() encodeHeader(header, headerBlock) - return !error } private fun encodeHeader(resHeader: ResStringPoolHeader, out: BigBuffer.BlockRef) { @@ -283,21 +276,18 @@ class StringPool { return 12 } - private fun encodeString( - str: String, utf8: Boolean, out: BigBuffer, logger: ILogger?): Boolean { - - if (utf8) { + private fun encodeString(str: String, utf8: Boolean, out: BigBuffer) { + if (utf8) { val utf16Length = str.length val utf8Str = str.toByteArray() val utf8Length = utf8Str.size // Make sure the lengths to be encoded do not exceed the maximum length that can be encoded // using chars - if (utf8Length > UTF8_ENCODE_LENGTH_MAX || utf16Length > UTF16_ENCODE_LENGTH_MAX) { - val errorMsg = "String too large to encode using UTF-8. Writing the string instead as '%s'." - logger?.error(null, errorMsg, STRING_TOO_LARGE) - encodeString(STRING_TOO_LARGE, utf8, out, logger) - return false + if (utf8Length > UTF8_ENCODE_LENGTH_MAX ) { + error("String of size $utf8Length bytes is too large to encode using UTF-8 " + + "($UTF8_ENCODE_LENGTH_MAX bytes). " + + "Affected string begins with: '${str.take(STRING_PREFIX_LENGTH_FOR_ERRORS)}'...") } val blockSize = getLengthUtf8(utf8Length) + getLengthUtf8(utf16Length) + utf8Length + 1 @@ -319,20 +309,15 @@ class StringPool { // Now write null terminator stringBlock.writeByte(0x00, locationToWrite) - - return true } else { val utf16Length = str.length // Make sure the length to be encoded does not exceed the maximum possible length that can be // encoded if (utf16Length > UTF16_ENCODE_LENGTH_MAX) { - val errorMsg = - "String too large to encode using UTF-16. Writing the string instead as '%s'." - logger?.error(null, errorMsg, STRING_TOO_LARGE) - - encodeString(STRING_TOO_LARGE, utf8, out, logger) - return false + error("String of size ${str.toByteArray().size} is too large to encode using " + + "UTF-16 ($UTF16_ENCODE_LENGTH_MAX bytes). " + + "Affected string begins with: '${str.take(STRING_PREFIX_LENGTH_FOR_ERRORS)}'...") } val blockSize = getLengthUtf16(utf16Length) + utf16Length*2 + 2 @@ -351,7 +336,6 @@ class StringPool { // now write null terminator stringBlock.writeShort(0x0000.toShort(), locationToWrite) - return true } } @@ -422,7 +406,9 @@ class StringPool { const val TWO_CHAR_UTF16_LENGTH_SIGNIFIER = 0x8000 const val ONE_CHAR_UTF16_ENCODE_LENGTH_MAX = 0x7fff - private const val STRING_TOO_LARGE = "STRING_TOO_LARGE" + // The maximum number of chars that will be presented to the user if the size of the string + // exceeds the maximum permitted utf-8 or utf-16 byte size. + const val STRING_PREFIX_LENGTH_FOR_ERRORS = 42 val ENTRY_ON_VALUE = compareBy {it.value} val STYLE_ON_VALUE = compareBy {it.value} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/TableExtractor.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/TableExtractor.kt old mode 100755 new mode 100644 index 427092e3c..089938485 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/TableExtractor.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/TableExtractor.kt @@ -3,6 +3,7 @@ package com.android.aaptcompiler import com.android.aapt.Resources import com.android.aaptcompiler.android.stringToInt import com.itsaky.androidide.layoutlib.resources.ResourceVisibility +import java.io.InputStream import jaxp.xml.XMLConstants import jaxp.xml.namespace.QName import jaxp.xml.stream.XMLEventReader @@ -12,7 +13,6 @@ import jaxp.xml.stream.events.Attribute import jaxp.xml.stream.events.Comment import jaxp.xml.stream.events.StartElement import jaxp.xml.stream.events.XMLEvent -import java.io.InputStream /** * Namespace uri for the xliff:g tag in XML. @@ -25,17 +25,14 @@ private const val XLIFF_NS_URI = "urn:oasis:names:tc:xliff:document:1.2" /** * Resource parsed from the XML, with all relevant metadata. * - * @property config The config description of the resource. This should be the same as the config of - * the source. + * @property config The config description of the resource. This should be the same as the config + * of the source. * @property source The start location in the xml from which this resource was extracted. * @property comment The comment describing the resource that appeared before it in the xml. This - * will be an empty string if no comment was supplied. + * will be an empty string if no comment was supplied. */ private class ParsedResource( - var config: ConfigDescription, - val source: Source, - val comment: String -) { + var config: ConfigDescription, val source: Source, val comment: String) { constructor() : this(ConfigDescription(), Source(""), "") @@ -55,10 +52,10 @@ private class ParsedResource( */ var overlayableItem: OverlayableItem? = null /** - * The value of the resource, this might be null if this is a use of a resource. (i.e. an - * within a + * The value of the resource, this might be null if this is a use of a resource. (i.e. an + * within a */ - var value: Value? = null + var value : Value? = null /** * The child resources of the given resource. These resources will be added to the table when * {@code this} is added. The connection of the resources to this resource should be reflected in @@ -73,17 +70,16 @@ private class ParsedResource( * @property translatable Whether the default setting for this parser is to allow translation. * * @property errorOnPositionalArgs Whether positional arguments in formatted strings are treated as - * errors or warnings. + * errors or warnings. * * @property visibility the default visibility of resources extracted. If non-null, all new - * resources are set with this visibility, and will error if trying to parse the , - * , or tags. + * resources are set with this visibility, and will error if trying to parse the , + * , or tags. */ data class TableExtractorOptions( val translatable: Boolean = true, val errorOnPositionalArgs: Boolean = true, - val visibility: ResourceVisibility? = null -) + val visibility: ResourceVisibility? = null) /** Returns true if the element is or and can be safely ignored */ fun shouldIgnoreElement(elementName: QName): Boolean { @@ -129,9 +125,9 @@ fun parseFormatAttribute(value: String): Int { *

Primarily, as each resource needs to be parsed, the name, package, and type of resource is * extracted. * - *

Then, if an item, a call to [parseItem] is invoked with a type mask of the valid types the - * xml element can be parsed as. This will result in a more specialized call to [parseXml] which - * will proceed to flatten the xml subtree of the item (which may include span tags and + *

Then, if an item, a call to [parseItem] is invoked with a type mask of the valid types + * the xml element can be parsed as. This will result in a more specialized call to [parseXml] + * which will proceed to flatten the xml subtree of the item (which may include span tags and * untranslatable section tags) and then attempt to process the flattened xml string (in accordance * with the valid types specified by the type mask). Finally, the parsed resource value will be * added to [ResourceTable], if successful. @@ -152,11 +148,10 @@ class TableExtractor( val source: Source, val config: ConfigDescription, val options: TableExtractorOptions, - val logger: BlameLogger -) { + val logger: BlameLogger) { fun extract(inputFile: InputStream) { - var eventReader: XMLEventReader? = null + var eventReader : XMLEventReader? = null try { eventReader = xmlInputFactory.createXMLEventReader(inputFile) @@ -167,7 +162,7 @@ class TableExtractor( } var rootStart: XMLEvent? = null - while (eventReader.hasNext()) { + while(eventReader.hasNext()) { rootStart = eventReader.nextEvent() // ignore comments and text before the root tag if (rootStart.isStartElement) { @@ -179,13 +174,15 @@ class TableExtractor( val rootName = rootStart.asStartElement().name if (rootName.namespaceURI != null && rootName.localPart != "resources") { val userReadableSource = logger.getOriginalSource(blameSource(source)).toString().trim() - error("Root xml element of resource table not labeled 'resources' ($userReadableSource).") + error( + "Root xml element of resource table not labeled 'resources' ($userReadableSource)." + ) } extractResourceValues(eventReader) } catch (xmlException: XMLStreamException) { if (xmlException.message?.contains("Premature end of file.", true) != true) { - // Having no root is not an error, but any other xml format exception is. - throw xmlException + // Having no root is not an error, but any other xml format exception is. + throw xmlException } } finally { eventReader?.close() @@ -193,7 +190,7 @@ class TableExtractor( } private fun logError(source: BlameLogger.Source, message: String) { - logger.error(message, source) + logger.error(message, source) } /** @@ -234,7 +231,7 @@ class TableExtractor( if (!event.isStartElement) { errors += - "Unexpected element type: ${event.eventType}, ${blameSource(source, event.location)}." + "Unexpected element type: ${event.eventType}, ${blameSource(source, event.location)}." } val element = event.asStartElement() @@ -270,26 +267,25 @@ class TableExtractor( } } if (errors.any()) { - error(errors.joinToString(separator = ",")) + error(errors.joinToString(separator = ",")) } } /** * Extracts the [Value] of a resource from the given element. This can be either an [Item] or a - * nested value type. + * nested value type. * * @param element The start of the element to be translated as a [Value]. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource The [ParsedResource] to hold the extracted value upon success. * @return Whether or not the parsing was a success. */ private fun extractResource( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element : StartElement, + eventReader : XMLEventReader, + parsedResource : ParsedResource): Boolean { var resourceTypeName = element.name.localPart @@ -311,8 +307,7 @@ class TableExtractor( if (resourceFormat == 0) { logError( blameSource(parsedResource.source), - "Resource has an invalid format of ${formatAttribute.value}." - ) + "Resource has an invalid format of ${formatAttribute.value}.") walkToEndOfElement(element, eventReader) return false } @@ -326,6 +321,7 @@ class TableExtractor( return false } resourceTypeName = typeAttribute.value + } else if (resourceTypeName == "bag") { canBeItem = false @@ -345,9 +341,7 @@ class TableExtractor( if (resourceTypeName == "id") { if (nameAttribute == null) { logError( - blameSource(parsedResource.source), - "<${element.name}> is missing the 'name' attribute." - ) + blameSource(parsedResource.source), "<${element.name}> is missing the 'name' attribute.") walkToEndOfElement(element, eventReader) return false } @@ -371,8 +365,7 @@ class TableExtractor( // if an inner element exists, the inner element must be a reference to another id logError( blameSource(parsedResource.source), - "<${element.name}> inner element must either be a resource reference or empty." - ) + "<${element.name}> inner element must either be a resource reference or empty.") return false } } @@ -380,43 +373,39 @@ class TableExtractor( } if (canBeItem) { - val (type, typeMask) = - when (resourceTypeName) { - "bool" -> Pair(AaptResourceType.BOOL, Resources.Attribute.FormatFlags.BOOLEAN_VALUE) - "color" -> Pair(AaptResourceType.COLOR, Resources.Attribute.FormatFlags.COLOR_VALUE) - "configVarying" -> - Pair(AaptResourceType.CONFIG_VARYING, Resources.Attribute.FormatFlags.ANY_VALUE) - "dimen" -> - Pair( - AaptResourceType.DIMEN, - Resources.Attribute.FormatFlags.FLOAT_VALUE or - Resources.Attribute.FormatFlags.FRACTION_VALUE or - Resources.Attribute.FormatFlags.DIMENSION_VALUE - ) - "drawable" -> Pair(AaptResourceType.DRAWABLE, Resources.Attribute.FormatFlags.COLOR_VALUE) - "fraction" -> - Pair( - AaptResourceType.FRACTION, - Resources.Attribute.FormatFlags.FLOAT_VALUE or - Resources.Attribute.FormatFlags.FRACTION_VALUE or - Resources.Attribute.FormatFlags.DIMENSION_VALUE - ) - "integer" -> Pair(AaptResourceType.INTEGER, Resources.Attribute.FormatFlags.INTEGER_VALUE) - "string" -> Pair(AaptResourceType.STRING, Resources.Attribute.FormatFlags.STRING_VALUE) - else -> Pair(null, Resources.Attribute.FormatFlags.ANY_VALUE) - } + val (type, typeMask) = when (resourceTypeName) { + "bool" -> Pair(AaptResourceType.BOOL, Resources.Attribute.FormatFlags.BOOLEAN_VALUE) + "color" -> Pair(AaptResourceType.COLOR, Resources.Attribute.FormatFlags.COLOR_VALUE) + "configVarying" -> + Pair(AaptResourceType.CONFIG_VARYING, Resources.Attribute.FormatFlags.ANY_VALUE) + "dimen" -> + Pair( + AaptResourceType.DIMEN, + Resources.Attribute.FormatFlags.FLOAT_VALUE or + Resources.Attribute.FormatFlags.FRACTION_VALUE or + Resources.Attribute.FormatFlags.DIMENSION_VALUE) + "drawable" -> Pair(AaptResourceType.DRAWABLE, Resources.Attribute.FormatFlags.COLOR_VALUE) + "fraction" -> + Pair( + AaptResourceType.FRACTION, + Resources.Attribute.FormatFlags.FLOAT_VALUE or + Resources.Attribute.FormatFlags.FRACTION_VALUE or + Resources.Attribute.FormatFlags.DIMENSION_VALUE) + "integer" -> Pair(AaptResourceType.INTEGER, Resources.Attribute.FormatFlags.INTEGER_VALUE) + "string" -> Pair(AaptResourceType.STRING, Resources.Attribute.FormatFlags.STRING_VALUE) + else -> Pair(null, Resources.Attribute.FormatFlags.ANY_VALUE) + } if (type != null) { // this is an item record its type and format and start parsing. if (nameAttribute == null) { logError( blameSource(parsedResource.source), - "<${element.name}> is missing the 'name' attribute." - ) + "<${element.name}> is missing the 'name' attribute.") walkToEndOfElement(element, eventReader) return false } - parsedResource.name = ResourceName("", type, nameAttribute.value) + parsedResource.name = ResourceName( "", type, nameAttribute.value) // Only use the implied format of the type when there is no explicit format. if (resourceFormat == 0) { @@ -427,25 +416,24 @@ class TableExtractor( } if (canBeBag) { - val parseBagMethod = - when (resourceTypeName) { - "add-resource" -> ::parseAddResource - "array" -> ::parseArray - "attr" -> ::parseAttr - "configVarying" -> ::parseConfigVarying - "declare-styleable" -> ::parseDeclareStyleable - "integer-array" -> ::parseIntegerArray - "java-symbol" -> ::parseSymbol - "macro" -> ::parseMacro - "overlayable" -> ::parseOverlayable - "plurals" -> ::parsePlural - "public" -> ::parsePublic - "public-group" -> ::parsePublicGroup - "string-array" -> ::parseStringArray - "style" -> ::parseStyle - "symbol" -> ::parseSymbol - else -> null - } + val parseBagMethod = when(resourceTypeName) { + "add-resource" -> ::parseAddResource + "array" -> ::parseArray + "attr" -> ::parseAttr + "configVarying" -> ::parseConfigVarying + "declare-styleable" -> ::parseDeclareStyleable + "integer-array" -> ::parseIntegerArray + "java-symbol" -> ::parseSymbol + "macro" -> ::parseMacro + "overlayable" -> ::parseOverlayable + "plurals" -> ::parsePlural + "public" -> ::parsePublic + "public-group" -> ::parsePublicGroup + "string-array" -> ::parseStringArray + "style" -> ::parseStyle + "symbol" -> ::parseSymbol + else -> null + } if (parseBagMethod != null) { // ensure we have a name (unless this is a or ). @@ -454,17 +442,17 @@ class TableExtractor( walkToEndOfElement(element, eventReader) logError( blameSource(parsedResource.source), - "<${element.name}> is missing the 'name' attribute." - ) + "<${element.name}> is missing the 'name' attribute.") return false } - parsedResource.name = parsedResource.name.copy(entry = nameAttribute.value) + parsedResource.name = parsedResource.name.copy(entry=nameAttribute.value) } // Call the associated parse method. The type will be filled in by the parse function return parseBagMethod(element, eventReader, parsedResource) } + } if (canBeItem) { @@ -473,8 +461,7 @@ class TableExtractor( if (nameAttribute == null) { logError( blameSource(parsedResource.source), - "<${element.name}> is missing the 'name' attribute." - ) + "<${element.name}> is missing the 'name' attribute.") walkToEndOfElement(element, eventReader) return false } @@ -486,8 +473,7 @@ class TableExtractor( if (parsedResource.value == null) { logError( blameSource(parsedResource.source), - "Invalid value for type '${parsedType.tagName}'. Expected a reference." - ) + "Invalid value for type '${parsedType.tagName}'. Expected a reference.") return false } @@ -504,24 +490,23 @@ class TableExtractor( * Parses the XML subtree and returns an Item. * * @param element The start of the element to be translated as an item type. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param resourceFormat A type mask that specifies which formats are valid for the xml to be - * interpreted as. + * interpreted as. * @param allowRawString If true, a [RawString] representing the xml is returned if it could not - * be parsed as any valid resource [Item]. If false, {@code null} will be returned instead on - * failure. + * be parsed as any valid resource [Item]. If false, {@code null} will be returned instead on + * failure. * * @return The [Item] that represents the xml subtree. This will be {@code null} if the xml failed - * to be interpreted as a valid resource. + * to be interpreted as a valid resource. */ private fun parseXml( element: StartElement, eventReader: XMLEventReader, resourceFormat: Int, - allowRawString: Boolean - ): Item? { + allowRawString : Boolean) : Item? { val flattenedXml = flattenXmlSubTree(element, eventReader) if (!flattenedXml.success) { @@ -533,10 +518,8 @@ class TableExtractor( return StyledString( table.stringPool.makeRef( flattenedXml.styleString, - StringPool.Context(StringPool.Context.Priority.NORMAL.priority, config) - ), - flattenedXml.untranslatableSections - ) + StringPool.Context(StringPool.Context.Priority.NORMAL.priority, config)), + flattenedXml.untranslatableSections) } // Process the raw value @@ -559,9 +542,9 @@ class TableExtractor( if (resourceFormat and Resources.Attribute.FormatFlags.STRING_VALUE != 0) { // use trimmed escaped string. return BasicString( - table.stringPool.makeRef(flattenedXml.styleString.str, StringPool.Context(config = config)), - flattenedXml.untranslatableSections - ) + table.stringPool.makeRef( + flattenedXml.styleString.str, StringPool.Context(config = config)), + flattenedXml.untranslatableSections) } // if the text is empty, and the value is not allowed to be a string, encode it as a @null. @@ -570,21 +553,20 @@ class TableExtractor( } if (allowRawString) { - val raw = - flattenedXml.rawString.let { raw -> - // Remove space, newline character wrapping (typically due to IDE formatting) - // and user added quotations for styleable children due to lack of type. - val isNotWrappingChar: (Char) -> Boolean = { it !in setOf(' ', '\n') } - val firstNonWrappingIndex = raw.indexOfFirst(isNotWrappingChar) - val lastNonWrappingIndex = raw.indexOfLast(isNotWrappingChar) - raw - .substring( - if (firstNonWrappingIndex == -1) 0 else firstNonWrappingIndex, - if (lastNonWrappingIndex == -1) 0 else lastNonWrappingIndex + 1 + val raw = flattenedXml.rawString.let { raw -> + // Remove space, newline character wrapping (typically due to IDE formatting) + // and user added quotations for styleable children due to lack of type. + val isNotWrappingChar: (Char) -> Boolean = { it !in setOf(' ', '\n') } + val firstNonWrappingIndex = raw.indexOfFirst(isNotWrappingChar) + val lastNonWrappingIndex = raw.indexOfLast(isNotWrappingChar) + raw.substring( + if (firstNonWrappingIndex == - 1) 0 else firstNonWrappingIndex, + if (lastNonWrappingIndex == - 1) 0 else lastNonWrappingIndex + 1 ) - .removeSurrounding("\"") + .removeSurrounding("\"") } - return RawString(table.stringPool.makeRef(raw, StringPool.Context(config = config))) + return RawString( + table.stringPool.makeRef(raw, StringPool.Context(config=config))) } return null @@ -594,20 +576,19 @@ class TableExtractor( * Attempts to parse the xml subtree as an item resource. * * @param element The start of the element to be translated as an item type. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource The resource to put the parsed [Item] into, if successful. * @param resourceFormat A type mask that specifies which formats are valid for the xml to be - * interpreted as. + * interpreted as. * @return Whether or not the xml could be parsed. */ private fun parseItem( element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource, - resourceFormat: Int - ): Boolean { + resourceFormat: Int) : Boolean { if (resourceFormat == Resources.Attribute.FormatFlags.STRING_VALUE) { return parseString(element, eventReader, parsedResource) @@ -617,8 +598,7 @@ class TableExtractor( if (parsedResource.value == null) { logError( blameSource(parsedResource.source), - "Invalid <${parsedResource.name.type.tagName}> for given resource value." - ) + "Invalid <${parsedResource.name.type.tagName}> for given resource value.") return false } return true @@ -629,18 +609,15 @@ class TableExtractor( * translatable. * * @param element The start of the element to be translated at a string. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource the resource to put the parsed String into. The [ParsedResource.value] - * will be set to either a [BasicString] or [StyleString] resource, if successful. + * will be set to either a [BasicString] or [StyleString] resource, if successful. * @return Whether or not the element could be parsed as a String resource. */ private fun parseString( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { var formatted = true val formattedAttribute = element.getAttributeByName(QName("formatted")) if (formattedAttribute != null) { @@ -649,8 +626,7 @@ class TableExtractor( logError( blameSource(parsedResource.source), "Invalid value for the 'formatted' attribute. " + - "Was '${formattedAttribute.value}', must be a boolean." - ) + "Was '${formattedAttribute.value}', must be a boolean.") walkToEndOfElement(element, eventReader) return false } @@ -665,20 +641,17 @@ class TableExtractor( logError( blameSource(parsedResource.source), "Invalid value for 'translatable' attribute. " + - "Was '${translatableAttribute.value}', must be a boolean." - ) + "Was '${translatableAttribute.value}', must be a boolean.") walkToEndOfElement(element, eventReader) return false } translatable = maybeTranslatable } - val value = parseXml(element, eventReader, Resources.Attribute.FormatFlags.STRING_VALUE, false) + val value = + parseXml(element, eventReader, Resources.Attribute.FormatFlags.STRING_VALUE, false) if (value == null) { - logError( - blameSource(parsedResource.source), - "${parsedResource.name} does not contain a valid string resource." - ) + logError( blameSource(parsedResource.source), "${parsedResource.name} does not contain a valid string resource.") return false } @@ -687,9 +660,8 @@ class TableExtractor( if (formatted && translatable) { if (!verifyJavaStringFormat(value.toString())) { - val errorMsg = - "Multiple substitutions specified in non-positional format of string " + - "resource ${parsedResource.name}. Did you mean to add the formatted=\"false\" attribute?" + val errorMsg = "Multiple substitutions specified in non-positional format of string " + + "resource ${parsedResource.name}. Did you mean to add the formatted=\"false\" attribute?" if (options.errorOnPositionalArgs) { logError(blameSource(parsedResource.source), errorMsg) return false @@ -708,18 +680,15 @@ class TableExtractor( * Parses the xml element specified by {@code element} as a Enum or Flag value of an Attribute. * * @param element The start element that represents the symbol to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param tag The name of the flag or enum item. * @return The child resource if the parsing was successful, or {@code null} if the parsing - * failed. + * failed. */ private fun parseEnumOrFlagItem( - element: StartElement, - eventReader: XMLEventReader, - tag: String - ): AttributeResource.Symbol? { + element: StartElement, eventReader: XMLEventReader, tag: String): AttributeResource.Symbol? { val elementSource = source.withLine(element.location.lineNumber) walkToEndOfElement(element, eventReader) @@ -739,9 +708,7 @@ class TableExtractor( val resValue = stringToInt(valueAttribute.value) if (resValue == null) { logError( - blameSource(elementSource), - "Invalid value '$resValue' for <$tag>. Must be an integer." - ) + blameSource(elementSource), "Invalid value '$resValue' for <$tag>. Must be an integer.") return null } @@ -754,18 +721,15 @@ class TableExtractor( * Parses the xml element specified by {@code element} as an [Item] under the style. * * @param element The start element that represents the symbol to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param style The parent style of which this resource is a part. * @return The child resource if the parsing was successful, or {@code null} if the parsing - * failed. + * failed. */ private fun parseStyleItem( - element: StartElement, - eventReader: XMLEventReader, - style: Style - ): Boolean { + element: StartElement, eventReader: XMLEventReader, style: Style): Boolean { val itemSource = source.withLine(element.location.lineNumber) val nameAttribute = element.getAttributeByName(QName("name")) @@ -784,8 +748,7 @@ class TableExtractor( if (xmlItem == null) { logError( blameSource(source, element.location), - "Could not parse style item with name '${nameAttribute.value}'." - ) + "Could not parse style item with name '${nameAttribute.value}'.") return false } @@ -798,24 +761,20 @@ class TableExtractor( * formatting). * * @param eventReader the xml to flattened. The reader should have just read the start of the - * element that is needed to be flattened. After this method is invoked, the event reader will be - * after the end of the element that the flattened xml is to represent. - * @return

If Parsing fails, the [FlattenedXml.success] fill be set to false and the rest of - * the flattened xml will be left in a unspecified state.

Otherwise: - * [FlattenedXml.styleString] contains the escaped and whitespace trimmed text with included - * ``` - * spans. [FlattenedXml.rawString] - * ``` - * contains the unescaped text. [FlattenedXml.untranslatableSections] contains the sections of the - * string that should not be - * ``` + * element that is needed to be flattened. After this method is invoked, the event reader will + * be after the end of the element that the flattened xml is to represent. + * @return + *

If Parsing fails, the [FlattenedXml.success] fill be set to false and the rest of the + * flattened xml will be left in a unspecified state. + *

Otherwise: + * [FlattenedXml.styleString] contains the escaped and whitespace trimmed text with included + * spans. + * [FlattenedXml.rawString] contains the unescaped text. + * [FlattenedXml.untranslatableSections] contains the sections of the string that should not be * translated. - * ``` */ private fun flattenXmlSubTree( - startElement: StartElement, - eventReader: XMLEventReader - ): FlattenedXml { + startElement: StartElement, eventReader: XMLEventReader) : FlattenedXml { var depth = 1 @@ -826,6 +785,7 @@ class TableExtractor( when { event.isCharacters -> builder.append(event.asCharacters().data) + event.isStartElement -> { val element = event.asStartElement() @@ -853,9 +813,9 @@ class TableExtractor( // besides XLIFF, any other namespaced tags are unsupported and ignored. logger?.warning( "Ignoring element '$elementName' with unknown namespace '${elementName.namespaceURI}'.", - blameSource(source.withLine(element.location.lineNumber)) - ) + blameSource(source.withLine(element.location.lineNumber))) } + } ++depth } @@ -886,8 +846,7 @@ class TableExtractor( val resourceName = startElement.getAttributeByName(QName("name")).value logError( blameSource(source, startElement.location), - "Failed to flatten XML for resource '$resourceName' with error: ${builder.error}" - ) + "Failed to flatten XML for resource '$resourceName' with error: ${builder.error}") } return flattenedXml } @@ -896,32 +855,27 @@ class TableExtractor( * Parses the xml with a "symbol" tag * * @param element The start element that represents the symbol to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the parsed symbol will be stored, if the parsing was successful. * @return Whether of not the parsing was successful. */ private fun parseSymbol( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { var error = false if (options.visibility != null) { logError( blameSource(source, element.location), - " and tags are not supported with resource visibility." - ) + " and tags are not supported with resource visibility.") error = true } // Symbols should have the default config if (parsedResource.config != ConfigDescription()) { logger?.warning( - "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", - blameSource(source, element.location) - ) + "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", + blameSource(source, element.location)) } if (!parseSymbolImpl(element, eventReader, parsedResource)) { @@ -933,10 +887,7 @@ class TableExtractor( } private fun parseMacro( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { parsedResource.name = parsedResource.name.copy(type = AaptResourceType.MACRO) // Macros can only be defined in the default config @@ -944,19 +895,17 @@ class TableExtractor( if (parsedResource.config != defaultConfig) { logError( blameSource(source, element.location), - " tags cannot be declared in configurations other than the default configuration" - ) + " tags cannot be declared in configurations other than the default configuration") return false } val flattenedXml = flattenXmlSubTree(element, eventReader) if (!flattenedXml.success) { - return false + return false } // TODO(198264572): extract namespaces - parsedResource.value = - Macro(flattenedXml.rawString, flattenedXml.styleString, flattenedXml.untranslatableSections) + parsedResource.value = Macro(flattenedXml.rawString, flattenedXml.styleString, flattenedXml.untranslatableSections) return true } @@ -965,17 +914,14 @@ class TableExtractor( * Parses the xml with an "add-resource" tag. * * @param element The start element that represents the symbol to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the parsed symbol will be stored, if the parsing was successful. * @return Whether of not the parsing was successful. */ private fun parseAddResource( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { if (parseSymbolImpl(element, eventReader, parsedResource)) { parsedResource.visibility = ResourceVisibility.UNDEFINED parsedResource.allowNew = true @@ -985,28 +931,24 @@ class TableExtractor( } /** - * Parses the xml as a Symbol represented by the specified start element. Then, stores the value - * in the parsed resource. + * Parses the xml as a Symbol represented by the specified start element. Then, stores the + * value in the parsed resource. * * @param element The start element that represents the symbol to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the parsed symbol will be stored, if the parsing was successful. * @return Whether of not the parsing was successful. */ private fun parseSymbolImpl( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { val typeAttribute = element.getAttributeByName(QName("type")) if (typeAttribute == null) { walkToEndOfElement(element, eventReader) logError( blameSource(source, element.location), - "<${element.name}> must have a 'type' attribute." - ) + "<${element.name}> must have a 'type' attribute.") return false } @@ -1015,8 +957,7 @@ class TableExtractor( walkToEndOfElement(element, eventReader) logError( blameSource(source, element.location), - "Invalid resource type '${typeAttribute.value}' in <${element.name}> resource." - ) + "Invalid resource type '${typeAttribute.value}' in <${element.name}> resource.") return false } @@ -1029,26 +970,24 @@ class TableExtractor( * Parses the xml represented by the "attr" tag. * * @param element The start element that represents the [AttributeResource] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the [AttributeResource] will be stored, if successful. * @return Whether of not the parsing was successful. */ private fun parseAttr( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean = parseAttrImpl(element, eventReader, parsedResource, false) + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean = + parseAttrImpl(element, eventReader, parsedResource, false) /** * parses the xml as a [AttributeResource] represented by the specified start element. Then stores * the value in the parsed resource. * * @param element The start element that represents the symbol to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the parsed symbol will be stored, if the parsing was successful. * @param isWeak whether or not the resource should be parsed as a weak attr (declaration). * @return Whether of not the parsing was successful. @@ -1057,8 +996,7 @@ class TableExtractor( element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource, - isWeak: Boolean - ): Boolean { + isWeak: Boolean): Boolean { parsedResource.name = parsedResource.name.copy(type = AaptResourceType.ATTR) // Attributes only end up in default configuration @@ -1066,8 +1004,7 @@ class TableExtractor( if (parsedResource.config != defaultConfig) { logger?.warning( "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", - blameSource(source, element.location) - ) + blameSource(source, element.location)) parsedResource.config = defaultConfig } @@ -1079,8 +1016,7 @@ class TableExtractor( if (typeMask == 0) { logError( blameSource(source, element.location), - "Invalid attribute format '${formatAttribute.value}'" - ) + "Invalid attribute format '${formatAttribute.value}'") walkToEndOfElement(element, eventReader) return false } @@ -1104,8 +1040,7 @@ class TableExtractor( if (min == null) { logError( blameSource(source, element.location), - "Invalid 'min' value '$minString'. Integer value required." - ) + "Invalid 'min' value '$minString'. Integer value required.") walkToEndOfElement(element, eventReader) return false } @@ -1123,21 +1058,17 @@ class TableExtractor( if (max == null) { logError( blameSource(source, element.location), - "Invalid 'max' value '$maxString'. Integer value required." - ) + "Invalid 'max' value '$maxString'. Integer value required.") walkToEndOfElement(element, eventReader) return false } } - if ( - (min != null || max != null) && - (typeMask and Resources.Attribute.FormatFlags.INTEGER_VALUE) == 0 - ) { + if ((min != null || max != null) && + (typeMask and Resources.Attribute.FormatFlags.INTEGER_VALUE) == 0) { logError( blameSource(source, element.location), - "'min' and 'max' attributes can only be used with format='integer' on <${element.name}> resource" - ) + "'min' and 'max' attributes can only be used with format='integer' on <${element.name}> resource") walkToEndOfElement(element, eventReader) return false } @@ -1167,18 +1098,15 @@ class TableExtractor( val childSource = source.withLine(event.location.lineNumber) val childName = childElement.name - if ( - childName.namespaceURI.isEmpty() && - (childName.localPart == "flag" || childName.localPart == "enum") - ) { + if (childName.namespaceURI.isEmpty() && + (childName.localPart == "flag" || childName.localPart == "enum")) { var itemError = false when (childName.localPart) { "enum" -> { if ((typeMask and Resources.Attribute.FormatFlags.FLAGS_VALUE) != 0) { logError( blameSource(source, childElement.location), - "Cannot define both and under the same <${element.name}> resource." - ) + "Cannot define both and under the same <${element.name}> resource.") error = true itemError = true } @@ -1188,8 +1116,7 @@ class TableExtractor( if ((typeMask and Resources.Attribute.FormatFlags.ENUM_VALUE) != 0) { logError( blameSource(source, childElement.location), - "Cannot define both and under the same <${element.name}> resource." - ) + "Cannot define both and under the same <${element.name}> resource.") error = true itemError = true } @@ -1222,7 +1149,7 @@ class TableExtractor( ?: blameSource(symbolMap[symbolName]!!.symbol.source) val errorMsg = "Duplicate symbol '$symbolName' defined here: $newSource" + - " and here: $previousSource" + " and here: $previousSource" logError(blameSource(symbol.symbol.source), errorMsg) error = true } @@ -1230,12 +1157,11 @@ class TableExtractor( } else { error = true } - } else { + } else{ if (!shouldIgnoreElement(childName)) { logError( blameSource(childSource), - "Unrecognized tag <$childName> of child element of <${element.name}>." - ) + "Unrecognized tag <$childName> of child element of <${element.name}>.") error = true } walkToEndOfElement(childElement, eventReader) @@ -1247,8 +1173,8 @@ class TableExtractor( return false } - val resource = - AttributeResource(if (typeMask == 0) Resources.Attribute.FormatFlags.ANY_VALUE else typeMask) + val resource = AttributeResource( + if (typeMask == 0) Resources.Attribute.FormatFlags.ANY_VALUE else typeMask) resource.weak = isWeak resource.symbols.addAll(symbolMap.values) resource.minInt = min ?: Int.MIN_VALUE @@ -1262,16 +1188,13 @@ class TableExtractor( * parsed from the format attribute of {@code element} * * @param element The start element of the [ArrayResource] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the read resource will be placed. */ private fun parseArray( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { var resourceFormat = Resources.Attribute.FormatFlags.ANY_VALUE val formatAttribute = element.getAttributeByName(QName("format")) if (formatAttribute != null) { @@ -1279,8 +1202,7 @@ class TableExtractor( if (resourceFormat == 0) { logError( blameSource(source, element.location), - "Invalid format value: '${formatAttribute.value}'." - ) + "Invalid format value: '${formatAttribute.value}'.") walkToEndOfElement(element, eventReader) return false } @@ -1292,62 +1214,47 @@ class TableExtractor( * Parse the xml that is contained by the "integer-array" tag. * * @param element The start element of the [ArrayResource] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the read resource will be placed. */ private fun parseIntegerArray( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ) = + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource) = parseArrayImpl( - element, - eventReader, - parsedResource, - Resources.Attribute.FormatFlags.INTEGER_VALUE - ) + element, eventReader, parsedResource, Resources.Attribute.FormatFlags.INTEGER_VALUE) /** * Parse the xml that is contained by the "string-array" tag. * * @param element The start element of the [ArrayResource] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the read resource will be placed. */ private fun parseStringArray( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ) = + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource) = parseArrayImpl( - element, - eventReader, - parsedResource, - Resources.Attribute.FormatFlags.STRING_VALUE - ) + element, eventReader, parsedResource, Resources.Attribute.FormatFlags.STRING_VALUE) /** * Parse the xml as an [ArrayResource]. * * @param element The start element of the [ArrayResource] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource where the read resource will be placed. * @param resourceFormat A type mask that specifies which formats are valid for the child elements - * of the array to be interpreted as. + * of the array to be interpreted as. * @return Whether or not the parsing was successful. */ private fun parseArrayImpl( element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource, - resourceFormat: Int - ): Boolean { + resourceFormat: Int): Boolean { parsedResource.name = parsedResource.name.copy(type = AaptResourceType.ARRAY) @@ -1360,8 +1267,7 @@ class TableExtractor( if (translatableValue == null) { logError( blameSource(parsedResource.source), - "Invalid value for 'translatable' attribute. Must be a boolean." - ) + "Invalid value for 'translatable' attribute. Must be a boolean.") walkToEndOfElement(element, eventReader) return false } @@ -1397,9 +1303,7 @@ class TableExtractor( } !shouldIgnoreElement(childName) -> { logError( - blameSource(childSource), - "Unknown tag <$childName> in <${element.name}> resource." - ) + blameSource(childSource), "Unknown tag <$childName> in <${element.name}> resource.") error = true walkToEndOfElement(childElement, eventReader) } @@ -1421,52 +1325,47 @@ class TableExtractor( * Parses the xml contained in a "configVarying" tag. * * @param element The start element of the [Style] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @return returns whether or not the parsing was a success. */ private fun parseConfigVarying( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ) = parseStyleImpl(element, eventReader, parsedResource, AaptResourceType.CONFIG_VARYING) + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource) = + parseStyleImpl(element, eventReader, parsedResource, AaptResourceType.CONFIG_VARYING) /** * Parses the xml contained in a "style" tag. * * @param element The start element of the [Style] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @return returns whether or not the parsing was a success. */ private fun parseStyle( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ) = parseStyleImpl(element, eventReader, parsedResource, AaptResourceType.STYLE) + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource) = + parseStyleImpl(element, eventReader, parsedResource, AaptResourceType.STYLE) /** * Parses the xml element as a [Style]. * * @param element The start element of the [Style] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @param type The actual type of the [Style] being parsed, which is reflected in the - * [ResourceName] of the parsed resource. + * [ResourceName] of the parsed resource. * @return returns whether or not the parsing was a success. */ private fun parseStyleImpl( element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource, - type: AaptResourceType - ): Boolean { + type: AaptResourceType): Boolean { parsedResource.name = parsedResource.name.copy(type = type) @@ -1524,8 +1423,7 @@ class TableExtractor( if (!shouldIgnoreElement(childName)) { logError( blameSource(source, childElement.location), - "Unrecognized child element <$childName> of <${element.name}> resource." - ) + "Unrecognized child element <$childName> of <${element.name}> resource.") error = true } walkToEndOfElement(childElement, eventReader) @@ -1544,17 +1442,14 @@ class TableExtractor( * Parses the xml element contained by a "declare-styleable" tag as a [Styleable] resource. * * @param element The start element of the [Styleable] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @return returns whether or not the parsing was a success. */ private fun parseDeclareStyleable( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { parsedResource.name = parsedResource.name.copy(type = AaptResourceType.STYLEABLE) // TODO(b/153454907): add option for preservation of stylable visibility to match aapt2 @@ -1565,8 +1460,7 @@ class TableExtractor( if (parsedResource.config != defaultConfig) { logger?.warning( "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", - blameSource(source, element.location) - ) + blameSource(source, element.location)) parsedResource.config = defaultConfig } @@ -1624,12 +1518,11 @@ class TableExtractor( styleable.entries.add(nameReference) parsedResource.children.add(childResource) + } else { if (!shouldIgnoreElement(childName)) { logError( - blameSource(itemSource), - "Unknown tag of <$childName> in <${element.name}> resource." - ) + blameSource(itemSource), "Unknown tag of <$childName> in <${element.name}> resource.") error = true } walkToEndOfElement(childElement, eventReader) @@ -1650,32 +1543,26 @@ class TableExtractor( * Parses the xml element surrounded by the "overlayable" tag as an [Overlayable] resource. * * @param element The start element of the [Overlayable] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @return returns whether or not the parsing was a success. */ private fun parseOverlayable( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { val defaultConfig = ConfigDescription() if (parsedResource.config != defaultConfig) { logger?.warning( "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", - blameSource(source, element.location) - ) + blameSource(source, element.location)) } val nameAttribute = element.getAttributeByName(QName(null, "name")) if (nameAttribute == null) { logError( - blameSource(source, element.location), - " tag must have a 'name' attribute" - ) + blameSource(source, element.location), " tag must have a 'name' attribute") return false } @@ -1683,9 +1570,8 @@ class TableExtractor( if (actorAttribute != null && !actorAttribute.value.startsWith(Overlayable.ACTOR_SCHEME_URI)) { logError( blameSource(source, element.location), - " tag has a 'actor' attribute: '${actorAttribute.value}'. " + - "Value must use the schema: ${Overlayable.ACTOR_SCHEME_URI}." - ) + " tag has a 'actor' attribute: '${actorAttribute.value}'. " + + "Value must use the schema: ${Overlayable.ACTOR_SCHEME_URI}.") return false } @@ -1732,7 +1618,8 @@ class TableExtractor( parsedResource.children.add(childResource) } } - childName.namespaceURI == XMLConstants.NULL_NS_URI && childName.localPart == "policy" -> { + childName.namespaceURI == XMLConstants.NULL_NS_URI && + childName.localPart == "policy" -> { ++depth val newPolicy = parsePoliciesFromElement(childElement, currentPolicies) @@ -1747,9 +1634,8 @@ class TableExtractor( } !shouldIgnoreElement(childName) -> { logError( - blameSource(source, childElement.location), - "Unrecognized tag '$childName' within an resource." - ) + blameSource(source, childElement.location), + "Unrecognized tag '$childName' within an resource.") error = true } else -> comment = "" @@ -1763,29 +1649,27 @@ class TableExtractor( * Parses the xml element as a [OverlayableItem] within an [Overlayable] resource. * * @param element The start element of the [OverlayableItem] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param policies The policies of the current policy block that this [OverlayableItem] is a part - * of. The value of policies should be non-zero. + * of. The value of policies should be non-zero. * @param overlayable The overlayable which the parsed resource will be a part of. * @param comment The comment that applies to this element. * @return The parsedResource representing the parsed Overlayable Item. If their is an issue - * parsing, then {@code null} is returned. + * parsing, then {@code null} is returned. */ private fun parseOverlayableItem( element: StartElement, eventReader: XMLEventReader, policies: Int, overlayable: Overlayable, - comment: String - ): ParsedResource? { + comment: String): ParsedResource? { if (policies == OverlayableItem.Policy.NONE) { logError( - blameSource(source, element.location), - " within an must be inside a block." - ) + blameSource(source, element.location), + " within an must be inside a block.") walkToEndOfElement(element, eventReader) return null } @@ -1794,9 +1678,8 @@ class TableExtractor( val nameAttribute = element.getAttributeByName(QName(null, "name")) if (nameAttribute == null || nameAttribute.value.isNullOrEmpty()) { logError( - blameSource(source, element.location), - " within an must have a 'name' attribute." - ) + blameSource(source, element.location), + " within an must have a 'name' attribute.") walkToEndOfElement(element, eventReader) return null } @@ -1804,9 +1687,8 @@ class TableExtractor( val typeAttribute = element.getAttributeByName(QName(null, "type")) if (typeAttribute == null || typeAttribute.value.isNullOrEmpty()) { logError( - blameSource(source, element.location), - " within an must have a 'type' attribute." - ) + blameSource(source, element.location), + " within an must have a 'type' attribute.") walkToEndOfElement(element, eventReader) return null } @@ -1814,9 +1696,8 @@ class TableExtractor( val type = resourceTypeFromTag(typeAttribute.value) if (type == null) { logError( - blameSource(source, element.location), - "Invalid resource type '${typeAttribute.value}' in in resource." - ) + blameSource(source, element.location), + "Invalid resource type '${typeAttribute.value}' in in resource.") walkToEndOfElement(element, eventReader) return null } @@ -1833,22 +1714,21 @@ class TableExtractor( } /** - * Parses the "type" attribute of the block for policies. This does not move the xml - * parser, as this does not read + * Parses the "type" attribute of the block for policies. This does not move the + * xml parser, as this does not read * * @param element The start element of the block. * @param oldPolicies The current policies to be overwritten. As nested policy blocks are not - * allowed, the oldPolicies is expected to be [OverlayableItem.Policy.NONE]. + * allowed, the oldPolicies is expected to be [OverlayableItem.Policy.NONE]. * @return The new policy values or null if an error occurred. */ - private fun parsePoliciesFromElement(element: StartElement, oldPolicies: Int): Int? { + private fun parsePoliciesFromElement( + element: StartElement, oldPolicies: Int): Int? { if (oldPolicies != OverlayableItem.Policy.NONE) { // If the policy list is not empty, then we are currently inside a policy element. logError( - blameSource(source, element.location), - "Policy blocks should not be nested recursively." - ) + blameSource(source, element.location), "Policy blocks should not be nested recursively.") return null } @@ -1862,24 +1742,21 @@ class TableExtractor( // Parse the polices separated by vertical bar characters to allow for specifying multiple // policies. Items within the policy tag will have the specified policy. for (string in typeAttribute.value.split('|')) { - newPolicy = - newPolicy or - when (string.trim()) { - "odm" -> OverlayableItem.Policy.ODM - "oem" -> OverlayableItem.Policy.OEM - "product" -> OverlayableItem.Policy.PRODUCT - "public" -> OverlayableItem.Policy.PUBLIC - "signature" -> OverlayableItem.Policy.SIGNATURE - "system" -> OverlayableItem.Policy.SYSTEM - "vendor" -> OverlayableItem.Policy.VENDOR - else -> { - logError( - blameSource(source, element.location), - " has unsupported type '${string.trim()}'." - ) - return null - } - } + newPolicy = newPolicy or when(string.trim()) { + "odm" -> OverlayableItem.Policy.ODM + "oem" -> OverlayableItem.Policy.OEM + "product" -> OverlayableItem.Policy.PRODUCT + "public" -> OverlayableItem.Policy.PUBLIC + "signature" -> OverlayableItem.Policy.SIGNATURE + "system" -> OverlayableItem.Policy.SYSTEM + "vendor" -> OverlayableItem.Policy.VENDOR + else -> { + logError( + blameSource(source, element.location), + " has unsupported type '${string.trim()}'.") + return null + } + } } return newPolicy @@ -1889,18 +1766,15 @@ class TableExtractor( * Parses the {@code element} as a [Plural] resource. * * @param element The start element of the [Plural] to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @return returns whether or not the parsing was a success. */ private fun parsePlural( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { - parsedResource.name = parsedResource.name.copy(type = AaptResourceType.PLURALS) + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { + parsedResource.name = parsedResource.name.copy(type=AaptResourceType.PLURALS) val plural = Plural() @@ -1924,32 +1798,29 @@ class TableExtractor( val quantityAttribute = childElement.getAttributeByName(QName("quantity")) if (quantityAttribute == null) { logError( - blameSource(source, childElement.location), - " in are required to have the 'quantity' attribute." - ) + blameSource(source, childElement.location), + " in are required to have the 'quantity' attribute.") walkToEndOfElement(childElement, eventReader) error = true continue } val trimmedQuantity = quantityAttribute.value.trim() - val pluralType = - when (trimmedQuantity) { - "zero" -> Plural.Type.ZERO - "one" -> Plural.Type.ONE - "two" -> Plural.Type.TWO - "few" -> Plural.Type.FEW - "many" -> Plural.Type.MANY - "other" -> Plural.Type.OTHER - else -> null - } + val pluralType = when (trimmedQuantity) { + "zero" -> Plural.Type.ZERO + "one" -> Plural.Type.ONE + "two" -> Plural.Type.TWO + "few" -> Plural.Type.FEW + "many" -> Plural.Type.MANY + "other" -> Plural.Type.OTHER + else -> null + } if (pluralType == null) { logError( - blameSource(source, childElement.location), - "Unrecognized quantity value '$trimmedQuantity' specified in " + - "in resource." - ) + blameSource(source, childElement.location), + "Unrecognized quantity value '$trimmedQuantity' specified in " + + "in resource.") walkToEndOfElement(childElement, eventReader) error = true continue @@ -1959,9 +1830,8 @@ class TableExtractor( if (plural.values[pluralIndex] != null) { logError( blameSource(source, childElement.location), - " has quantity '$trimmedQuantity' which has already been specified " + - "in resource '${element.name}'" - ) + " has quantity '$trimmedQuantity' which has already been specified " + + "in resource '${element.name}'") error = true walkToEndOfElement(childElement, eventReader) continue @@ -1975,9 +1845,8 @@ class TableExtractor( } else { if (!shouldIgnoreElement(childName)) { logError( - blameSource(source, childElement.location), - "Unrecognized tag '$childName' within an resource." - ) + blameSource(source, childElement.location), + "Unrecognized tag '$childName' within an resource.") error = true } walkToEndOfElement(childElement, eventReader) @@ -1996,23 +1865,19 @@ class TableExtractor( * Parses the {@code element} as a public resource. * * @param element The start element of the resource to be parsed. - * @param eventReader The xml to be read. The event reader should have just pulled the {@code - * StartElement} element. After this method is invoked the eventReader will be placed after the - * corresponding end tag for element. + * @param eventReader The xml to be read. The event reader should have just pulled the + * {@code StartElement} element. After this method is invoked the eventReader will be placed + * after the corresponding end tag for element. * @param parsedResource Where the parsed resource will be placed. * @return returns whether or not the parsing was a success. */ private fun parsePublic( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { - if (options.visibility != null) { + if (options.visibility != null ) { logError( - blameSource(source, element.location), - " tag not allowed with --visibility flag." - ) + blameSource(source, element.location), + " tag not allowed with --visibility flag.") walkToEndOfElement(element, eventReader) return false } @@ -2020,8 +1885,7 @@ class TableExtractor( if (parsedResource.config != ConfigDescription()) { logger?.warning( "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", - blameSource(source, element.location) - ) + blameSource(source, element.location)) } val typeAttribute = element.getAttributeByName(QName("type")) @@ -2034,9 +1898,8 @@ class TableExtractor( val parsedType = resourceTypeFromTag(typeAttribute.value) if (parsedType == null) { logError( - blameSource(source, element.location), - "Invalid resource type '${typeAttribute.value}' in resource." - ) + blameSource(source, element.location), + "Invalid resource type '${typeAttribute.value}' in resource.") walkToEndOfElement(element, eventReader) return false } @@ -2048,9 +1911,8 @@ class TableExtractor( val id = parseResourceId(idAttribute.value) if (id == null) { logError( - blameSource(source, element.location), - "Invalid resource Id '${idAttribute.value}' in resource." - ) + blameSource(source, element.location), + "Invalid resource Id '${idAttribute.value}' in resource.") walkToEndOfElement(element, eventReader) return false } @@ -2072,32 +1934,26 @@ class TableExtractor( * corresponding end of {@code element} */ private fun parsePublicGroup( - element: StartElement, - eventReader: XMLEventReader, - parsedResource: ParsedResource - ): Boolean { + element: StartElement, eventReader: XMLEventReader, parsedResource: ParsedResource): Boolean { if (options.visibility != null) { logError( - blameSource(source, element.location), - " tag not allowed with --visibility flag." - ) + blameSource(source, element.location), + " tag not allowed with --visibility flag.") walkToEndOfElement(element, eventReader) return false } if (parsedResource.config != ConfigDescription()) { logger?.warning( - "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", - blameSource(source, element.location) - ) + "Ignoring configuration '${parsedResource.config}' for <${element.name}> tag.", + blameSource(source, element.location)) } val typeAttribute = element.getAttributeByName(QName("type")) if (typeAttribute == null) { logError( - blameSource(source, element.location), - " must have a 'type' attribute." - ) + blameSource(source, element.location), + " must have a 'type' attribute.") walkToEndOfElement(element, eventReader) return false } @@ -2105,9 +1961,8 @@ class TableExtractor( val parsedType = resourceTypeFromTag(typeAttribute.value) if (parsedType == null) { logError( - blameSource(source, element.location), - "Invalid resource type '${typeAttribute.value}' in ." - ) + blameSource(source, element.location), + "Invalid resource type '${typeAttribute.value}' in .") walkToEndOfElement(element, eventReader) return false } @@ -2115,9 +1970,8 @@ class TableExtractor( val idAttribute = element.getAttributeByName(QName("first-id")) if (idAttribute == null) { logError( - blameSource(source, element.location), - " must have a 'first-id' attribute." - ) + blameSource(source, element.location), + " must have a 'first-id' attribute.") walkToEndOfElement(element, eventReader) return false } @@ -2125,9 +1979,8 @@ class TableExtractor( val idVal = parseResourceId(idAttribute.value) if (idVal == null) { logError( - blameSource(source, element.location), - "Invalid resource ID '${idAttribute.value}' in . Integer expected." - ) + blameSource(source, element.location), + "Invalid resource ID '${idAttribute.value}' in . Integer expected.") walkToEndOfElement(element, eventReader) return false } @@ -2159,11 +2012,9 @@ class TableExtractor( val itemSource = source.withLine(childElement.location.lineNumber) if (childName.namespaceURI.isEmpty() && childName.localPart == "public") { val nameAttribute = childElement.getAttributeByName(QName("name")) - if (nameAttribute == null) { + if (nameAttribute == null) { logError( - blameSource(source, childElement.location), - " must have a 'name' attribute." - ) + blameSource(source, childElement.location), " must have a 'name' attribute.") walkToEndOfElement(childElement, eventReader) error = true continue @@ -2172,9 +2023,8 @@ class TableExtractor( val childIdAttribute = childElement.getAttributeByName(QName("id")) if (childIdAttribute != null) { logError( - blameSource(source, childElement.location), - "'id' attribute is not allowed on tags within a ." - ) + blameSource(source, childElement.location), + "'id' attribute is not allowed on tags within a .") walkToEndOfElement(childElement, eventReader) error = true continue @@ -2183,9 +2033,8 @@ class TableExtractor( val childTypeAttribute = childElement.getAttributeByName(QName("type")) if (childTypeAttribute != null) { logError( - blameSource(source, childElement.location), - "'type' attribute is not allowed on tags within a ." - ) + blameSource(source, childElement.location), + "'type' attribute is not allowed on tags within a .") walkToEndOfElement(childElement, eventReader) error = true continue @@ -2202,9 +2051,8 @@ class TableExtractor( } else { if (!shouldIgnoreElement(childName)) { logError( - blameSource(source, childElement.location), - "Unrecognized tag '$childName' within an resource." - ) + blameSource(source, childElement.location), + "Unrecognized tag '$childName' within an resource.") error = true } walkToEndOfElement(childElement, eventReader) @@ -2247,15 +2095,12 @@ class TableExtractor( resource.comment = parsedResource.comment resource.source = parsedResource.source - if ( - !table.addResourceWithId( + if (!table.addResourceWithId( parsedResource.name, parsedResource.resourceId, parsedResource.config, parsedResource.productString, - resource - ) - ) { + resource)) { return false } } diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ToolFingerprint.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/ToolFingerprint.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/UnicodeUtil.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/UnicodeUtil.kt old mode 100755 new mode 100644 index 014a384d4..55c925af0 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/UnicodeUtil.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/UnicodeUtil.kt @@ -675,14 +675,15 @@ fun isJavaIdentifier(string: String): Boolean { val firstCodePoint = string.codePointAt(0) if (!isXidStart(firstCodePoint) && - firstCodePoint != '_'.toInt() && - firstCodePoint != '$'.toInt()) { + firstCodePoint != '_'.code && + firstCodePoint != '$'.code + ) { return false } for (i in 1.until(string.codePointCount(0, string.length))) { val codePoint = string.codePointAt(i) - if (!isXidContinue(codePoint) && codePoint != '$'.toInt()) { + if (!isXidContinue(codePoint) && codePoint != '$'.code) { return false } } @@ -700,15 +701,15 @@ fun isValidResourceEntryName(string: String): Boolean { // Resources can start with '_'. val firstCodePoint = string.codePointAt(0) - if (!isXidStart(firstCodePoint) && firstCodePoint != '_'.toInt()) { + if (!isXidStart(firstCodePoint) && firstCodePoint != '_'.code) { return false } for (i in 1.until(string.codePointCount(0, string.length))) { val codePoint = string.codePointAt(i) - if (!isXidContinue(codePoint) && codePoint != '.'.toInt() && codePoint != '-'.toInt()) { + if (!isXidContinue(codePoint) && codePoint != '.'.code && codePoint != '-'.code) { return false } } return true -} \ No newline at end of file +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlProcessor.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlProcessor.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlResource.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlResource.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlResourceBuilder.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlResourceBuilder.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlStringBuilder.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlStringBuilder.kt old mode 100755 new mode 100644 index aefff9eb4..909764ce6 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlStringBuilder.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlStringBuilder.kt @@ -107,11 +107,11 @@ class XmlStringBuilder(private val preserveSpaces: Boolean = false) { str: String ): Boolean { when (codePoint) { - 't'.toInt() -> textBuilder.append('\t') - 'n'.toInt() -> textBuilder.append('\n') - '#'.toInt(), '@'.toInt(), '?'.toInt(), '"'.toInt(), '\''.toInt(), '\\'.toInt() -> + 't'.code -> textBuilder.append('\t') + 'n'.code -> textBuilder.append('\n') + '#'.code, '@'.code, '?'.code, '"'.code, '\''.code, '\\'.code -> textBuilder.appendCodePoint(codePoint) - 'u'.toInt() -> { + 'u'.code -> { if (!appendUnicodeEscapeSequence(codePoints, textBuilder)) { error = "Invalid unicode escape sequence in string\n\"$str\"" return false @@ -160,7 +160,7 @@ class XmlStringBuilder(private val preserveSpaces: Boolean = false) { lastChar = textBuilder.length lastCodepointWasBackslash = false } - codePoint == '\\'.toInt() -> { + codePoint == '\\'.code -> { if (codePoints.hasNext()) { codePoint = codePoints.nextInt() if (!handleEscape(codePoint, textBuilder, codePoints, str)) { @@ -171,7 +171,7 @@ class XmlStringBuilder(private val preserveSpaces: Boolean = false) { lastCodepointWasBackslash = true } } - codePoint == '\"'.toInt() && !preserveSpaces -> { + codePoint == '\"'.code && !preserveSpaces -> { // only toggle quote when we are not preserving spaces. inQuote = !inQuote if (firstQuote == -1) { @@ -180,7 +180,7 @@ class XmlStringBuilder(private val preserveSpaces: Boolean = false) { lastQuote = textBuilder.length lastChar = textBuilder.length } - codePoint == '\''.toInt() && !preserveSpaces && !inQuote -> { + codePoint == '\''.code && !preserveSpaces && !inQuote -> { // this should be escaped when we are not preserving spaces error = "Invalid unicode escape sequence in string\n\"{str}\"" return this diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlUtil.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlUtil.kt old mode 100755 new mode 100644 index d0c63fd4b..e2fac5248 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlUtil.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/XmlUtil.kt @@ -1,11 +1,12 @@ package com.android.aaptcompiler import com.android.SdkConstants +import jaxp.sun.xml.internal.stream.XMLInputFactoryImpl import jaxp.xml.stream.XMLEventReader import jaxp.xml.stream.XMLInputFactory import jaxp.xml.stream.events.StartElement -internal val xmlInputFactory = XMLInputFactory.newFactory() +internal val xmlInputFactory = XMLInputFactoryImpl() const val SCHEMA_PUBLIC_PREFIX = SdkConstants.URI_PREFIX const val SCHEMA_PRIVATE_PREFIX = "http://schemas.android.com/apk/prv/res/" diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/AConfiguration.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/AConfiguration.kt old mode 100755 new mode 100644 index 0f3e5ad29..c864faa4f --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/AConfiguration.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/AConfiguration.kt @@ -179,6 +179,26 @@ object AConfiguration { * resource qualifier. */ const val ACONFIGURATION_NAVHIDDEN_YES : Byte = 0x0002 + /** Grammatical gender: not specified. */ + const val ACONFIGURATION_GRAMMATICAL_GENDER_ANY : Byte = 0x00 + /** + * Grammatical gender: const value corresponding to the + * [neuter](@dacRoot/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier) + * resource qualifier. + */ + const val ACONFIGURATION_GRAMMATICAL_GENDER_NEUTER : Byte = 0x01 + /** + * Grammatical gender: const value corresponding to the + * [feminine](@dacRoot/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier) + * resource qualifier. + */ + const val ACONFIGURATION_GRAMMATICAL_GENDER_FEMININE : Byte = 0x02 + /** + * Grammatical gender: const value corresponding to the + * [masculine](@dacRoot/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier) + * resource qualifier. + */ + const val ACONFIGURATION_GRAMMATICAL_GENDER_MASCULINE : Byte = 0x03 /** Screen size: not specified. */ const val ACONFIGURATION_SCREENSIZE_ANY : Byte = 0x00 /** @@ -440,5 +460,11 @@ object AConfiguration { * 0 cannot be used, since it is used to represent an undefined MNC. */ const val ACONFIGURATION_MNC_ZERO : Int = 0xffff + /** + * Bit mask for + * [grammatical gender](@dacRoot/guide/topics/resources/providing-resources.html#GrammaticalInflectionQualifier) + * configuration. + */ + const val ACONFIGURATION_GRAMMATICAL_GENDER : Int = 0x20000 } diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleData.java b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleData.java old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleDataTables.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleDataTables.kt old mode 100755 new mode 100644 index dfeeb75d1..343b726de --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleDataTables.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/LocaleDataTables.kt @@ -1,5 +1,7 @@ package com.android.aaptcompiler.android +import com.itsaky.androidide.utils.intByteMapOf + internal val SCRIPT_CODES = arrayOf( /* 0 */ byteArrayOf('A'.code.toByte(), 'h'.code.toByte(), 'o'.code.toByte(), 'm'.code.toByte()), /* 1 */ byteArrayOf('A'.code.toByte(), 'r'.code.toByte(), 'a'.code.toByte(), 'b'.code.toByte()), @@ -91,7 +93,7 @@ internal val SCRIPT_CODES = arrayOf( /* 87 */ byteArrayOf('~'.code.toByte(), '~'.code.toByte(), '~'.code.toByte(), 'A'.code.toByte()), /* 88 */ byteArrayOf('~'.code.toByte(), '~'.code.toByte(), '~'.code.toByte(), 'B'.code.toByte())) -internal val LIKELY_SCRIPTS = mapOf( +internal val LIKELY_SCRIPTS = intByteMapOf( 0x61610000 to 40, // aa -> Latn 0xa0000000.toInt() to 40, // aai -> Latn 0xa8000000.toInt() to 40, // aak -> Latn diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Ref.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Ref.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResTableConfig.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResTableConfig.kt old mode 100755 new mode 100644 index 53cee21a5..1f8403c8d --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResTableConfig.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResTableConfig.kt @@ -2,6 +2,7 @@ package com.android.aaptcompiler.android import com.android.aaptcompiler.android.AConfiguration.ACONFIGURATION_COLOR_MODE import com.android.aaptcompiler.android.AConfiguration.ACONFIGURATION_DENSITY +import com.android.aaptcompiler.android.AConfiguration.ACONFIGURATION_GRAMMATICAL_GENDER import com.android.aaptcompiler.android.AConfiguration.ACONFIGURATION_KEYBOARD import com.android.aaptcompiler.android.AConfiguration.ACONFIGURATION_KEYBOARD_HIDDEN import com.android.aaptcompiler.android.AConfiguration.ACONFIGURATION_LAYOUTDIR @@ -52,6 +53,7 @@ open class ResTableConfig( var keyboard: Byte = 0, var navigation: Byte = 0, var inputFlags: Byte = 0, + var grammaticalInflection: Byte = 0, // padding: Byte, // screenSize block var screenWidth: Int = 0, @@ -84,6 +86,7 @@ open class ResTableConfig( locale: Int, screenType: Int, input: Int, + grammaticalInflection: Int, screenSize: Int, version: Int, screenConfig: Int, @@ -104,6 +107,7 @@ open class ResTableConfig( keyboardFromInput(input), navigationFromInput(input), inputFlagsFromInput(input), + grammaticalInflection.deviceToHost().toByte(), screenWidthFromScreenSize(screenSize), screenHeightFromScreenSize(screenSize), sdkVersionFromVersion(version), @@ -132,6 +136,7 @@ open class ResTableConfig( other.keyboard, other.navigation, other.inputFlags, + other.grammaticalInflection, other.screenWidth, other.screenHeight, other.sdkVersion, @@ -231,6 +236,17 @@ open class ResTableConfig( appendDirLocale(result) + if (grammaticalInflection != GRAMMATICAL_GENDER.ANY) { + result.append( + when (grammaticalInflection) { + GRAMMATICAL_GENDER.NEUTER -> "neuter" + GRAMMATICAL_GENDER.FEMININE -> "feminine" + GRAMMATICAL_GENDER.MASCULINE -> "masculine" + else -> "grammaticalInflection=$grammaticalInflection" + }) + result.append("-") + } + val layoutDir = (screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK).toByte() if (layoutDir != SCREEN_LAYOUT.DIR_ANY) { result.append( @@ -570,6 +586,9 @@ open class ResTableConfig( if (compareLocales(other) != 0) { result = result or CONFIG_LOCALE } + if (grammaticalInflection != other.grammaticalInflection) { + result = result or CONFIG_GRAMMATICAL_GENDER + } return result } @@ -586,6 +605,11 @@ open class ResTableConfig( return localeDiff } + when { + grammaticalInflection > other.grammaticalInflection -> return 1 + grammaticalInflection < other.grammaticalInflection -> return -1 + } + val screenType = getScreenType().deviceToHost() val oScreenType = other.getScreenType().deviceToHost() when { @@ -648,7 +672,7 @@ open class ResTableConfig( return 0 } - + fun compareLocales(other: ResTableConfig): Int { val locale = getLocale().deviceToHost() val oLocale = other.getLocale().deviceToHost() @@ -659,7 +683,7 @@ open class ResTableConfig( locale > oLocale -> return 1 oLocale > locale -> return -1 } - + // The language & region are equal, so compare the scripts, variants and numbering systems in // that order. Comparison of variants and numbering systems should happen very infrequently (if // at all.) @@ -670,12 +694,12 @@ open class ResTableConfig( if (scriptDiff != 0) { return scriptDiff } - + val variantDiff = compareArrays(localeVariant, other.localeVariant) if (variantDiff != 0) { return variantDiff } - + return compareArrays(localeNumberSystem, other.localeNumberSystem) } @@ -699,6 +723,10 @@ open class ResTableConfig( localeDiff > 0 -> return true } + if (grammaticalInflection.isTruthy() || other.grammaticalInflection.isTruthy()) { + if (grammaticalInflection != other.grammaticalInflection) return grammaticalInflection.isTruthy() + } + if (screenLayout.isTruthy() || other.screenLayout.isTruthy()) { if (screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK != other.screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK) { @@ -881,6 +909,10 @@ open class ResTableConfig( return true } + if (grammaticalInflection != other.grammaticalInflection && requested.grammaticalInflection.isTruthy()) { + return grammaticalInflection.isTruthy() + } + if (screenLayout.isTruthy() || other.screenLayout.isTruthy()) { val myLayoutDir = screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK val otherLayoutDir = other.screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK @@ -1258,6 +1290,10 @@ open class ResTableConfig( } } + if (grammaticalInflection.isTruthy() && grammaticalInflection != settings.grammaticalInflection) { + return false + } + if (getScreenConfig().isTruthy()) { val layoutDir = screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK val setLayoutDir = settings.screenLayout.toInt() and SCREEN_LAYOUT.DIR_MASK @@ -1478,6 +1514,7 @@ open class ResTableConfig( const val CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR const val CONFIG_SCREEN_ROUND = ACONFIGURATION_SCREEN_ROUND const val CONFIG_COLOR_MODE = ACONFIGURATION_COLOR_MODE + const val CONFIG_GRAMMATICAL_GENDER = ACONFIGURATION_GRAMMATICAL_GENDER const val SCREEN_CONFIG_MIN_SIZE = 32 const val SCREEN_DP_MIN_SIZE = 36 @@ -1552,6 +1589,7 @@ open class ResTableConfig( val locale = buffer.int val screenType = buffer.int val input = buffer.int + val grammaticalInflection = buffer.int val screenSize = buffer.int val version = buffer.int @@ -1587,6 +1625,7 @@ open class ResTableConfig( locale, screenType, input, + grammaticalInflection, screenSize, version, screenConfig, @@ -1660,12 +1699,14 @@ open class ResTableConfig( const val ANY = AConfiguration.ACONFIGURATION_ORIENTATION_ANY const val PORT = AConfiguration.ACONFIGURATION_ORIENTATION_PORT const val LAND = AConfiguration.ACONFIGURATION_ORIENTATION_LAND + @Suppress("DEPRECATION") // Legacy support const val SQUARE = AConfiguration.ACONFIGURATION_ORIENTATION_SQUARE } object TOUCHSCREEN { const val ANY = AConfiguration.ACONFIGURATION_TOUCHSCREEN_ANY const val NOTOUCH = AConfiguration.ACONFIGURATION_TOUCHSCREEN_NOTOUCH + @Suppress("DEPRECATION") // Legacy support const val STYLUS = AConfiguration.ACONFIGURATION_TOUCHSCREEN_STYLUS const val FINGER = AConfiguration.ACONFIGURATION_TOUCHSCREEN_FINGER } @@ -1717,6 +1758,13 @@ open class ResTableConfig( (AConfiguration.ACONFIGURATION_NAVHIDDEN_YES.toInt() shl NAVHIDDEN_SHIFT).toByte() } + object GRAMMATICAL_GENDER { + const val ANY = AConfiguration.ACONFIGURATION_GRAMMATICAL_GENDER_ANY + const val NEUTER = AConfiguration.ACONFIGURATION_GRAMMATICAL_GENDER_NEUTER + const val FEMININE = AConfiguration.ACONFIGURATION_GRAMMATICAL_GENDER_FEMININE + const val MASCULINE = AConfiguration.ACONFIGURATION_GRAMMATICAL_GENDER_MASCULINE + } + // Screensize values object SCREENSIZE { const val WIDTH_ANY = 0 @@ -1799,4 +1847,4 @@ open class ResTableConfig( const val HDR_NO = (AConfiguration.ACONFIGURATION_HDR_NO.toInt() shl HDR_SHIFT).toByte() const val HDR_YES = (AConfiguration.ACONFIGURATION_HDR_YES.toInt() shl HDR_SHIFT).toByte() } -} \ No newline at end of file +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt old mode 100755 new mode 100644 index e4c4e55c6..3a60afb5f --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt @@ -20,22 +20,25 @@ import com.android.aaptcompiler.StringPool import com.android.aaptcompiler.parseFloat import java.nio.ByteBuffer import java.nio.ByteOrder +import kotlin.text.Charsets /** - * Definitions of resource data structures.

Transliterated from: * + * Definitions of resource data structures. + *

Transliterated from: * * https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp */ -/** Header that appears at the front of every data chunk in a resource. */ +/** + * Header that appears at the front of every data chunk in a resource. + */ class ResChunkHeader( // Type identifier for this chunk. The meaning of this value depends on the containing chunk. val typeId: Short, // Size of the chunk header (in bytes). Adding this value to the address of the chunk will be // the address of the associated data if any. - val headerSize: Short -) { + val headerSize: Short) { - constructor() : this(0, 0) + constructor(): this(0, 0) // Total size of this chunk (in bytes). This is the chunkSize plus the size of any data // associated with this chunk. Adding the this to the address of this chunk will completely skip @@ -71,52 +74,48 @@ enum class ChunkType(val id: Short) { TABLE_OVERLAYABLE_POLICY_TYPE(0x0205), } -/** Representation of a value in a resource, supplying type information. */ +/** + * Representation of a value in a resource, supplying type information. + */ data class ResValue(val dataType: DataType, val data: Int, val size: Short = 0) { constructor() : this(DataType.NULL, 0) - /** type of the data value. */ + // type of the data value. enum class DataType(val byteValue: Byte) { - /** - * The 'data' is either 0 or 1, specifying this resource is undefined or empty, respectively. - */ + // The 'data' is either 0 or 1, specifying this resource is undefined or empty, respectively. NULL(0X00), - /** The 'data' holds a ResTable reference, i.e. a reference to another resource. */ + // The 'data' holds a ResTable reference, i.e. a reference to another resource. REFERENCE(0x01), - /** The 'data' holds an attribute resource identifier. */ + // The 'data' holds an attribute resource identifier. ATTRIBUTE(0x02), - /** The 'data' holds an index into the containing resource table's string pool. */ + // The 'data' holds an index into the containing resource table's string pool. STRING(0x03), - /** The 'data' holds a single-precision floating point number. */ + // The 'data' holds a single-precision floating point number. FLOAT(0x04), - /** The 'data' holds a fixed point number encoding a dimension value, e.g. 100in. */ + // The 'data' holds a fixed point number encoding a dimension value, e.g. 100in. DIMENSION(0x05), - /** The 'data' holds a fixed point number encoding a fraction of a container. */ + // The 'data' holds a fixed point number encoding a fraction of a container. FRACTION(0x06), - /** - * The 'data' holds a dynamic ResTable reference, which needs to be resolved before it can be - * used as a REFERENCE. - */ + // The 'data' holds a dynamic ResTable reference, which needs to be resolved before it can be + // used as a REFERENCE. DYNAMIC_REFERENCE(0x07), - /** - * The 'data' holds an attribute resource identifier, which needs to be resolved before it can - * be used like a ATTRIBUTE. - */ + // The 'data' holds an attribute resource identifier, which needs to be resolved before it can + // be used like a ATTRIBUTE. DYNAMIC_ATTRIBUTE(0x08), - /** The 'data' is a raw integer of the form n..n. */ + // The 'data' is a raw integer of the form n..n. INT_DEC(0x10), - /** The 'data' is a raw integer value of the form 0xn..n. */ + // The 'data' is a raw integer value of the form 0xn..n. INT_HEX(0x11), - /** The 'data' is either 0 or 1, for input 'false' or 'true' respectively. */ + // The 'data' is either 0 or 1, for input 'false' or 'true' respectively. INT_BOOLEAN(0x12), - /** The 'data' is a raw integer value of the form #aarrggbb. */ + // The 'data' is a raw integer value of the form #aarrggbb. INT_COLOR_ARGB8(0x1c), - /** The 'data' is a raw integer value of the form #rrggbb. */ + // The 'data' is a raw integer value of the form #rrggbb. INT_COLOR_RGB8(0x1d), - /** The 'data' is a raw integer value of the form #argb */ + // The 'data' is a raw integer value of the form #argb INT_COLOR_ARGB4(0x1e), - /** The 'data is a raw integer value of the form #rgb */ + // The 'data is a raw integer value of the form #rgb INT_COLOR_RGB4(0x1f); companion object { @@ -134,10 +133,6 @@ data class ResValue(val dataType: DataType, val data: Int, val size: Short = 0) const val UNIT_SHIFT = 0 const val UNIT_MASK = 0xf - // ================================== - // The values here are same as specified in TypedValue - // ================================== - // TYPE_DIMENSION: Value is raw pixels. const val UNIT_PX = 0 // TYPE_DIMENSION: Value is Device Independent Pixels. @@ -184,12 +179,14 @@ data class ResValue(val dataType: DataType, val data: Int, val size: Short = 0) } } -/** Reference to a string in a string pool */ + +/** + * Reference to a string in a string pool + */ data class ResStringPoolRef( // Index into the string pool table (unsigned 32 bit offset from the indices immediately after // ResStringPoolHeader) at which to find the location of the string data in the pool - val index: Int -) + val index: Int) /** * Definition of a pool of strings. @@ -202,8 +199,8 @@ data class ResStringPoolRef( * to indicate this format is being used. * *

If {@code styleCount} is not zero, then immediately following the array of indices into the - * string table is another array of indices into a style table starting at {@code stylesStart}. Each - * entry in the style table is an array of {@code ResStringPoolSpan} structures. + * string table is another array of indices into a style table starting at {@code stylesStart}. + * Each entry in the style table is an array of {@code ResStringPoolSpan} structures. */ data class ResStringPoolHeader( val header: ResChunkHeader, @@ -211,10 +208,9 @@ data class ResStringPoolHeader( val stringCount: Int, // Number of style span arrays in the pool (number of 32 bit indices that follow the string // indices). - val styleCount: Int -) { + val styleCount: Int) { - constructor() : this(ResChunkHeader(), 0, 0) + constructor(): this(ResChunkHeader(), 0, 0) var flags: Int = 0 // Index from header of the string data. @@ -230,7 +226,9 @@ data class ResStringPoolHeader( } } -/** This structure defines a span of style information associated with a string in the pool. */ +/** + * This structure defines a span of style information associated with a string in the pool. + */ data class ResStringPoolSpan( // this is the name of the span -- that is, the name of the XML tag that defined it. The special // value END indicates the end of an array of spans. @@ -246,15 +244,13 @@ data class ResStringPoolSpan( } /** Convenience class for accessing data in a String Pool Flattened Resource */ -class ResStringPool -private constructor( +class ResStringPool private constructor( val data: ByteBuffer, val header: ResStringPoolHeader, val stringPoolSize: Int, val strings: List, val stylesPoolSize: Int, - val styles: List> -) { + val styles: List>) { companion object { fun get(buffer: ByteBuffer, length: Int): ResStringPool { @@ -268,11 +264,10 @@ private constructor( val headerSize = buffer.getShort(2).deviceToHost() val resourceSize = buffer.getInt(4).deviceToHost() - if ( - typeId != ChunkType.STRING_POOL_TYPE.id || - headerSize != ResStringPoolHeader.SIZE || - resourceSize < headerSize || - resourceSize > length + if (typeId != ChunkType.STRING_POOL_TYPE.id || + headerSize != ResStringPoolHeader.SIZE || + resourceSize < headerSize || + resourceSize > length ) { error("Invalid StringPool: Header has invalid format.") } @@ -283,10 +278,7 @@ private constructor( val header = ResStringPoolHeader( - chunkHeader, - buffer.getInt(8).deviceToHost(), - buffer.getInt(12).deviceToHost() - ) + chunkHeader, buffer.getInt(8).deviceToHost(), buffer.getInt(12).deviceToHost()) header.flags = buffer.getInt(16).deviceToHost() header.stringsStart = buffer.getInt(20).deviceToHost() header.stylesStart = buffer.getInt(24).deviceToHost() @@ -295,10 +287,8 @@ private constructor( val strings = mutableListOf() if (header.stringCount != 0) { // we need to check overflow and ensure the string indexes can fit. - if ( - header.stringCount * 4 < header.stringCount || - (header.header.headerSize + (header.stringCount * 4)) > resourceSize - ) { + if (header.stringCount*4 < header.stringCount || + (header.header.headerSize + (header.stringCount*4)) > resourceSize) { error("Invalid StringPool: Buffer not large enough for string indices.") } @@ -306,7 +296,7 @@ private constructor( // There should at least be enough space for the smallest string. // (2 bytes length, null terminator) - if (header.stringsStart + 2 >= resourceSize) { + if (header.stringsStart+2 >= resourceSize) { error("Invalid StringPool: Buffer not large enough for strings.") } @@ -324,7 +314,7 @@ private constructor( stringPoolSize = (header.stylesStart - header.stringsStart) / charSize } - if (stringPoolSize == 0) { + if (stringPoolSize ==0) { error("Invalid StringPool: Space for strings in header is too small.") } @@ -343,13 +333,13 @@ private constructor( val styles = mutableListOf>() if (header.styleCount != 0) { - var currentStyleIndex = headerSize.toInt() + header.stringCount * 4 + var currentStyleIndex = headerSize.toInt() + header.stringCount*4 // invariant: integer overflow in calculating styles if (currentStyleIndex < headerSize.toInt()) { error("Invalid StringPool: Integer overflow encountered while decoding styles.") } - stylePoolSize = (resourceSize - header.stylesStart) / 4 + stylePoolSize = (resourceSize - header.stylesStart)/4 for (i in 0.until(header.styleCount)) { val styleLocation = buffer.getInt(currentStyleIndex).deviceToHost() + header.stylesStart @@ -370,31 +360,29 @@ private constructor( val firstByteUTF16 = buffer.get(stringPosition) ++stringPosition - val utf16Length = - when { - (firstByteUTF16.toInt() and StringPool.TWO_BYTE_UTF8_LENGTH_SIGNIFIER) != 0 -> { - val secondByte = buffer.get(stringPosition).toInt() and 0xff - ++stringPosition + val utf16Length = when { + (firstByteUTF16.toInt() and StringPool.TWO_BYTE_UTF8_LENGTH_SIGNIFIER) != 0 -> { + val secondByte = buffer.get(stringPosition).toInt() and 0xff + ++stringPosition - ((firstByteUTF16.toInt() shl 8) + secondByte) and StringPool.UTF8_ENCODE_LENGTH_MAX - } - else -> firstByteUTF16.toInt() + ((firstByteUTF16.toInt() shl 8) + secondByte) and StringPool.UTF8_ENCODE_LENGTH_MAX } + else -> firstByteUTF16.toInt() + } // In UTF8 mode, the length in UTF8 comes next val firstByteUTF8 = buffer.get(stringPosition) ++stringPosition - val utf8Length = - when { - (firstByteUTF8.toInt() and StringPool.TWO_BYTE_UTF8_LENGTH_SIGNIFIER) != 0 -> { - val secondByte = buffer.get(stringPosition).toInt() and 0xff - ++stringPosition + val utf8Length = when { + (firstByteUTF8.toInt() and StringPool.TWO_BYTE_UTF8_LENGTH_SIGNIFIER) != 0 -> { + val secondByte = buffer.get(stringPosition).toInt() and 0xff + ++stringPosition - ((firstByteUTF8.toInt() shl 8) + secondByte) and StringPool.UTF8_ENCODE_LENGTH_MAX - } - else -> firstByteUTF8.toInt() + ((firstByteUTF8.toInt() shl 8) + secondByte) and StringPool.UTF8_ENCODE_LENGTH_MAX } + else -> firstByteUTF8.toInt() + } // pull the bytes out of the buffer. val array = ByteArray(utf8Length) @@ -420,15 +408,14 @@ private constructor( val firstShort = buffer.getShort(stringPosition).deviceToHost() stringPosition += 2 - val utf16Length = - when { - (firstShort.toInt() and StringPool.TWO_CHAR_UTF16_LENGTH_SIGNIFIER) != 0 -> { - val secondShort = buffer.getShort(stringPosition).deviceToHost().toInt() and 0xffff - stringPosition += 2 - ((firstShort.toInt() shl 16) + secondShort) and StringPool.UTF16_ENCODE_LENGTH_MAX - } - else -> firstShort.toInt() + val utf16Length = when { + (firstShort.toInt() and StringPool.TWO_CHAR_UTF16_LENGTH_SIGNIFIER) != 0 -> { + val secondShort = buffer.getShort(stringPosition).deviceToHost().toInt() and 0xffff + stringPosition += 2 + ((firstShort.toInt() shl 16) + secondShort) and StringPool.UTF16_ENCODE_LENGTH_MAX } + else -> firstShort.toInt() + } // pull the chars out of the buffer val array = CharArray(utf16Length) @@ -466,7 +453,7 @@ private constructor( } fun parseHex(codePoint: Int) = - when (codePoint) { + when(codePoint) { in '0'.code..'9'.code -> codePoint - '0'.code in 'A'.code..'F'.code -> codePoint - 'A'.code + 10 in 'a'.code..'f'.code -> codePoint - 'a'.code + 10 @@ -490,17 +477,17 @@ fun stringToInt(string: String): ResValue? { isNegative = true } - if (codePointCount == index || trimmedString.codePointAt(index) !in '0'.code..'9'.code) { + if (codePointCount == index + || trimmedString.codePointAt(index) !in '0'.code..'9'.code + ) { return null } val isHex: Boolean - if ( - codePointCount >= index + 2 && - trimmedString.codePointAt(index) == '0'.code && - trimmedString.codePointAt(index + 1) == 'x'.code - ) { + if (codePointCount >= index + 2 + && trimmedString.codePointAt(index) == '0'.code + && trimmedString.codePointAt(index+1) == 'x'.code) { isHex = true index += 2 @@ -538,7 +525,7 @@ fun stringToInt(string: String): ResValue? { ++index val decValue = codePoint - '0'.code - value = (value * 10) + decValue + value = (value*10) + decValue val outOfBounds = if (isNegative) -value < Int.MIN_VALUE.toLong() else value > Int.MAX_VALUE.toLong() @@ -553,37 +540,26 @@ fun stringToInt(string: String): ResValue? { } return ResValue( - if (isHex) ResValue.DataType.INT_HEX else ResValue.DataType.INT_DEC, - value.toInt() - ) + if(isHex) ResValue.DataType.INT_HEX else ResValue.DataType.INT_DEC, + value.toInt()) } private data class UnitEntry( - val name: String, - val dataType: ResValue.DataType, - val unitValue: Int, - val scale: Float = 1.0f -) + val name: String, val dataType: ResValue.DataType, val unitValue: Int, val scale: Float = 1.0f) -private val unitSuffixes = - listOf( - UnitEntry("px", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_PX), - UnitEntry("dip", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_DIP), - UnitEntry("dp", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_DIP), - UnitEntry("sp", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_SP), - UnitEntry("pt", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_PT), - UnitEntry("in", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_IN), - UnitEntry("mm", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_MM), - UnitEntry("%", ResValue.DataType.FRACTION, ResValue.ComplexFormat.UNIT_FRACTION, 1.0f / 100), - UnitEntry( - "%p", - ResValue.DataType.FRACTION, - ResValue.ComplexFormat.UNIT_FRACTION_PARENT, - 1.0f / 100 - ) - ) +private val unitSuffixes = listOf( + UnitEntry("px", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_PX), + UnitEntry("dip", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_DIP), + UnitEntry("dp", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_DIP), + UnitEntry("sp", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_SP), + UnitEntry("pt", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_PT), + UnitEntry("in", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_IN), + UnitEntry("mm", ResValue.DataType.DIMENSION, ResValue.ComplexFormat.UNIT_MM), + UnitEntry("%", ResValue.DataType.FRACTION, ResValue.ComplexFormat.UNIT_FRACTION, 1.0f/100), + UnitEntry( + "%p", ResValue.DataType.FRACTION, ResValue.ComplexFormat.UNIT_FRACTION_PARENT, 1.0f/100)) -private fun parseUnitType(string: String): UnitEntry? { +private fun parseUnitType(string: String) : UnitEntry? { for (entry in unitSuffixes) { if (string.endsWith(entry.name)) { return entry @@ -605,7 +581,7 @@ fun stringToFloat(string: String): ResValue? { if (entry != null) trimmedString.lastIndexOf(entry.name) else trimmedString.length // no spaces allowed between suffix and floating point value - if (suffixIndex == 0 || Character.isWhitespace(trimmedString.codePointAt(suffixIndex - 1))) { + if (suffixIndex == 0 || Character.isWhitespace(trimmedString.codePointAt(suffixIndex-1))) { return null } @@ -615,7 +591,8 @@ fun stringToFloat(string: String): ResValue? { // i.e "0x1d.dp" should be considered: "0x1d.d p" not "0x1d. dp" if (parseHex(entry?.name?.codePointAt(0) ?: 0) != -1) { // try to parse the float value with the beginning of the suffix considered. - if (trimmedString.contains("0x", true) && !stringToParse.contains('p')) { + if (trimmedString.contains("0x", true) && + !stringToParse.contains('p')) { // ambiguous value string, resulting in a parse failure. return null } @@ -647,39 +624,37 @@ fun stringToFloat(string: String): ResValue? { // Transform the float to (rounded) integer with the lower 23 bits representing the fraction // and bits 45 to 23 representing the whole number part. - val bits = (parsedValue * (1 shl 23) + .5f).toLong() + val bits = (parsedValue*(1 shl 23) + .5f).toLong() - val (radix, shift) = - when { - bits and ((1L shl 23) - 1) == 0L -> - // Always use 23p0 if there is no fraction, as it is easier to read. - Pair(ResValue.ComplexFormat.RADIX_23p0, 23) - bits and ((1L shl 23) - 1).inv() == 0L -> - // Whole number part is zero -- can fit into 0 leading bits of precision. - Pair(ResValue.ComplexFormat.RADIX_0p23, 0) - bits and ((1L shl 31) - 1).inv() == 0L -> - // Magnitude can fit in 8 leading bits of precision. - Pair(ResValue.ComplexFormat.RADIX_8p15, 8) - bits and ((1L shl 39) - 1).inv() == 0L -> - // Magnitude can fit in 16 leading bits of precision. - Pair(ResValue.ComplexFormat.RADIX_16p7, 16) - else -> - // Need whole range, so no fractional part. - Pair(ResValue.ComplexFormat.RADIX_23p0, 23) - } + val (radix, shift) = when { + bits and ((1L shl 23) - 1) == 0L -> + // Always use 23p0 if there is no fraction, as it is easier to read. + Pair(ResValue.ComplexFormat.RADIX_23p0, 23) + bits and ((1L shl 23) - 1).inv() == 0L -> + // Whole number part is zero -- can fit into 0 leading bits of precision. + Pair(ResValue.ComplexFormat.RADIX_0p23, 0) + bits and ((1L shl 31) -1).inv() == 0L -> + // Magnitude can fit in 8 leading bits of precision. + Pair(ResValue.ComplexFormat.RADIX_8p15, 8) + bits and ((1L shl 39) -1).inv() == 0L -> + // Magnitude can fit in 16 leading bits of precision. + Pair(ResValue.ComplexFormat.RADIX_16p7, 16) + else -> + // Need whole range, so no fractional part. + Pair(ResValue.ComplexFormat.RADIX_23p0, 23) + } var mantissa = (bits ushr shift).toInt() and ResValue.ComplexFormat.MANTISSA_MASK if (negative) { mantissa = (-mantissa) and ResValue.ComplexFormat.MANTISSA_MASK } - val dataValue = - (entry.unitValue shl ResValue.ComplexFormat.UNIT_SHIFT) or - (radix shl ResValue.ComplexFormat.RADIX_SHIFT) or - (mantissa shl ResValue.ComplexFormat.MANTISSA_SHIFT) + val dataValue = (entry.unitValue shl ResValue.ComplexFormat.UNIT_SHIFT) or + (radix shl ResValue.ComplexFormat.RADIX_SHIFT) or + (mantissa shl ResValue.ComplexFormat.MANTISSA_SHIFT) return ResValue(entry.dataType, dataValue) } return ResValue(ResValue.DataType.FLOAT, parsedValue.toRawBits()) -} \ No newline at end of file +} diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Util.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Util.kt old mode 100755 new mode 100644 index 109a21014..02a08e239 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Util.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/android/Util.kt @@ -16,6 +16,7 @@ package com.android.aaptcompiler.android +import androidx.collection.mutableIntIntMapOf import java.nio.ByteOrder private val littleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/buffer/BigBuffer.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/buffer/BigBuffer.kt old mode 100755 new mode 100644 diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoDeserialize.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoDeserialize.kt old mode 100755 new mode 100644 index c4a04068e..dc7de2d48 --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoDeserialize.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoDeserialize.kt @@ -1,6 +1,6 @@ package com.android.aaptcompiler.proto -import androidx.collection.MutableIntObjectMap +import androidx.collection.mutableIntObjectMapOf import com.android.aapt.ConfigurationOuterClass import com.android.aapt.Resources import com.android.aaptcompiler.AllowNew @@ -45,7 +45,8 @@ import com.android.utils.ILogger import com.itsaky.androidide.layoutlib.resources.ResourceVisibility fun deserializeConfigFromPb( - config: ConfigurationOuterClass.Configuration, logger: ILogger?): ConfigDescription? { + config: ConfigurationOuterClass.Configuration, logger: ILogger? +): ConfigDescription? { val mcc = config.getMcc().toShort() val mnc = config.getMnc().toShort() @@ -62,8 +63,10 @@ fun deserializeConfigFromPb( val screenLayoutDir = when (config.getLayoutDirection()) { ConfigurationOuterClass.Configuration.LayoutDirection.LAYOUT_DIRECTION_LTR -> ResTableConfig.SCREEN_LAYOUT.DIR_LTR + ConfigurationOuterClass.Configuration.LayoutDirection.LAYOUT_DIRECTION_RTL -> ResTableConfig.SCREEN_LAYOUT.DIR_RTL + else -> ResTableConfig.SCREEN_LAYOUT.DIR_ANY } @@ -74,36 +77,46 @@ fun deserializeConfigFromPb( val screenLayoutSize = when (config.getScreenLayoutSize()) { ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_SMALL -> ResTableConfig.SCREEN_LAYOUT.SIZE_SMALL + ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_NORMAL -> ResTableConfig.SCREEN_LAYOUT.SIZE_NORMAL + ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_LARGE -> ResTableConfig.SCREEN_LAYOUT.SIZE_LARGE + ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_XLARGE -> ResTableConfig.SCREEN_LAYOUT.SIZE_XLARGE + else -> ResTableConfig.SCREEN_LAYOUT.SIZE_ANY } val screenLayoutLong = when (config.getScreenLayoutLong()) { ConfigurationOuterClass.Configuration.ScreenLayoutLong.SCREEN_LAYOUT_LONG_LONG -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_YES + ConfigurationOuterClass.Configuration.ScreenLayoutLong.SCREEN_LAYOUT_LONG_NOTLONG -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_NO + else -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_ANY } val screenRound = when (config.getScreenRound()) { ConfigurationOuterClass.Configuration.ScreenRound.SCREEN_ROUND_ROUND -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_YES + ConfigurationOuterClass.Configuration.ScreenRound.SCREEN_ROUND_NOTROUND -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_NO + else -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_ANY } val wideGamutColor = when (config.getWideColorGamut()) { ConfigurationOuterClass.Configuration.WideColorGamut.WIDE_COLOR_GAMUT_WIDECG -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_YES + ConfigurationOuterClass.Configuration.WideColorGamut.WIDE_COLOR_GAMUT_NOWIDECG -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_NO + else -> ResTableConfig.COLOR_MODE.WIDE_GAMUT_ANY } @@ -116,36 +129,48 @@ fun deserializeConfigFromPb( val orientation = when (config.getOrientation()) { ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_PORT -> ResTableConfig.ORIENTATION.PORT + ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_LAND -> ResTableConfig.ORIENTATION.LAND + ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_SQUARE -> ResTableConfig.ORIENTATION.SQUARE + else -> ResTableConfig.ORIENTATION.ANY } val uiModeType = when (config.getUiModeType()) { ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_NORMAL -> ResTableConfig.UI_MODE.TYPE_NORMAL + ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_DESK -> ResTableConfig.UI_MODE.TYPE_DESK + ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_CAR -> ResTableConfig.UI_MODE.TYPE_CAR + ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_TELEVISION -> ResTableConfig.UI_MODE.TYPE_TELEVISION + ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_APPLIANCE -> ResTableConfig.UI_MODE.TYPE_APPLIANCE + ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_WATCH -> ResTableConfig.UI_MODE.TYPE_WATCH + ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_VRHEADSET -> ResTableConfig.UI_MODE.TYPE_VR_HEADSET + else -> ResTableConfig.UI_MODE.TYPE_ANY } val uiModeNight = when (config.getUiModeNight()) { ConfigurationOuterClass.Configuration.UiModeNight.UI_MODE_NIGHT_NIGHT -> ResTableConfig.UI_MODE.NIGHT_YES + ConfigurationOuterClass.Configuration.UiModeNight.UI_MODE_NIGHT_NOTNIGHT -> ResTableConfig.UI_MODE.NIGHT_NO + else -> ResTableConfig.UI_MODE.NIGHT_ANY } @@ -154,53 +179,81 @@ fun deserializeConfigFromPb( val touchscreen = when (config.getTouchscreen()) { ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_NOTOUCH -> ResTableConfig.TOUCHSCREEN.NOTOUCH + ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_STYLUS -> ResTableConfig.TOUCHSCREEN.STYLUS + ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_FINGER -> ResTableConfig.TOUCHSCREEN.FINGER + else -> ResTableConfig.TOUCHSCREEN.ANY } val keysHidden = when (config.getKeysHidden()) { ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_KEYSEXPOSED -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_NO + ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_KEYSHIDDEN -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_YES + ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_KEYSSOFT -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_SOFT + else -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_ANY } val keyboard = when (config.getKeyboard()) { ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_NOKEYS -> ResTableConfig.KEYBOARD.NOKEYS + ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_QWERTY -> ResTableConfig.KEYBOARD.QWERTY + ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_TWELVEKEY -> ResTableConfig.KEYBOARD.TWELVEKEY + else -> ResTableConfig.KEYBOARD.ANY } val navHidden = when (config.getNavHidden()) { ConfigurationOuterClass.Configuration.NavHidden.NAV_HIDDEN_NAVHIDDEN -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_YES + ConfigurationOuterClass.Configuration.NavHidden.NAV_HIDDEN_NAVEXPOSED -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_NO + else -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_ANY } val navigation = when (config.getNavigation()) { ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_NONAV -> ResTableConfig.NAVIGATION.NONAV + ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_DPAD -> ResTableConfig.NAVIGATION.DPAD + ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_TRACKBALL -> ResTableConfig.NAVIGATION.TRACKBALL + ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_WHEEL -> ResTableConfig.NAVIGATION.WHEEL + else -> ResTableConfig.NAVIGATION.ANY } + val grammaticalGender = when (config.grammaticalGender) { + ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_NEUTER -> + ResTableConfig.GRAMMATICAL_GENDER.NEUTER + + ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_FEMININE -> + ResTableConfig.GRAMMATICAL_GENDER.FEMININE + + ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_MASCULINE -> + ResTableConfig.GRAMMATICAL_GENDER.MASCULINE + + else -> ResTableConfig.GRAMMATICAL_GENDER.ANY + } + val screenWidth = config.getScreenWidth() val screenHeight = config.getScreenHeight() val sdkVersion = config.getSdkVersion().toShort() @@ -214,6 +267,7 @@ fun deserializeConfigFromPb( density = density, keyboard = keyboard, navigation = navigation, + grammaticalInflection = grammaticalGender, inputFlags = (keysHidden.toInt() or navHidden.toInt()).toByte(), screenWidth = screenWidth, screenHeight = screenHeight, @@ -226,7 +280,9 @@ fun deserializeConfigFromPb( screenWidthDp = screenWidthDp, screenHeightDp = screenHeightDp, screenLayout2 = screenRound, - colorMode = (wideGamutColor.toInt() or hdr.toInt()).toByte())) + colorMode = (wideGamutColor.toInt() or hdr.toInt()).toByte() + ) + ) localeValue.writeTo(configDescription) return configDescription @@ -250,7 +306,7 @@ fun deserializeSourceFromPb(source: Resources.Source, sourcePool: ResStringPool) return Source(path, line) } -fun deserializeVisibilityFromPb(level: Resources.Visibility.Level) = when(level) { +fun deserializeVisibilityFromPb(level: Resources.Visibility.Level) = when (level) { Resources.Visibility.Level.PRIVATE -> ResourceVisibility.PRIVATE Resources.Visibility.Level.PUBLIC -> ResourceVisibility.PUBLIC else -> ResourceVisibility.UNDEFINED @@ -260,7 +316,8 @@ fun deserializeOverlayableFromPb( item: Resources.OverlayableItem, overlayable: Overlayable, sourcePool: ResStringPool, - logger: ILogger?): OverlayableItem? { + logger: ILogger? +): OverlayableItem? { var policies = 0 for (policy in item.getPolicyList()) { @@ -289,45 +346,60 @@ fun deserializeOverlayableFromPb( } fun deserializeBinPrimitiveFromPb( - primitive: Resources.Primitive, logger: ILogger?): BinaryPrimitive? { + primitive: Resources.Primitive, logger: ILogger? +): BinaryPrimitive? { - val (type, deviceValue) = when(primitive.getOneofValueCase()) { + val (type, deviceValue) = when (primitive.getOneofValueCase()) { Resources.Primitive.OneofValueCase.NULL_VALUE -> Pair(ResValue.DataType.NULL, ResValue.NullFormat.UNDEFINED) + Resources.Primitive.OneofValueCase.EMPTY_VALUE -> Pair(ResValue.DataType.NULL, ResValue.NullFormat.EMPTY) + Resources.Primitive.OneofValueCase.FLOAT_VALUE -> { val float = primitive.getFloatValue() Pair(ResValue.DataType.FLOAT, float.toRawBits()) } + Resources.Primitive.OneofValueCase.DIMENSION_VALUE -> Pair(ResValue.DataType.DIMENSION, primitive.getDimensionValue()) + Resources.Primitive.OneofValueCase.FRACTION_VALUE -> Pair(ResValue.DataType.FRACTION, primitive.getFractionValue()) + Resources.Primitive.OneofValueCase.INT_DECIMAL_VALUE -> Pair(ResValue.DataType.INT_DEC, primitive.getIntDecimalValue()) + Resources.Primitive.OneofValueCase.INT_HEXADECIMAL_VALUE -> Pair(ResValue.DataType.INT_HEX, primitive.getIntHexadecimalValue()) + Resources.Primitive.OneofValueCase.BOOLEAN_VALUE -> { val boolValue = if (primitive.getBooleanValue()) 0xffffffff.toInt() else 0 Pair(ResValue.DataType.INT_BOOLEAN, boolValue) } + Resources.Primitive.OneofValueCase.COLOR_ARGB8_VALUE -> Pair(ResValue.DataType.INT_COLOR_ARGB8, primitive.getColorArgb8Value()) + Resources.Primitive.OneofValueCase.COLOR_RGB8_VALUE -> Pair(ResValue.DataType.INT_COLOR_RGB8, primitive.getColorRgb8Value()) + Resources.Primitive.OneofValueCase.COLOR_ARGB4_VALUE -> Pair(ResValue.DataType.INT_COLOR_ARGB4, primitive.getColorArgb4Value()) + Resources.Primitive.OneofValueCase.COLOR_RGB4_VALUE -> Pair(ResValue.DataType.INT_COLOR_RGB4, primitive.getColorRgb4Value()) + Resources.Primitive.OneofValueCase.DIMENSION_VALUE_DEPRECATED -> { val float = primitive.getDimensionValueDeprecated() Pair(ResValue.DataType.DIMENSION, float.toRawBits()) } + Resources.Primitive.OneofValueCase.FRACTION_VALUE_DEPRECATED -> { val float = primitive.getFractionValueDeprecated() Pair(ResValue.DataType.FRACTION, float.toRawBits()) } + else -> { val errorMsg = "Value case unrecognized for Primitive proto: %s" logger?.error(null, errorMsg, primitive.oneofValueCase) @@ -344,7 +416,8 @@ fun deserializeRawFromPb(string: Resources.RawString, valuePool: StringPool) = RawString(valuePool.makeRef(string.getValue())) fun deserializeStyledStrFromPb( - string: Resources.StyledString, valuePool: StringPool): StyledString { + string: Resources.StyledString, valuePool: StringPool +): StyledString { val spans = mutableListOf() for (span in string.getSpanList()) { @@ -376,19 +449,23 @@ fun deserializeReferenceFromPb(ref: Resources.Reference, logger: ILogger?): Refe } fun deserializeFileRefFromPb( - file: Resources.FileReference, valuePool: StringPool, config: ConfigDescription): FileReference { + file: Resources.FileReference, valuePool: StringPool, config: ConfigDescription +): FileReference { val fileRef = FileReference( valuePool.makeRef( file.getPath(), - StringPool.Context(StringPool.Context.Priority.HIGH.priority, config))) + StringPool.Context(StringPool.Context.Priority.HIGH.priority, config) + ) + ) fileRef.type = deserializeFileTypeFromPb(file.getType()) return fileRef } fun deserializeAttrFromPb( - attr: Resources.Attribute, sourcePool: ResStringPool, logger: ILogger?): AttributeResource? { + attr: Resources.Attribute, sourcePool: ResStringPool, logger: ILogger? +): AttributeResource? { val attrResource = AttributeResource(attr.getFormatFlags()) attrResource.minInt = attr.getMinInt() @@ -404,7 +481,8 @@ fun deserializeAttrFromPb( reference.comment = symbol.getComment() attrResource.symbols.add( - AttributeResource.Symbol(reference, symbol.getValue(), symbol.getType().toByte())) + AttributeResource.Symbol(reference, symbol.getValue(), symbol.getType().toByte()) + ) } return attrResource @@ -415,7 +493,8 @@ fun deserializeStyleFromPb( valuePool: StringPool, config: ConfigDescription, sourcePool: ResStringPool, - logger: ILogger?): Style? { + logger: ILogger? +): Style? { val styleResource = Style() if (style.hasParent()) { @@ -449,12 +528,13 @@ fun deserializeStyleFromPb( } fun deserializeStyleableFromPb( - styleable: Resources.Styleable, sourcePool: ResStringPool, logger: ILogger?): Styleable? { + styleable: Resources.Styleable, sourcePool: ResStringPool, logger: ILogger? +): Styleable? { val styleableResource = Styleable() for (entry in styleable.getEntryList()) { val reference = deserializeReferenceFromPb(entry.getAttr(), logger) - reference?: return null + reference ?: return null if (entry.hasSource()) { reference.source = deserializeSourceFromPb(entry.getSource(), sourcePool) } @@ -469,7 +549,8 @@ fun deserializeArrayFromPb( valuePool: StringPool, config: ConfigDescription, sourcePool: ResStringPool, - logger: ILogger?): ArrayResource? { + logger: ILogger? +): ArrayResource? { val arrayResource = ArrayResource() for (entry in array.getElementList()) { @@ -499,7 +580,8 @@ fun deserializePluralFromPb( valuePool: StringPool, config: ConfigDescription, sourcePool: ResStringPool, - logger: ILogger?): Plural? { + logger: ILogger? +): Plural? { val pluralResource = Plural() for (entry in plural.getEntryList()) { @@ -526,32 +608,34 @@ fun deserializeMacroFromPb( valuePool: StringPool, config: ConfigDescription, sourcePool: ResStringPool, - logger: ILogger?): Macro { + logger: ILogger? +): Macro { val macro = Macro() macro.rawValue = pbMacro.rawString if (pbMacro.hasStyleString()) { - val spans = ArrayList() - for (span in pbMacro.styleString.spansList) { - spans.add(Span(span.name, span.startIndex, span.endIndex)) - } - macro.styleString = StyleString(pbMacro.styleString.str, spans) + val spans = ArrayList() + for (span in pbMacro.styleString.spansList) { + spans.add(Span(span.name, span.startIndex, span.endIndex)) + } + macro.styleString = StyleString(pbMacro.styleString.str, spans) } val untranslatableList = ArrayList() for (untranslatable in pbMacro.untranslatableSectionsList) { - untranslatableList.add( - UntranslatableSection( - untranslatable.startIndex.toInt(), untranslatable.endIndex.toInt()) + untranslatableList.add( + UntranslatableSection( + untranslatable.startIndex.toInt(), untranslatable.endIndex.toInt() ) + ) } macro.untranslatables = untranslatableList val aliasNamespaces = ArrayList() for (namespace in pbMacro.namespaceStackList) { aliasNamespaces.add( - Macro.Namespace(namespace.prefix, namespace.packageName, namespace.isPrivate) + Macro.Namespace(namespace.prefix, namespace.packageName, namespace.isPrivate) ) } macro.aliasNamespaces = aliasNamespaces @@ -563,13 +647,15 @@ fun deserializeItemFromPb( item: Resources.Item, valuePool: StringPool, config: ConfigDescription, - logger: ILogger?): Item? = when (item.getValueCase()) { + logger: ILogger? +): Item? = when (item.getValueCase()) { Resources.Item.ValueCase.REF -> deserializeReferenceFromPb(item.getRef(), logger) Resources.Item.ValueCase.STR -> deserializeStringFromPb(item.getStr(), valuePool) Resources.Item.ValueCase.RAW_STR -> deserializeRawFromPb(item.getRawStr(), valuePool) Resources.Item.ValueCase.STYLED_STR -> deserializeStyledStrFromPb(item.getStyledStr(), valuePool) + Resources.Item.ValueCase.FILE -> deserializeFileRefFromPb(item.getFile(), valuePool, config) Resources.Item.ValueCase.ID -> Id() Resources.Item.ValueCase.PRIM -> deserializeBinPrimitiveFromPb(item.getPrim(), logger) @@ -585,7 +671,8 @@ fun deserializeValueFromPb( valuePool: StringPool, config: ConfigDescription, sourcePool: ResStringPool, - logger: ILogger?): Value? { + logger: ILogger? +): Value? { val valueResource = when { value.hasItem() -> deserializeItemFromPb(value.getItem(), valuePool, config, logger) @@ -595,16 +682,22 @@ fun deserializeValueFromPb( when (compoundValue.getValueCase()) { Resources.CompoundValue.ValueCase.ATTR -> deserializeAttrFromPb(compoundValue.getAttr(), sourcePool, logger) + Resources.CompoundValue.ValueCase.STYLE -> deserializeStyleFromPb(compoundValue.getStyle(), valuePool, config, sourcePool, logger) + Resources.CompoundValue.ValueCase.STYLEABLE -> deserializeStyleableFromPb(compoundValue.getStyleable(), sourcePool, logger) + Resources.CompoundValue.ValueCase.ARRAY -> deserializeArrayFromPb(compoundValue.getArray(), valuePool, config, sourcePool, logger) + Resources.CompoundValue.ValueCase.PLURAL -> deserializePluralFromPb(compoundValue.getPlural(), valuePool, config, sourcePool, logger) + Resources.CompoundValue.ValueCase.MACRO -> deserializeMacroFromPb(compoundValue.getMacro(), valuePool, config, sourcePool, logger) + else -> { val errorMsg = "Unrecognized compoundValue value case %s" logger?.error(null, errorMsg, compoundValue.valueCase) @@ -612,6 +705,7 @@ fun deserializeValueFromPb( } } } + else -> { val errorMsg = "Unrecognized case for serialized value %s" logger?.error(null, errorMsg, value.valueCase) @@ -634,12 +728,13 @@ fun deserializePackageFromPb( sourcePool: ResStringPool, overlayables: List, logger: ILogger?, - table: ResourceTable): Boolean { + table: ResourceTable +): Boolean { val packageId = if (original.hasPackageId()) original.getPackageId().getId().toByte() else 0.toByte() - val idIndex = MutableIntObjectMap() + val idIndex = mutableIntObjectMapOf() val resourcePackage = table.createPackageAllowingDuplicateNames(original.getPackageName(), packageId) @@ -672,11 +767,12 @@ fun deserializePackageFromPb( // Propagate the visibility up to the type group. ResourceVisibility.PUBLIC -> resourceGroup.visibility = ResourceVisibility.PUBLIC // Propagate private, only if previously undefined. - ResourceVisibility.PRIVATE-> { + ResourceVisibility.PRIVATE -> { if (resourceGroup.visibility == ResourceVisibility.UNDEFINED) { resourceGroup.visibility = ResourceVisibility.PRIVATE } } + else -> {} } @@ -706,7 +802,7 @@ fun deserializePackageFromPb( val overlayableItem = entry.getOverlayableItem() if (overlayableItem.getOverlayableIdx() >= overlayables.size) { val errorMsg = "Illegal value for overlayable id '%s'. Because only '%s' overlayables" + - "exist in proto." + "exist in proto." logger?.error(null, errorMsg, overlayableItem.overlayableIdx, overlayables.size) return false } @@ -715,7 +811,8 @@ fun deserializePackageFromPb( overlayableItem, overlayables[overlayableItem.getOverlayableIdx()], sourcePool, - logger) + logger + ) resourceOverlayable ?: return false resourceEntry.overlayable = resourceOverlayable @@ -724,9 +821,11 @@ fun deserializePackageFromPb( val resourceId = resourceIdFromParts( original.getPackageId().getId().toByte(), group.getTypeId().getId().toByte(), - entry.getEntryId().getId().toShort()) + entry.getEntryId().getId().toShort() + ) if (resourceId.isValidId()) { - idIndex.put(resourceId, ResourceName(resourcePackage.name, resourceGroup.type, resourceEntry.name)) + idIndex[resourceId] = + ResourceName(resourcePackage.name, resourceGroup.type, resourceEntry.name) } for (configValue in entry.getConfigValueList()) { @@ -744,13 +843,15 @@ fun deserializePackageFromPb( errorMsg, ResourceName(resourcePackage.name, resourceGroup.type, resourceEntry.name), resourceConfig, - config.product) + config.product + ) return false } resourceConfigValue.value = deserializeValueFromPb( - configValue.getValue(), table.stringPool, resourceConfig, sourcePool, logger) + configValue.getValue(), table.stringPool, resourceConfig, sourcePool, logger + ) resourceConfigValue.value ?: return false } @@ -780,11 +881,13 @@ fun deserializePackageFromPb( } fun deserializeTableFromPb( - table: Resources.ResourceTable, outTable: ResourceTable, logger: ILogger?): Boolean { + table: Resources.ResourceTable, outTable: ResourceTable, logger: ILogger? +): Boolean { val sourcePool = ResStringPool.get( table.getSourcePool().getData().asReadOnlyByteBuffer(), - table.getSourcePool().getSerializedSize()) + table.getSourcePool().getSerializedSize() + ) // Deserialize the overlayable groups of a table val overlayables = mutableListOf() diff --git a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoSerialize.kt b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoSerialize.kt old mode 100755 new mode 100644 index af8aeb10c..d4b5f19dd --- a/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoSerialize.kt +++ b/subprojects/aaptcompiler/src/main/java/com/android/aaptcompiler/proto/ProtoSerialize.kt @@ -1,6 +1,7 @@ package com.android.aaptcompiler.proto import android.aapt.pb.internal.ResourcesInternal +import androidx.collection.intIntMapOf import com.android.aapt.ConfigurationOuterClass import com.android.aapt.Resources import com.android.aaptcompiler.ArrayResource @@ -17,6 +18,7 @@ import com.android.aaptcompiler.OverlayableItem import com.android.aaptcompiler.Plural import com.android.aaptcompiler.RawString import com.android.aaptcompiler.Reference +import com.android.aaptcompiler.ResourceCompilationException import com.android.aaptcompiler.ResourceFile import com.android.aaptcompiler.ResourceName import com.android.aaptcompiler.ResourceTable @@ -31,13 +33,17 @@ import com.android.aaptcompiler.android.ResTableConfig import com.android.aaptcompiler.android.ResValue import com.android.aaptcompiler.blameSource import com.android.aaptcompiler.buffer.BigBuffer -import com.itsaky.androidide.layoutlib.resources.ResourceVisibility import com.android.utils.ILogger import com.google.protobuf.ByteString +import com.itsaky.androidide.layoutlib.resources.ResourceVisibility internal fun serializePoolToPb(pool: StringPool, logger: ILogger?): Resources.StringPool { val buffer = BigBuffer(1024) - pool.flattenUtf8(buffer, logger) + try { + pool.flattenUtf8(buffer, logger) + } catch (e: Exception) { + throw ResourceCompilationException("Failed to flatten utf-8 string.", e) + } val builder = Resources.StringPool.newBuilder() builder.setData(ByteString.copyFrom(buffer.toBytes())) @@ -53,7 +59,8 @@ internal fun serializeSourceToPb(source: Source, sourcePool: StringPool): Resour sourceBuilder.setPathIdx(ref.index()) if (source.line != null) { sourceBuilder.setPosition( - Resources.SourcePosition.newBuilder().setLineNumber(source.line).build()) + Resources.SourcePosition.newBuilder().setLineNumber(source.line).build() + ) } return sourceBuilder.build() } @@ -63,10 +70,12 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources val sourcePool = StringPool() - tableBuilder.addToolFingerprint(Resources.ToolFingerprint.newBuilder() - .setTool(ToolFingerprint.TOOL_NAME) - .setVersion(ToolFingerprint.FINGERPRINT) - .build()) + tableBuilder.addToolFingerprint( + Resources.ToolFingerprint.newBuilder() + .setTool(ToolFingerprint.TOOL_NAME) + .setVersion(ToolFingerprint.FINGERPRINT) + .build() + ) val overlayables = mutableListOf() for (resourcePackage in table.packages) { val packageBuilder = Resources.Package.newBuilder() @@ -76,7 +85,8 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources packageBuilder.setPackageId( Resources.PackageId.newBuilder() .setId(packageId.toInt()) - .build()) + .build() + ) } packageBuilder.setPackageName(resourcePackage.name) @@ -88,7 +98,8 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources groupBuilder.setTypeId( Resources.TypeId.newBuilder() .setId(groupId.toInt()) - .build()) + .build() + ) } groupBuilder.setName(resourceGroup.type.tagName) @@ -102,7 +113,8 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources entryBuilder.setEntryId( Resources.EntryId.newBuilder() .setId(entryId.toInt()) - .build()) + .build() + ) } entryBuilder.setName(entry.name) @@ -114,7 +126,8 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources visibilityBuilder .setLevel(serializeVisibilityToPb(entry.visibility.level)) .setComment(entry.visibility.comment) - .build()) + .build() + ) val entryAllowNew = entry.allowNew if (entryAllowNew != null) { @@ -125,7 +138,8 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources entryBuilder.setAllowNew( allowNewBuilder .setComment(entryAllowNew.comment) - .build()) + .build() + ) } val entryOverlayableItem = entry.overlayable @@ -136,7 +150,9 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources entryOverlayableItem, overlayables, tableBuilder, - sourcePool)) + sourcePool + ) + ) } for (configValue in entry.values) { @@ -159,9 +175,10 @@ fun serializeTableToPb(table: ResourceTable, logger: ILogger? = null): Resources } fun serializeVisibilityToPb(resourceVisibility: ResourceVisibility) = - when(resourceVisibility) { + when (resourceVisibility) { ResourceVisibility.PRIVATE, - ResourceVisibility.PRIVATE_XML_ONLY -> Resources.Visibility.Level.PRIVATE + ResourceVisibility.PRIVATE_XML_ONLY -> Resources.Visibility.Level.PRIVATE + ResourceVisibility.PUBLIC -> Resources.Visibility.Level.PUBLIC else -> Resources.Visibility.Level.UNKNOWN } @@ -179,7 +196,9 @@ fun serializeCompiledFileToPb(file: ResourceFile): ResourcesInternal.CompiledFil .setResourceName(exportedResourceName.name.toString()) .setSource( Resources.SourcePosition.newBuilder() - .setLineNumber(exportedResourceName.line))) + .setLineNumber(exportedResourceName.line) + ) + ) } return compiledFile.build() } @@ -187,7 +206,8 @@ fun serializeCompiledFileToPb(file: ResourceFile): ResourcesInternal.CompiledFil fun serializeConfigToPb( config: ConfigDescription, product: String?, - logger: ILogger?): ConfigurationOuterClass.Configuration { + logger: ILogger? +): ConfigurationOuterClass.Configuration { val configBuilder = ConfigurationOuterClass.Configuration.newBuilder() configBuilder.setMcc(config.mcc.toInt() and 0xffff) @@ -198,10 +218,13 @@ fun serializeConfigToPb( when ((config.screenLayout.toInt() and ResTableConfig.SCREEN_LAYOUT.DIR_MASK).toByte()) { ResTableConfig.SCREEN_LAYOUT.DIR_LTR -> ConfigurationOuterClass.Configuration.LayoutDirection.LAYOUT_DIRECTION_LTR + ResTableConfig.SCREEN_LAYOUT.DIR_RTL -> ConfigurationOuterClass.Configuration.LayoutDirection.LAYOUT_DIRECTION_RTL + else -> ConfigurationOuterClass.Configuration.LayoutDirection.LAYOUT_DIRECTION_UNSET - }) + } + ) configBuilder.setScreenWidth(config.screenWidth) configBuilder.setScreenHeight(config.screenHeight) @@ -213,14 +236,19 @@ fun serializeConfigToPb( when ((config.screenLayout.toInt() and ResTableConfig.SCREEN_LAYOUT.SIZE_MASK).toByte()) { ResTableConfig.SCREEN_LAYOUT.SIZE_SMALL -> ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_SMALL + ResTableConfig.SCREEN_LAYOUT.SIZE_NORMAL -> ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_NORMAL + ResTableConfig.SCREEN_LAYOUT.SIZE_LARGE -> ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_LARGE + ResTableConfig.SCREEN_LAYOUT.SIZE_XLARGE -> ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_XLARGE + else -> ConfigurationOuterClass.Configuration.ScreenLayoutSize.SCREEN_LAYOUT_SIZE_UNSET - }) + } + ) configBuilder.setScreenLayoutLong( when ( @@ -228,10 +256,13 @@ fun serializeConfigToPb( ResTableConfig.SCREEN_LAYOUT.SCREENLONG_YES -> ConfigurationOuterClass.Configuration.ScreenLayoutLong.SCREEN_LAYOUT_LONG_LONG + ResTableConfig.SCREEN_LAYOUT.SCREENLONG_NO -> ConfigurationOuterClass.Configuration.ScreenLayoutLong.SCREEN_LAYOUT_LONG_NOTLONG + else -> ConfigurationOuterClass.Configuration.ScreenLayoutLong.SCREEN_LAYOUT_LONG_UNSET - }) + } + ) configBuilder.setScreenRound( when ( @@ -239,65 +270,87 @@ fun serializeConfigToPb( ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_YES -> ConfigurationOuterClass.Configuration.ScreenRound.SCREEN_ROUND_ROUND + ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_NO -> ConfigurationOuterClass.Configuration.ScreenRound.SCREEN_ROUND_NOTROUND + else -> ConfigurationOuterClass.Configuration.ScreenRound.SCREEN_ROUND_UNSET - }) + } + ) configBuilder.setWideColorGamut( when ((config.colorMode.toInt() and ResTableConfig.COLOR_MODE.WIDE_GAMUT_MASK).toByte()) { ResTableConfig.COLOR_MODE.WIDE_GAMUT_YES -> ConfigurationOuterClass.Configuration.WideColorGamut.WIDE_COLOR_GAMUT_WIDECG + ResTableConfig.COLOR_MODE.WIDE_GAMUT_NO -> ConfigurationOuterClass.Configuration.WideColorGamut.WIDE_COLOR_GAMUT_NOWIDECG + else -> ConfigurationOuterClass.Configuration.WideColorGamut.WIDE_COLOR_GAMUT_UNSET - }) + } + ) configBuilder.setHdr( when ((config.colorMode.toInt() and ResTableConfig.COLOR_MODE.HDR_MASK).toByte()) { ResTableConfig.COLOR_MODE.HDR_YES -> ConfigurationOuterClass.Configuration.Hdr.HDR_HIGHDR ResTableConfig.COLOR_MODE.HDR_NO -> ConfigurationOuterClass.Configuration.Hdr.HDR_LOWDR else -> ConfigurationOuterClass.Configuration.Hdr.HDR_UNSET - }) + } + ) configBuilder.setOrientation( when (config.orientation) { ResTableConfig.ORIENTATION.PORT -> ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_PORT + ResTableConfig.ORIENTATION.LAND -> ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_LAND + ResTableConfig.ORIENTATION.SQUARE -> ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_SQUARE + else -> ConfigurationOuterClass.Configuration.Orientation.ORIENTATION_UNSET - }) + } + ) configBuilder.setUiModeType( when ((config.uiMode.toInt() and ResTableConfig.UI_MODE.TYPE_MASK).toByte()) { ResTableConfig.UI_MODE.TYPE_NORMAL -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_NORMAL + ResTableConfig.UI_MODE.TYPE_DESK -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_DESK + ResTableConfig.UI_MODE.TYPE_CAR -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_CAR + ResTableConfig.UI_MODE.TYPE_TELEVISION -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_TELEVISION + ResTableConfig.UI_MODE.TYPE_APPLIANCE -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_APPLIANCE + ResTableConfig.UI_MODE.TYPE_WATCH -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_WATCH + ResTableConfig.UI_MODE.TYPE_VR_HEADSET -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_VRHEADSET + else -> ConfigurationOuterClass.Configuration.UiModeType.UI_MODE_TYPE_UNSET - }) + } + ) configBuilder.setUiModeNight( when ((config.uiMode.toInt() and ResTableConfig.UI_MODE.NIGHT_MASK).toByte()) { ResTableConfig.UI_MODE.NIGHT_YES -> ConfigurationOuterClass.Configuration.UiModeNight.UI_MODE_NIGHT_NIGHT + ResTableConfig.UI_MODE.NIGHT_NO -> ConfigurationOuterClass.Configuration.UiModeNight.UI_MODE_NIGHT_NOTNIGHT + else -> ConfigurationOuterClass.Configuration.UiModeNight.UI_MODE_NIGHT_UNSET - }) + } + ) configBuilder.setDensity(config.density) @@ -305,56 +358,91 @@ fun serializeConfigToPb( when (config.touchscreen) { ResTableConfig.TOUCHSCREEN.NOTOUCH -> ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_NOTOUCH + ResTableConfig.TOUCHSCREEN.STYLUS -> ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_STYLUS + ResTableConfig.TOUCHSCREEN.FINGER -> ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_FINGER + else -> ConfigurationOuterClass.Configuration.Touchscreen.TOUCHSCREEN_UNSET - }) + } + ) configBuilder.setKeysHidden( when ((config.inputFlags.toInt() and ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_MASK).toByte()) { ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_NO -> ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_KEYSEXPOSED + ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_YES -> ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_KEYSHIDDEN + ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_SOFT -> ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_KEYSSOFT + else -> ConfigurationOuterClass.Configuration.KeysHidden.KEYS_HIDDEN_UNSET - }) + } + ) configBuilder.setKeyboard( when (config.keyboard) { ResTableConfig.KEYBOARD.NOKEYS -> ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_NOKEYS + ResTableConfig.KEYBOARD.QWERTY -> ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_QWERTY + ResTableConfig.KEYBOARD.TWELVEKEY -> ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_TWELVEKEY + else -> ConfigurationOuterClass.Configuration.Keyboard.KEYBOARD_UNSET - }) + } + ) configBuilder.setNavHidden( when ((config.inputFlags.toInt() and ResTableConfig.INPUT_FLAGS.NAVHIDDEN_MASK).toByte()) { ResTableConfig.INPUT_FLAGS.NAVHIDDEN_YES -> ConfigurationOuterClass.Configuration.NavHidden.NAV_HIDDEN_NAVHIDDEN + ResTableConfig.INPUT_FLAGS.NAVHIDDEN_NO -> ConfigurationOuterClass.Configuration.NavHidden.NAV_HIDDEN_NAVEXPOSED + else -> ConfigurationOuterClass.Configuration.NavHidden.NAV_HIDDEN_UNSET - }) + } + ) configBuilder.setNavigation( when (config.navigation) { ResTableConfig.NAVIGATION.NONAV -> ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_NONAV + ResTableConfig.NAVIGATION.DPAD -> ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_DPAD + ResTableConfig.NAVIGATION.TRACKBALL -> ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_TRACKBALL + ResTableConfig.NAVIGATION.WHEEL -> ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_WHEEL + else -> ConfigurationOuterClass.Configuration.Navigation.NAVIGATION_UNSET - }) + } + ) + + configBuilder.setGrammaticalGender( + when (config.grammaticalInflection) { + ResTableConfig.GRAMMATICAL_GENDER.NEUTER -> + ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_NEUTER + + ResTableConfig.GRAMMATICAL_GENDER.FEMININE -> + ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_FEMININE + + ResTableConfig.GRAMMATICAL_GENDER.MASCULINE -> + ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_MASCULINE + + else -> ConfigurationOuterClass.Configuration.GrammaticalGender.GRAM_GENDER_USET + } + ) configBuilder.setSdkVersion(config.sdkVersion.toInt() and 0xffff) @@ -369,7 +457,8 @@ fun serializeOverlayableToPb( item: OverlayableItem, overlayables: MutableList, table: Resources.ResourceTable.Builder, - sourcePool: StringPool): Resources.OverlayableItem { + sourcePool: StringPool +): Resources.OverlayableItem { // Retrieve the index of the overlayable in the list of groups that have already been serialized. val foundIndex = overlayables.indexOf(item.overlayable) @@ -427,6 +516,7 @@ fun serializeOverlayableToPb( fun serializeReferenceTypeToPb(type: Reference.Type) = when (type) { Reference.Type.RESOURCE -> Resources.Reference.Type.REFERENCE + Reference.Type.ATTRIBUTE -> Resources.Reference.Type.ATTRIBUTE } @@ -495,7 +585,8 @@ fun serializeBinPrimitiveToPb(primitive: BinaryPrimitive, logger: ILogger?): Res if (resVal == null) { logger?.error( - null, "%s, Failed to serialize primitive %s.", blameSource(primitive.source), primitive) + null, "%s, Failed to serialize primitive %s.", blameSource(primitive.source), primitive + ) return primitiveBuilder.build() } @@ -504,14 +595,17 @@ fun serializeBinPrimitiveToPb(primitive: BinaryPrimitive, logger: ILogger?): Res when (resVal.data) { ResValue.NullFormat.UNDEFINED -> primitiveBuilder.setNullValue(Resources.Primitive.NullType.newBuilder().build()) + ResValue.NullFormat.EMPTY -> primitiveBuilder.setEmptyValue(Resources.Primitive.EmptyType.newBuilder().build()) + else -> { val errorMsg = "%s, Invalid null format value '%s' for primitive %s." logger?.error(null, errorMsg, blameSource(primitive.source), resVal.data, primitive) } } } + ResValue.DataType.FLOAT -> primitiveBuilder.setFloatValue(Float.fromBits(resVal.data)) ResValue.DataType.DIMENSION -> primitiveBuilder.setDimensionValue(resVal.data) ResValue.DataType.FRACTION -> primitiveBuilder.setFractionValue(resVal.data) @@ -548,7 +642,8 @@ fun serializeAttrToPb(attribute: AttributeResource, sourcePool: StringPool): Res .setName(serializeReferenceToPb(symbol.symbol)) .setValue(symbol.value) .setType(symbol.type.toInt()) - .build()) + .build() + ) } return attrBuilder.build() @@ -575,7 +670,8 @@ fun serializeStyleToPb(style: Style, sourcePool: StringPool, logger: ILogger?): .setKey(serializeReferenceToPb(entry.key)) .setComment(entry.key.comment) .setItem(serializeItemToPb(entry.value!!, logger)) - .build()) + .build() + ) } return styleBuilder.build() } @@ -592,13 +688,15 @@ fun serializeStyleableToPb(styleable: Styleable, sourcePool: StringPool): Resour entryBuilder .setComment(entry.comment) .setAttr(serializeReferenceToPb(entry)) - .build()) + .build() + ) } return styleableBuilder.build() } fun serializeArrayToPb( - array: ArrayResource, sourcePool: StringPool, logger: ILogger?): Resources.Array { + array: ArrayResource, sourcePool: StringPool, logger: ILogger? +): Resources.Array { val arrayBuilder = Resources.Array.newBuilder() for (element in array.elements) { @@ -610,13 +708,15 @@ fun serializeArrayToPb( entryBuilder .setComment(element.comment) .setItem(serializeItemToPb(element, logger)) - .build()) + .build() + ) } return arrayBuilder.build() } fun serializePluralToPb( - plural: Plural, sourcePool: StringPool, logger: ILogger?): Resources.Plural { + plural: Plural, sourcePool: StringPool, logger: ILogger? +): Resources.Plural { val pluralBuilder = Resources.Plural.newBuilder() for (type in Plural.Type.TYPES) { @@ -631,43 +731,43 @@ fun serializePluralToPb( .setComment(entry.comment) .setArity(serializePluralTypeToPb(type)) .setItem(serializeItemToPb(entry, logger)) - .build()) + .build() + ) } return pluralBuilder.build() } fun serializeMacroToPb(macro: Macro): Resources.MacroBody { + val macroBuilder = Resources.MacroBody.newBuilder() + macroBuilder.rawString = macro.rawValue!! - val macroBuilder = Resources.MacroBody.newBuilder() - macroBuilder.rawString = macro.rawValue!! + val styleString = Resources.StyleString.newBuilder() + styleString.str = macro.styleString!!.str + for (span in macro.styleString!!.spans) { + val spansBuilder = Resources.StyleString.Span.newBuilder() + spansBuilder.name = span.name + spansBuilder.startIndex = span.firstChar + spansBuilder.endIndex = span.lastChar + styleString.addSpans(spansBuilder.build()) + } + macroBuilder.styleString = styleString.build() - val styleString = macroBuilder.styleStringBuilder - styleString.str = macro.styleString!!.str - for (span in macro.styleString!!.spans) { - val spansBuilder = styleString.addSpansBuilder() - spansBuilder.name = span.name - spansBuilder.startIndex = span.firstChar - spansBuilder.endIndex = span.lastChar - styleString.addSpans(spansBuilder.build()) - } - macroBuilder.styleString = styleString.build() + for (untranslatables in macro.untranslatables) { + val section = Resources.UntranslatableSection.newBuilder() + section.startIndex = untranslatables.startIndex.toLong() + section.endIndex = untranslatables.endIndex.toLong() + macroBuilder.addUntranslatableSections(section.build()) + } - for (untranslatables in macro.untranslatables) { - val section = macroBuilder.addUntranslatableSectionsBuilder() - section.startIndex = untranslatables.startIndex.toLong() - section.endIndex = untranslatables.endIndex.toLong() - macroBuilder.addUntranslatableSections(section.build()) - } + for (aliasNamespace in macro.aliasNamespaces) { + val namespace = Resources.NamespaceAlias.newBuilder() + namespace.prefix = aliasNamespace.alias + namespace.packageName = aliasNamespace.packageName + namespace.isPrivate = aliasNamespace.isPrivate + macroBuilder.addNamespaceStack(namespace.build()) + } - for (aliasNamespace in macro.aliasNamespaces) { - val namespace = macroBuilder.addNamespaceStackBuilder() - namespace.prefix = aliasNamespace.alias - namespace.packageName = aliasNamespace.packageName - namespace.isPrivate = aliasNamespace.isPrivate - macroBuilder.addNamespaceStack(namespace.build()) - } - - return macroBuilder.build() + return macroBuilder.build() } fun serializeItemToPb(item: Item, logger: ILogger?): Resources.Item { @@ -709,7 +809,7 @@ fun serializeValueToPb(value: Value, sourcePool: StringPool, logger: ILogger?): } valueBuilder.setCompoundValue(compoundBuilder.build()) } - if (value.source.isNotEmpty()){ + if (value.source.isNotEmpty()) { valueBuilder.setSource(serializeSourceToPb(value.source, sourcePool)) } return valueBuilder diff --git a/subprojects/aaptcompiler/src/main/proto/com/android/aapt/Configuration.proto b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/Configuration.proto new file mode 100644 index 000000000..3998ab033 --- /dev/null +++ b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/Configuration.proto @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +//package aapt.pb; + +option java_package = "com.android.aapt"; + +// A description of the requirements a device must have in order for a +// resource to be matched and selected. +message Configuration { + enum LayoutDirection { + LAYOUT_DIRECTION_UNSET = 0; + LAYOUT_DIRECTION_LTR = 1; + LAYOUT_DIRECTION_RTL = 2; + } + enum ScreenLayoutSize { + SCREEN_LAYOUT_SIZE_UNSET = 0; + SCREEN_LAYOUT_SIZE_SMALL = 1; + SCREEN_LAYOUT_SIZE_NORMAL = 2; + SCREEN_LAYOUT_SIZE_LARGE = 3; + SCREEN_LAYOUT_SIZE_XLARGE = 4; + } + enum ScreenLayoutLong { + SCREEN_LAYOUT_LONG_UNSET = 0; + SCREEN_LAYOUT_LONG_LONG = 1; + SCREEN_LAYOUT_LONG_NOTLONG = 2; + } + enum ScreenRound { + SCREEN_ROUND_UNSET = 0; + SCREEN_ROUND_ROUND = 1; + SCREEN_ROUND_NOTROUND = 2; + } + enum WideColorGamut { + WIDE_COLOR_GAMUT_UNSET = 0; + WIDE_COLOR_GAMUT_WIDECG = 1; + WIDE_COLOR_GAMUT_NOWIDECG = 2; + } + enum Hdr { + HDR_UNSET = 0; + HDR_HIGHDR = 1; + HDR_LOWDR = 2; + } + enum Orientation { + ORIENTATION_UNSET = 0; + ORIENTATION_PORT = 1; + ORIENTATION_LAND = 2; + ORIENTATION_SQUARE = 3; + } + enum UiModeType { + UI_MODE_TYPE_UNSET = 0; + UI_MODE_TYPE_NORMAL = 1; + UI_MODE_TYPE_DESK = 2; + UI_MODE_TYPE_CAR = 3; + UI_MODE_TYPE_TELEVISION = 4; + UI_MODE_TYPE_APPLIANCE = 5; + UI_MODE_TYPE_WATCH = 6; + UI_MODE_TYPE_VRHEADSET = 7; + } + enum UiModeNight { + UI_MODE_NIGHT_UNSET = 0; + UI_MODE_NIGHT_NIGHT = 1; + UI_MODE_NIGHT_NOTNIGHT = 2; + } + enum Touchscreen { + TOUCHSCREEN_UNSET = 0; + TOUCHSCREEN_NOTOUCH = 1; + TOUCHSCREEN_STYLUS = 2; + TOUCHSCREEN_FINGER = 3; + } + enum KeysHidden { + KEYS_HIDDEN_UNSET = 0; + KEYS_HIDDEN_KEYSEXPOSED = 1; + KEYS_HIDDEN_KEYSHIDDEN = 2; + KEYS_HIDDEN_KEYSSOFT = 3; + } + enum Keyboard { + KEYBOARD_UNSET = 0; + KEYBOARD_NOKEYS = 1; + KEYBOARD_QWERTY = 2; + KEYBOARD_TWELVEKEY = 3; + } + enum NavHidden { + NAV_HIDDEN_UNSET = 0; + NAV_HIDDEN_NAVEXPOSED = 1; + NAV_HIDDEN_NAVHIDDEN = 2; + } + enum Navigation { + NAVIGATION_UNSET = 0; + NAVIGATION_NONAV = 1; + NAVIGATION_DPAD = 2; + NAVIGATION_TRACKBALL = 3; + NAVIGATION_WHEEL = 4; + } + enum GrammaticalGender { + GRAM_GENDER_USET = 0; + GRAM_GENDER_NEUTER = 1; + GRAM_GENDER_FEMININE = 2; + GRAM_GENDER_MASCULINE = 3; + } + // + // Axis/dimensions that are understood by the runtime. + // + // Mobile country code. + uint32 mcc = 1; + // Mobile network code. + uint32 mnc = 2; + // BCP-47 locale tag. + string locale = 3; + // Left-to-right, right-to-left... + LayoutDirection layout_direction = 4; + // Screen width in pixels. Prefer screen_width_dp. + uint32 screen_width = 5; + // Screen height in pixels. Prefer screen_height_dp. + uint32 screen_height = 6; + // Screen width in density independent pixels (dp). + uint32 screen_width_dp = 7; + // Screen height in density independent pixels (dp). + uint32 screen_height_dp = 8; + // The smallest screen dimension, regardless of orientation, in dp. + uint32 smallest_screen_width_dp = 9; + // Whether the device screen is classified as small, normal, large, xlarge. + ScreenLayoutSize screen_layout_size = 10; + // Whether the device screen is long. + ScreenLayoutLong screen_layout_long = 11; + // Whether the screen is round (Android Wear). + ScreenRound screen_round = 12; + // Whether the screen supports wide color gamut. + WideColorGamut wide_color_gamut = 13; + // Whether the screen has high dynamic range. + Hdr hdr = 14; + // Which orientation the device is in (portrait, landscape). + Orientation orientation = 15; + // Which type of UI mode the device is in (television, car, etc.). + UiModeType ui_mode_type = 16; + // Whether the device is in night mode. + UiModeNight ui_mode_night = 17; + // The device's screen density in dots-per-inch (dpi). + uint32 density = 18; + // Whether a touchscreen exists, supports a stylus, or finger. + Touchscreen touchscreen = 19; + // Whether the keyboard hardware keys are currently hidden, exposed, or + // if the keyboard is a software keyboard. + KeysHidden keys_hidden = 20; + // The type of keyboard present (none, QWERTY, 12-key). + Keyboard keyboard = 21; + // Whether the navigation is exposed or hidden. + NavHidden nav_hidden = 22; + // The type of navigation present on the device + // (trackball, wheel, dpad, etc.). + Navigation navigation = 23; + // The minimum SDK version of the device. + uint32 sdk_version = 24; + // Grammatical gender. + GrammaticalGender grammatical_gender = 26; + // + // Build-time only dimensions. + // + string product = 25; +} \ No newline at end of file diff --git a/subprojects/aaptcompiler/src/main/proto/com/android/aapt/README.md b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/README.md new file mode 100644 index 000000000..2bfb38a06 --- /dev/null +++ b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/README.md @@ -0,0 +1,5 @@ +# AAPT2 Protobuf sources + +These files are taken from +the [AAPT2 sources](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/tools/aapt2/), +licensed under The Apache License 2.0. See the files for the license. \ No newline at end of file diff --git a/subprojects/aaptcompiler/src/main/proto/com/android/aapt/Resources.proto b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/Resources.proto new file mode 100644 index 000000000..668d58599 --- /dev/null +++ b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/Resources.proto @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +import "com/android/aapt/Configuration.proto"; + +// package aapt.pb; + +option java_package = "com.android.aapt"; + + +// A string pool that wraps the binary form of the C++ class android::ResStringPool. +message StringPool { + bytes data = 1; +} +// The position of a declared entity within a file. +message SourcePosition { + uint32 line_number = 1; + uint32 column_number = 2; +} +// Developer friendly source file information for an entity in the resource table. +message Source { + // The index of the string path within the source string pool of a ResourceTable. + uint32 path_idx = 1; + SourcePosition position = 2; +} +// The name and version fingerprint of a build tool. +message ToolFingerprint { + string tool = 1; + string version = 2; +} +// References to non local resources +message DynamicRefTable { + PackageId package_id = 1; + string package_name = 2; +} +// Top level message representing a resource table. +message ResourceTable { + // The string pool containing source paths referenced throughout the resource table. This does + // not end up in the final binary ARSC file. + StringPool source_pool = 1; + // Resource definitions corresponding to an Android package. + repeated Package package = 2; + // The declarations within the resource table. + repeated Overlayable overlayable = 3; + // The version fingerprints of the tools that built the resource table. + repeated ToolFingerprint tool_fingerprint = 4; + repeated DynamicRefTable dynamic_ref_table = 5; +} +// A package ID in the range [0x00, 0xff]. +message PackageId { + uint32 id = 1; +} +// Defines resources for an Android package. +message Package { + // The package ID of this package, in the range [0x00, 0xff]. + // - ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time. + // - ID 0x01 is reserved for the 'android' package (framework). + // - ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time. + // - ID 0x7f is reserved for the application package. + // - IDs > 0x7f are reserved for the application as well and are treated as feature splits. + // This may not be set if no ID was assigned. + PackageId package_id = 1; + // The Java compatible Android package name of the app. + string package_name = 2; + // The series of types defined by the package. + repeated Type type = 3; +} +// A type ID in the range [0x01, 0xff]. +message TypeId { + uint32 id = 1; +} +// A set of resources grouped under a common type. Such types include string, layout, xml, dimen, +// attr, etc. This maps to the second part of a resource identifier in Java (R.type.entry). +message Type { + // The ID of the type. This may not be set if no ID was assigned. + TypeId type_id = 1; + // The name of the type. This corresponds to the 'type' part of a full resource name of the form + // package:type/entry. The set of legal type names is listed in Resource.cpp. + string name = 2; + // The entries defined for this type. + repeated Entry entry = 3; +} +// The Visibility of a symbol/entry (public, private, undefined). +message Visibility { + // The visibility of the resource outside of its package. + enum Level { + // No visibility was explicitly specified. This is typically treated as private. + // The distinction is important when two separate R.java files are generated: a public and + // private one. An unknown visibility, in this case, would cause the resource to be omitted + // from either R.java. + UNKNOWN = 0; + // A resource was explicitly marked as private. This means the resource can not be accessed + // outside of its package unless the @*package:type/entry notation is used (the asterisk being + // the private accessor). If two R.java files are generated (private + public), the resource + // will only be emitted to the private R.java file. + PRIVATE = 1; + // A resource was explicitly marked as public. This means the resource can be accessed + // from any package, and is emitted into all R.java files, public and private. + PUBLIC = 2; + } + Level level = 1; + // The path at which this entry's visibility was defined (eg. public.xml). + Source source = 2; + // The comment associated with the tag. + string comment = 3; + // Indicates that the resource id may change across builds and that the public R.java identifier + // for this resource should not be final. This is set to `true` for resources in `staging-group` + // tags. + bool staged_api = 4; +} +// Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an +// existing resource. +message AllowNew { + // Where this was defined in source. + Source source = 1; + // Any comment associated with the declaration. + string comment = 2; +} +// Represents a set of overlayable resources. +message Overlayable { + // The name of the . + string name = 1; + // The location of the declaration in the source. + Source source = 2; + // The component responsible for enabling and disabling overlays targeting this . + string actor = 3; +} +// Represents an overlayable declaration within an tag. +message OverlayableItem { + enum Policy { + NONE = 0; + PUBLIC = 1; + SYSTEM = 2; + VENDOR = 3; + PRODUCT = 4; + SIGNATURE = 5; + ODM = 6; + OEM = 7; + ACTOR = 8; + CONFIG_SIGNATURE = 9; + } + // The location of the declaration in source. + Source source = 1; + // Any comment associated with the declaration. + string comment = 2; + // The policy defined by the enclosing tag of this . + repeated Policy policy = 3; + // The index into overlayable list that points to the tag that contains + // this . + uint32 overlayable_idx = 4; +} +// The staged resource ID definition of a finalized resource. +message StagedId { + Source source = 1; + uint32 staged_id = 2; +} +// An entry ID in the range [0x0000, 0xffff]. +message EntryId { + uint32 id = 1; +} +// An entry declaration. An entry has a full resource ID that is the combination of package ID, +// type ID, and its own entry ID. An entry on its own has no value, but values are defined for +// various configurations/variants. +message Entry { + // The ID of this entry. Together with the package ID and type ID, this forms a full resource ID + // of the form 0xPPTTEEEE, where PP is the package ID, TT is the type ID, and EEEE is the entry + // ID. + // This may not be set if no ID was assigned. + EntryId entry_id = 1; + // The name of this entry. This corresponds to the 'entry' part of a full resource name of the + // form package:type/entry. + string name = 2; + // The visibility of this entry (public, private, undefined). + Visibility visibility = 3; + // Whether this resource, when originating from a compile-time overlay, is allowed to NOT overlay + // any existing resources. + AllowNew allow_new = 4; + // Whether this resource can be overlaid by a runtime resource overlay (RRO). + OverlayableItem overlayable_item = 5; + // The set of values defined for this entry, each corresponding to a different + // configuration/variant. + repeated ConfigValue config_value = 6; + // The staged resource ID of this finalized resource. + StagedId staged_id = 7; +} +// A Configuration/Value pair. +message ConfigValue { + Configuration config = 1; + Value value = 2; +} +// The generic meta-data for every value in a resource table. +message Value { + // Where the value was defined. + Source source = 1; + // Any comment associated with the value. + string comment = 2; + // Whether the value can be overridden. + bool weak = 3; + // The value is either an Item or a CompoundValue. + oneof value { + Item item = 4; + CompoundValue compound_value = 5; + } +} +// An Item is an abstract type. It represents a value that can appear inline in many places, such +// as XML attribute values or on the right hand side of style attribute definitions. The concrete +// type is one of the types below. Only one can be set. +message Item { + oneof value { + Reference ref = 1; + String str = 2; + RawString raw_str = 3; + StyledString styled_str = 4; + FileReference file = 5; + Id id = 6; + Primitive prim = 7; + } +} +// A CompoundValue is an abstract type. It represents a value that is a made of other values. +// These can only usually appear as top-level resources. The concrete type is one of the types +// below. Only one can be set. +message CompoundValue { + oneof value { + Attribute attr = 1; + Style style = 2; + Styleable styleable = 3; + Array array = 4; + Plural plural = 5; + MacroBody macro = 6; + } +} +// Message holding a boolean, so it can be optionally encoded. +message Boolean { + bool value = 1; +} +// A value that is a reference to another resource. This reference can be by name or resource ID. +message Reference { + enum Type { + // A plain reference (@package:type/entry). + REFERENCE = 0; + // A reference to a theme attribute (?package:type/entry). + ATTRIBUTE = 1; + } + Type type = 1; + // The resource ID (0xPPTTEEEE) of the resource being referred. This is optional. + uint32 id = 2; + // The name of the resource being referred. This is optional if the resource ID is set. + string name = 3; + // Whether this reference is referencing a private resource (@*package:type/entry). + bool private = 4; + // Whether this reference is dynamic. + Boolean is_dynamic = 5; + // The type flags used when compiling the reference. Used for substituting the contents of macros. + uint32 type_flags = 6; + // Whether raw string values would have been accepted in place of this reference definition. Used + // for substituting the contents of macros. + bool allow_raw = 7; +} +// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a +// resource ID (0xPPTTEEEE) as a unique identifier. Their value is unimportant. +message Id { +} +// A value that is a string. +message String { + string value = 1; +} +// A value that is a raw string, which is unescaped/uninterpreted. This is typically used to +// represent the value of a style attribute before the attribute is compiled and the set of +// allowed values is known. +message RawString { + string value = 1; +} +// A string with styling information, like html tags that specify boldness, italics, etc. +message StyledString { + // The raw text of the string. + string value = 1; + // A Span marks a region of the string text that is styled. + message Span { + // The name of the tag, and its attributes, encoded as follows: + // tag_name;attr1=value1;attr2=value2;[...] + string tag = 1; + // The first character position this span applies to, in UTF-16 offset. + uint32 first_char = 2; + // The last character position this span applies to, in UTF-16 offset. + uint32 last_char = 3; + } + repeated Span span = 2; +} +// A value that is a reference to an external entity, like an XML file or a PNG. +message FileReference { + enum Type { + UNKNOWN = 0; + PNG = 1; + BINARY_XML = 2; + PROTO_XML = 3; + } + // Path to a file within the APK (typically res/type-config/entry.ext). + string path = 1; + // The type of file this path points to. For UAM bundle, this cannot be + // BINARY_XML. + Type type = 2; +} +// A value that represents a primitive data type (float, int, boolean, etc.). +// Refer to Res_value in ResourceTypes.h for info on types and formatting +message Primitive { + message NullType { + } + message EmptyType { + } + oneof oneof_value { + NullType null_value = 1; + EmptyType empty_value = 2; + float float_value = 3; + uint32 dimension_value = 13; + uint32 fraction_value = 14; + int32 int_decimal_value = 6; + uint32 int_hexadecimal_value = 7; + bool boolean_value = 8; + uint32 color_argb8_value = 9; + uint32 color_rgb8_value = 10; + uint32 color_argb4_value = 11; + uint32 color_rgb4_value = 12; + float dimension_value_deprecated = 4 [deprecated=true]; + float fraction_value_deprecated = 5 [deprecated=true]; + } +} +// A value that represents an XML attribute and what values it accepts. +message Attribute { + // A Symbol used to represent an enum or a flag. + message Symbol { + // Where the enum/flag item was defined. + Source source = 1; + // Any comments associated with the enum or flag. + string comment = 2; + // The name of the enum/flag as a reference. Enums/flag items are generated as ID resource + // values. + Reference name = 3; + // The value of the enum/flag. + uint32 value = 4; + // The data type of the enum/flag as defined in android::Res_value. + uint32 type = 5; + } + // Bitmask of formats allowed for an attribute. + enum FormatFlags { + NONE = 0x0; // Proto3 requires a default of 0. + ANY = 0x0000ffff; // Allows any type except ENUM and FLAGS. + REFERENCE = 0x01; // Allows Reference values. + STRING = 0x02; // Allows String/StyledString values. + INTEGER = 0x04; // Allows any integer BinaryPrimitive values. + BOOLEAN = 0x08; // Allows any boolean BinaryPrimitive values. + COLOR = 0x010; // Allows any color BinaryPrimitive values. + FLOAT = 0x020; // Allows any float BinaryPrimitive values. + DIMENSION = 0x040; // Allows any dimension BinaryPrimitive values. + FRACTION = 0x080; // Allows any fraction BinaryPrimitive values. + ENUM = 0x00010000; // Allows enums that are defined in the Attribute's symbols. + // ENUM and FLAGS cannot BOTH be set. + FLAGS = 0x00020000; // Allows flags that are defined in the Attribute's symbols. + // ENUM and FLAGS cannot BOTH be set. + } + // A bitmask of types that this XML attribute accepts. Corresponds to the flags in the + // enum FormatFlags. + uint32 format_flags = 1; + // The smallest integer allowed for this XML attribute. Only makes sense if the format includes + // FormatFlags::INTEGER. + int32 min_int = 2; + // The largest integer allowed for this XML attribute. Only makes sense if the format includes + // FormatFlags::INTEGER. + int32 max_int = 3; + // The set of enums/flags defined in this attribute. Only makes sense if the format includes + // either FormatFlags::ENUM or FormatFlags::FLAGS. Having both is an error. + repeated Symbol symbol = 4; +} +// A value that represents a style. +message Style { + // An XML attribute/value pair defined in the style. + message Entry { + // Where the entry was defined. + Source source = 1; + // Any comments associated with the entry. + string comment = 2; + // A reference to the XML attribute. + Reference key = 3; + // The Item defined for this XML attribute. + Item item = 4; + } + // The optinal style from which this style inherits attributes. + Reference parent = 1; + // The source file information of the parent inheritance declaration. + Source parent_source = 2; + // The set of XML attribute/value pairs for this style. + repeated Entry entry = 3; +} +// A value that represents a XML resource. These are not real resources and +// only end up as Java fields in the generated R.java. They do not end up in the binary ARSC file. +message Styleable { + // An attribute defined for this styleable. + message Entry { + // Where the attribute was defined within the block. + Source source = 1; + // Any comments associated with the declaration. + string comment = 2; + // The reference to the attribute. + Reference attr = 3; + } + // The set of attribute declarations. + repeated Entry entry = 1; +} +// A value that represents an array of resource values. +message Array { + // A single element of the array. + message Element { + // Where the element was defined. + Source source = 1; + // Any comments associated with the element. + string comment = 2; + // The value assigned to this element. + Item item = 3; + } + // The list of array elements. + repeated Element element = 1; +} +// A value that represents a string and its many variations based on plurality. +message Plural { + // The arity of the plural. + enum Arity { + ZERO = 0; + ONE = 1; + TWO = 2; + FEW = 3; + MANY = 4; + OTHER = 5; + } + // The plural value for a given arity. + message Entry { + // Where the plural was defined. + Source source = 1; + // Any comments associated with the plural. + string comment = 2; + // The arity of the plural. + Arity arity = 3; + // The value assigned to this plural. + Item item = 4; + } + // The set of arity/plural mappings. + repeated Entry entry = 1; +} +// Defines an abstract XmlNode that must be either an XmlElement, or +// a text node represented by a string. +message XmlNode { + oneof node { + XmlElement element = 1; + string text = 2; + } + // Source line and column info. + SourcePosition source = 3; +} +// An in an XML document. +message XmlElement { + // Namespaces defined on this element. + repeated XmlNamespace namespace_declaration = 1; + // The namespace URI of this element. + string namespace_uri = 2; + // The name of this element. + string name = 3; + // The attributes of this element. + repeated XmlAttribute attribute = 4; + // The children of this element. + repeated XmlNode child = 5; +} +// A namespace declaration on an XmlElement (xmlns:android="http://..."). +message XmlNamespace { + string prefix = 1; + string uri = 2; + // Source line and column info. + SourcePosition source = 3; +} +// An attribute defined on an XmlElement (android:text="..."). +message XmlAttribute { + string namespace_uri = 1; + string name = 2; + string value = 3; + // Source line and column info. + SourcePosition source = 4; + // The optional resource ID (0xPPTTEEEE) of the attribute. + uint32 resource_id = 5; + // The optional interpreted/compiled version of the `value` string. + Item compiled_item = 6; +} +message MacroBody { + string raw_string = 1; + StyleString style_string = 2; + repeated UntranslatableSection untranslatable_sections = 3; + repeated NamespaceAlias namespace_stack = 4; + SourcePosition source = 5; +} +message NamespaceAlias { + string prefix = 1; + string package_name = 2; + bool is_private = 3; +} +message StyleString { + message Span { + string name = 1; + uint32 start_index = 2; + uint32 end_index = 3; + } + string str = 1; + repeated Span spans = 2; +} +message UntranslatableSection { + uint64 start_index = 1; + uint64 end_index = 2; +} \ No newline at end of file diff --git a/subprojects/aaptcompiler/src/main/proto/com/android/aapt/ResourcesInternal.proto b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/ResourcesInternal.proto new file mode 100644 index 000000000..e9791cbe6 --- /dev/null +++ b/subprojects/aaptcompiler/src/main/proto/com/android/aapt/ResourcesInternal.proto @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +import "com/android/aapt/Configuration.proto"; +import "com/android/aapt/Resources.proto"; + +// package aapt.pb.internal; + +option java_package = "android.aapt.pb.internal"; + +// The top level message representing an external resource file (layout XML, PNG, etc). +// This is used to represent a compiled file before it is linked. Only useful to aapt2. +message CompiledFile { + message Symbol { + // The name of the symbol (in the form package:type/name). + string resource_name = 1; + // The position in the file at which this symbol is defined. For debug use. + SourcePosition source = 2; + } + // The name of the resource (in the form package:type/name). + string resource_name = 1; + // The configuration for which the resource is defined. + Configuration config = 2; + // The type of the file. + FileReference.Type type = 3; + // The filesystem path to where the source file originated. + // Mainly used to display helpful error messages. + string source_path = 4; + // Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file). + repeated Symbol exported_symbol = 5; +} \ No newline at end of file