fix(aaptcompiler): merge aaptcompiler from AOSP upstream

This commit is contained in:
Akash Yadav
2024-05-23 23:11:14 +05:30
parent 275d98c23c
commit dc0b00c7f5
48 changed files with 2251 additions and 1499 deletions
+1 -33
View File
@@ -121,6 +121,7 @@
<module name="AndroidIDE.subprojects.fuzzysearch.test" target="11" />
<module name="AndroidIDE.subprojects.google-java-format" target="17" />
<module name="AndroidIDE.subprojects.google-java-format.test" target="11" />
<module name="AndroidIDE.subprojects.indexing.test" target="17" />
<module name="AndroidIDE.subprojects.java-compiler-utils.main" target="8" />
<module name="AndroidIDE.subprojects.java-compiler-utils.test" target="8" />
<module name="AndroidIDE.subprojects.javac" target="11" />
@@ -166,50 +167,17 @@
<module name="ATS.tree-sitter-log" target="17" />
<module name="ATS.tree-sitter-python" target="17" />
<module name="ATS.tree-sitter-xml" target="17" />
<module name="build-deps.appintro" target="17" />
<module name="build-deps.desugaring-core" target="11" />
<module name="build-deps.desugaring-core.main" target="11" />
<module name="build-deps.desugaring-core.test" target="11" />
<module name="build-deps.fuzzysearch" target="17" />
<module name="build-deps.fuzzysearch.main" target="17" />
<module name="build-deps.fuzzysearch.test" target="17" />
<module name="build-deps.google-java-format" target="17" />
<module name="build-deps.javac" target="17" />
<module name="build-deps.javac.main" target="17" />
<module name="build-deps.javac.test" target="17" />
<module name="build-deps.javapoet" target="17" />
<module name="build-deps.javapoet.main" target="17" />
<module name="build-deps.javapoet.test" target="17" />
<module name="build-deps.jdk-jdeps" target="17" />
<module name="build-deps.jdk-jdeps.main" target="17" />
<module name="build-deps.jdk-jdeps.test" target="17" />
<module name="build-deps.jdt" target="17" />
<module name="build-deps.jdt.main" target="17" />
<module name="build-deps.jdt.test" target="17" />
<module name="build-deps.layoutlib-api" target="17" />
<module name="build-deps.layoutlib-api.main" target="17" />
<module name="build-deps.layoutlib-api.test" target="17" />
<module name="build-logic.ats" target="17" />
<module name="build-logic.ats.main" target="17" />
<module name="build-logic.ats.test" target="17" />
<module name="build-logic.common" target="17" />
<module name="build-logic.common.main" target="17" />
<module name="build-logic.common.test" target="17" />
<module name="build-logic.desugaring" target="17" />
<module name="build-logic.desugaring-core" target="11" />
<module name="build-logic.desugaring-core.main" target="11" />
<module name="build-logic.desugaring-core.test" target="11" />
<module name="build-logic.desugaring.main" target="17" />
<module name="build-logic.desugaring.test" target="17" />
<module name="build-logic.ide" target="17" />
<module name="build-logic.ide.main" target="17" />
<module name="build-logic.ide.test" target="17" />
<module name="build-logic.plugins" target="17" />
<module name="build-logic.plugins.main" target="17" />
<module name="build-logic.plugins.test" target="17" />
<module name="build-logic.properties-parser" target="17" />
<module name="build-logic.properties-parser.main" target="17" />
<module name="build-logic.properties-parser.test" target="17" />
<module name="composite-builds.build-logic.desugaring-core.main" target="11" />
<module name="composite-builds.build-logic.desugaring-core.test" target="11" />
<module name="composite-builds.plugins" target="17" />
+1
View File
@@ -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 {
+2 -1
View File
@@ -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" }
+1
View File
@@ -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)
+21 -2
View File
@@ -15,11 +15,13 @@
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
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)
@@ -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"
+45 -21
View File
@@ -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 {
@@ -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
}
@@ -27,9 +27,8 @@ fun parseMcc(part: String, config: ConfigDescription): Boolean {
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) {
// 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
@@ -44,8 +43,9 @@ fun parseMnc(part: String, config: ConfigDescription): Boolean {
return true
}
// The only acceptable format is "mncDDD" (where D is a digit).
if (!part.startsWith("mnc") || part.length != 6) {
// 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
@@ -57,8 +57,7 @@ fun parseMnc(part: String, config: ConfigDescription): Boolean {
// TODO(@daniellabar): Make more like the aapt2 parser.
fun parseLayoutDirection(part: String, config: ConfigDescription) : Boolean {
val value =
when (part) {
val value = when(part) {
WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT.DIR_ANY
"ldltr" -> ResTableConfig.SCREEN_LAYOUT.DIR_LTR
"ldrtl" -> ResTableConfig.SCREEN_LAYOUT.DIR_RTL
@@ -110,8 +109,7 @@ fun parseScreenHeightDp(part: String, config: ConfigDescription): Boolean {
}
fun parseScreenLayoutSize(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> 0
"small" -> ResTableConfig.SCREEN_LAYOUT.SIZE_SMALL
"normal" -> ResTableConfig.SCREEN_LAYOUT.SIZE_NORMAL
@@ -126,8 +124,7 @@ fun parseScreenLayoutSize(part: String, config: ConfigDescription): Boolean {
}
fun parseScreenLayoutLong(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_ANY
"long" -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_YES
"notlong" -> ResTableConfig.SCREEN_LAYOUT.SCREENLONG_NO
@@ -140,8 +137,7 @@ fun parseScreenLayoutLong(part: String, config: ConfigDescription): Boolean {
}
fun parseScreenRound(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_ANY
"round" -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_YES
"notround" -> ResTableConfig.SCREEN_LAYOUT2.SCREENROUND_NO
@@ -153,8 +149,7 @@ fun parseScreenRound(part: String, config: ConfigDescription): Boolean {
}
fun parseWideColorGamut(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
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
@@ -166,8 +161,7 @@ fun parseWideColorGamut(part: String, config: ConfigDescription): Boolean {
}
fun parseHdr(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.COLOR_MODE.HDR_ANY
"highdr" -> ResTableConfig.COLOR_MODE.HDR_YES
"lowdr" -> ResTableConfig.COLOR_MODE.HDR_NO
@@ -178,8 +172,7 @@ fun parseHdr(part: String, config: ConfigDescription): Boolean {
}
fun parseOrientation(part: String, config: ConfigDescription): Boolean {
config.orientation =
when (part) {
config.orientation = when (part) {
WILDCARD_NAME -> ResTableConfig.ORIENTATION.ANY
"port" -> ResTableConfig.ORIENTATION.PORT
"land" -> ResTableConfig.ORIENTATION.LAND
@@ -190,8 +183,7 @@ fun parseOrientation(part: String, config: ConfigDescription): Boolean {
}
fun parseUiModeType(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.UI_MODE.TYPE_ANY
"desk" -> ResTableConfig.UI_MODE.TYPE_DESK
"car" -> ResTableConfig.UI_MODE.TYPE_CAR
@@ -206,8 +198,7 @@ fun parseUiModeType(part: String, config: ConfigDescription): Boolean {
}
fun parseUiModeNight(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.UI_MODE.NIGHT_ANY
"night" -> ResTableConfig.UI_MODE.NIGHT_YES
"notnight" -> ResTableConfig.UI_MODE.NIGHT_NO
@@ -219,8 +210,7 @@ fun parseUiModeNight(part: String, config: ConfigDescription): Boolean {
fun parseDensity(part: String, config: ConfigDescription): Boolean {
if (part.isNotEmpty() && part[0].isLetter()) {
config.density =
when (part) {
config.density = when (part) {
WILDCARD_NAME -> ResTableConfig.DENSITY.DEFAULT
"anydpi" -> ResTableConfig.DENSITY.ANY
"nodpi" -> ResTableConfig.DENSITY.NONE
@@ -246,8 +236,7 @@ fun parseDensity(part: String, config: ConfigDescription): Boolean {
}
fun parseTouchscreen(part: String, config: ConfigDescription): Boolean {
config.touchscreen =
when (part) {
config.touchscreen = when (part) {
WILDCARD_NAME -> ResTableConfig.TOUCHSCREEN.ANY
"notouch" -> ResTableConfig.TOUCHSCREEN.NOTOUCH
"stylus" -> ResTableConfig.TOUCHSCREEN.STYLUS
@@ -258,8 +247,7 @@ fun parseTouchscreen(part: String, config: ConfigDescription): Boolean {
}
fun parseKeysHidden(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_ANY
"keysexposed" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_NO
"keyshidden" -> ResTableConfig.INPUT_FLAGS.KEYSHIDDEN_YES
@@ -272,8 +260,7 @@ fun parseKeysHidden(part: String, config: ConfigDescription): Boolean {
}
fun parseKeyboard(part: String, config: ConfigDescription): Boolean {
config.keyboard =
when (part) {
config.keyboard = when (part) {
WILDCARD_NAME -> ResTableConfig.KEYBOARD.ANY
"nokeys" -> ResTableConfig.KEYBOARD.NOKEYS
"qwerty" -> ResTableConfig.KEYBOARD.QWERTY
@@ -284,8 +271,7 @@ fun parseKeyboard(part: String, config: ConfigDescription): Boolean {
}
fun parseNavHidden(part: String, config: ConfigDescription): Boolean {
val value =
when (part) {
val value = when (part) {
WILDCARD_NAME -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_ANY
"navexposed" -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_NO
"navhidden" -> ResTableConfig.INPUT_FLAGS.NAVHIDDEN_YES
@@ -297,8 +283,7 @@ fun parseNavHidden(part: String, config: ConfigDescription): Boolean {
}
fun parseNavigation(part: String, config: ConfigDescription): Boolean {
config.navigation =
when (part) {
config.navigation = when (part) {
WILDCARD_NAME -> ResTableConfig.NAVIGATION.ANY
"nonav" -> ResTableConfig.NAVIGATION.NONAV
"dpad" -> ResTableConfig.NAVIGATION.DPAD
@@ -309,6 +294,17 @@ fun parseNavigation(part: String, config: ConfigDescription): Boolean {
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
@@ -346,5 +342,6 @@ fun parseVersion(part: String, config: ConfigDescription): Boolean {
fun maskAndApply(current: Byte, mask: Int, value: Byte) =
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
@@ -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"
@@ -40,8 +40,8 @@ private const val PNG_EXTENSION = "png"
* @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)
* @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.
* @param verbose Whether or not to show verbose logs.
@@ -55,8 +55,7 @@ data class ResourceCompilerOptions(
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 {
@@ -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) {
@@ -136,20 +130,16 @@ fun compileResource(
}
}
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}'" +
val errorMsg = "Invalid resource type '${pathData.resourceDirectory}'" +
" for file ${pathData.file.absolutePath}"
logger?.warning(errorMsg)
error(errorMsg)
@@ -168,9 +158,9 @@ 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.
@@ -180,19 +170,16 @@ private fun compileTable(
pathData: ResourcePathData,
outputDirectory: File,
options: ResourceCompilerOptions,
logger: BlameLogger
) {
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(
val extractorOptions = TableExtractorOptions(
translatable = !pathData.name.contains("donottranslate"),
errorOnPositionalArgs = !options.legacyMode,
visibility = options.visibility
)
visibility = options.visibility)
val tableExtractor =
TableExtractor(table, pathData.source, pathData.config, extractorOptions, logger)
@@ -203,8 +190,7 @@ private fun compileTable(
// 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
"Failed to compile values resource file ${pathData.file}", e
)
}
@@ -235,17 +221,14 @@ private fun compileTable(
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}"
)
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 ""
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}"
)
builder.appendln("public int styleable ${entry.key}${childPackage}_${it.name.entry}")
}
}
}
@@ -257,10 +240,7 @@ private fun compileTable(
}
}
private fun getVisibility(
values: Collection<ResourceEntry>,
type: AaptResourceType
): ResourceVisibility {
private fun getVisibility(values: Collection<ResourceEntry>, 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
@@ -287,13 +267,11 @@ private fun compileFile(
pathData: ResourcePathData,
outputDirectory: File,
options: ResourceCompilerOptions,
logger: BlameLogger?
) {
logger: BlameLogger?) {
val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename())
logger?.info("Compiling file ${pathData.file.absolutePath} to $outputFile")
pathData.file.inputStream().use {
val resourceFile =
ResourceFile(
val resourceFile = ResourceFile(
ResourceName("", resourceTypeFromTag(pathData.resourceDirectory)!!, pathData.name),
pathData.config,
pathData.source,
@@ -331,13 +309,11 @@ private fun compileXml(
pathData: ResourcePathData,
outputDirectory: File,
options: ResourceCompilerOptions,
logger: BlameLogger?
) {
logger: BlameLogger?) {
val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename())
logger?.info("Compiling xml file ${pathData.file.absolutePath} to $outputFile")
val fileToProcess =
ResourceFile(
val fileToProcess = ResourceFile(
ResourceName("", pathData.type!!, pathData.name),
pathData.config,
pathData.source,
@@ -350,12 +326,14 @@ private fun compileXml(
xmlProcessor.process(fileToProcess, it)
} catch (e: Exception) {
throw ResourceCompilationException(
"Failed to compile resource file: " + logger?.getOutputSource(blameSource(pathData.source)),
"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,8 +359,9 @@ 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.
@@ -391,8 +370,7 @@ private fun compilePng(
pathData: ResourcePathData,
outputDirectory: File,
options: ResourceCompilerOptions,
logger: BlameLogger?
) {
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.")
@@ -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)
}
/**
+63 -140
View File
@@ -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<String>()
private val includedPackages = mutableIntObjectMapOf<String>()
enum class CollisionResult {
KEEP_ORIGINAL,
@@ -40,10 +39,7 @@ 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.
@@ -58,20 +54,10 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
* 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.
@@ -91,17 +77,9 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
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.
@@ -116,11 +94,9 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
* 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
@@ -142,8 +118,8 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
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
@@ -158,12 +134,9 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
* 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
@@ -184,9 +157,9 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
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,7 +263,6 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
packages.add(newPackage)
newPackage
}
else -> tablePackage
}
}
@@ -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
}
@@ -471,8 +422,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
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
}
@@ -504,27 +453,22 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
resourceEntry.visibility.level != ResourceVisibility.UNDEFINED -> {
// We can't undefine a symbol. Ignore
}
visibility.level == ResourceVisibility.PRIVATE &&
resourceEntry.visibility.level == ResourceVisibility.PUBLIC -> {
logError(
blameSource(source),
"Failed to add resource '$name' as private (java-symbol) because it was " +
"previously defined as public."
)
"previously defined as public.")
return false
}
visibility.level == ResourceVisibility.PUBLIC &&
resourceEntry.visibility.level == ResourceVisibility.PRIVATE -> {
logError(
blameSource(source),
"Failed to add resource '$name' as public because it was previously defined as " +
"private (java-symbol)."
)
"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,20 +516,17 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
}
companion object {
fun resolveValueCollision(existing: Value, incoming: Value):CollisionResult {
val existingAttr = existing as? AttributeResource
val incomingAttr = incoming as? AttributeResource
incomingAttr
?: return when {
incomingAttr ?: return when {
incoming.weak -> CollisionResult.KEEP_ORIGINAL
existing.weak -> CollisionResult.TAKE_NEW
else -> CollisionResult.CONFLICT
}
existingAttr
?: return when {
existingAttr ?: return when {
existing.weak -> CollisionResult.TAKE_NEW
else -> CollisionResult.CONFLICT
}
@@ -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 <add-resource> 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(""))
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}
}
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
}
}
@@ -729,7 +657,6 @@ class ResourceTablePackage(var name: String = "", var id: Byte? = null) {
* etc.).
*/
class ResourceGroup(val type : AaptResourceType) {
var id: Byte? = null
var visibility = ResourceVisibility.UNDEFINED
@@ -766,7 +693,6 @@ class ResourceGroup(val type: AaptResourceType) {
entries.getOrPut(name) { TreeMap(nullsFirst()) }[entryId] = newEntry
newEntry
}
else -> entry
}
}
@@ -774,14 +700,13 @@ class ResourceGroup(val type: AaptResourceType) {
/** Represents a resource entry, which may have varying values for each defined configuration. */
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<ResourceConfigValue>()
internal val values = mutableListOf<ResourceConfigValue>()
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
}
}
@@ -811,5 +735,4 @@ class ResourceEntry(val name: String) {
data class ResourceConfigValue(
val config: ConfigDescription,
val product : String,
var value: Value? = null
)
var value: Value? = null)
+35 -45
View File
@@ -32,12 +32,8 @@ fun tryParseBool(string: String): BinaryPrimitive? {
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
}
@@ -66,11 +62,9 @@ fun tryParseInt(value: String): BinaryPrimitive? {
fun parseResourceId(value: String): Int? {
val resValue = stringToInt(value)
if (
resValue != null &&
if (resValue != null &&
resValue.dataType == ResValue.DataType.INT_HEX &&
resValue.data.isValidDynamicId()
) {
resValue.data.isValidDynamicId()) {
return resValue.data
}
return null
@@ -142,7 +136,10 @@ 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)
@@ -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? {
*
* <p> The reference must be of one of the following forms:
*
* + "@<entry>" + "@<type>/<entry>" + "@<package>:<entry>" + "@<package>:<type>/<entry>"
* + "@<entry>"
* + "@<type>/<entry>"
* + "@<package>:<entry>"
* + "@<package>:<type>/<entry>"
*
* <p> 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,8 +222,9 @@ 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? {
val trimmedValue = value.trim()
@@ -271,7 +269,10 @@ data class ResourceNameInfo(val resourceName: ResourceName, val isPrivate: Boole
*
* <p> The resource name must be in one of the following forms:
*
* + "<entry>" + "<type>/<entry>" + "<package>:<entry>" + "<package>:<type>/<entry>"
* + "<entry>"
* + "<type>/<entry>"
* + "<package>:<entry>"
* + "<package>:<type>/<entry>"
*
* The value may optionally be preceded by a '*' to represent that the resource name is private.
*
@@ -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) {
@@ -397,16 +393,13 @@ fun tryParseItemForAttribute(
}
}
val floatMask =
Resources.Attribute.FormatFlags.FLOAT_VALUE or
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 &&
if (!hasLeadingIdentifiers &&
resourceName.pck!!.isEmpty() &&
possibleResourceName.typeName.isNotEmpty()
) {
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,7 +602,7 @@ 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)
@@ -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()
const val SDK_U = 34.toShort()
}
+14 -28
View File
@@ -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,9 +276,7 @@ class StringPool {
return 12
}
private fun encodeString(
str: String, utf8: Boolean, out: BigBuffer, logger: ILogger?): Boolean {
private fun encodeString(str: String, utf8: Boolean, out: BigBuffer) {
if (utf8) {
val utf16Length = str.length
val utf8Str = str.toByteArray()
@@ -293,11 +284,10 @@ class StringPool {
// 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<Entry> {it.value}
val STYLE_ON_VALUE = compareBy<StyleEntry> {it.value}
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -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,13 +701,13 @@ 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
}
}
@@ -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
+2 -1
View File
@@ -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/"
@@ -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
}
@@ -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<Int, Byte>(
internal val LIKELY_SCRIPTS = intByteMapOf(
0x61610000 to 40, // aa -> Latn
0xa0000000.toInt() to 40, // aai -> Latn
0xa8000000.toInt() to 40, // aak -> Latn
@@ -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 {
@@ -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
@@ -20,20 +20,23 @@ 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. <p>Transliterated from: *
* Definitions of resource data structures.
* <p>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)
@@ -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.
*
* <p> 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,8 +208,7 @@ 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)
@@ -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<String>,
val stylesPoolSize: Int,
val styles: List<List<ResStringPoolSpan>>
) {
val styles: List<List<ResStringPoolSpan>>) {
companion object {
fun get(buffer: ByteBuffer, length: Int): ResStringPool {
@@ -268,8 +264,7 @@ private constructor(
val headerSize = buffer.getShort(2).deviceToHost()
val resourceSize = buffer.getInt(4).deviceToHost()
if (
typeId != ChunkType.STRING_POOL_TYPE.id ||
if (typeId != ChunkType.STRING_POOL_TYPE.id ||
headerSize != ResStringPoolHeader.SIZE ||
resourceSize < headerSize ||
resourceSize > length
@@ -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<String>()
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.")
}
@@ -370,8 +360,7 @@ private constructor(
val firstByteUTF16 = buffer.get(stringPosition)
++stringPosition
val utf16Length =
when {
val utf16Length = when {
(firstByteUTF16.toInt() and StringPool.TWO_BYTE_UTF8_LENGTH_SIGNIFIER) != 0 -> {
val secondByte = buffer.get(stringPosition).toInt() and 0xff
++stringPosition
@@ -385,8 +374,7 @@ private constructor(
val firstByteUTF8 = buffer.get(stringPosition)
++stringPosition
val utf8Length =
when {
val utf8Length = when {
(firstByteUTF8.toInt() and StringPool.TWO_BYTE_UTF8_LENGTH_SIGNIFIER) != 0 -> {
val secondByte = buffer.get(stringPosition).toInt() and 0xff
++stringPosition
@@ -420,8 +408,7 @@ private constructor(
val firstShort = buffer.getShort(stringPosition).deviceToHost()
stringPosition += 2
val utf16Length =
when {
val utf16Length = when {
(firstShort.toInt() and StringPool.TWO_CHAR_UTF16_LENGTH_SIGNIFIER) != 0 -> {
val secondShort = buffer.getShort(stringPosition).deviceToHost().toInt() and 0xffff
stringPosition += 2
@@ -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
@@ -554,19 +541,13 @@ fun stringToInt(string: String): ResValue? {
return ResValue(
if(isHex) ResValue.DataType.INT_HEX else ResValue.DataType.INT_DEC,
value.toInt()
)
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(
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),
@@ -576,12 +557,7 @@ private val unitSuffixes =
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
)
)
"%p", ResValue.DataType.FRACTION, ResValue.ComplexFormat.UNIT_FRACTION_PARENT, 1.0f/100))
private fun parseUnitType(string: String) : UnitEntry? {
for (entry in unitSuffixes) {
@@ -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
}
@@ -649,8 +626,7 @@ fun stringToFloat(string: String): ResValue? {
// and bits 45 to 23 representing the whole number part.
val bits = (parsedValue*(1 shl 23) + .5f).toLong()
val (radix, shift) =
when {
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)
@@ -673,8 +649,7 @@ fun stringToFloat(string: String): ResValue? {
mantissa = (-mantissa) and ResValue.ComplexFormat.MANTISSA_MASK
}
val dataValue =
(entry.unitValue shl ResValue.ComplexFormat.UNIT_SHIFT) or
val dataValue = (entry.unitValue shl ResValue.ComplexFormat.UNIT_SHIFT) or
(radix shl ResValue.ComplexFormat.RADIX_SHIFT) or
(mantissa shl ResValue.ComplexFormat.MANTISSA_SHIFT)
@@ -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
@@ -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
@@ -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()) {
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<Span>()
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,7 +528,8 @@ 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()) {
@@ -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,7 +608,8 @@ fun deserializeMacroFromPb(
valuePool: StringPool,
config: ConfigDescription,
sourcePool: ResStringPool,
logger: ILogger?): Macro {
logger: ILogger?
): Macro {
val macro = Macro()
macro.rawValue = pbMacro.rawString
@@ -543,7 +626,8 @@ fun deserializeMacroFromPb(
for (untranslatable in pbMacro.untranslatableSectionsList) {
untranslatableList.add(
UntranslatableSection(
untranslatable.startIndex.toInt(), untranslatable.endIndex.toInt())
untranslatable.startIndex.toInt(), untranslatable.endIndex.toInt()
)
)
}
macro.untranslatables = untranslatableList
@@ -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<Overlayable>,
logger: ILogger?,
table: ResourceTable): Boolean {
table: ResourceTable
): Boolean {
val packageId =
if (original.hasPackageId()) original.getPackageId().getId().toByte() else 0.toByte()
val idIndex = MutableIntObjectMap<ResourceName>()
val idIndex = mutableIntObjectMapOf<ResourceName>()
val resourcePackage =
table.createPackageAllowingDuplicateNames(original.getPackageName(), packageId)
@@ -677,6 +772,7 @@ fun deserializePackageFromPb(
resourceGroup.visibility = ResourceVisibility.PRIVATE
}
}
else -> {}
}
@@ -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<Overlayable>()
@@ -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)
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()
tableBuilder.addToolFingerprint(
Resources.ToolFingerprint.newBuilder()
.setTool(ToolFingerprint.TOOL_NAME)
.setVersion(ToolFingerprint.FINGERPRINT)
.build())
.build()
)
val overlayables = mutableListOf<Overlayable>()
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) {
@@ -162,6 +178,7 @@ fun serializeVisibilityToPb(resourceVisibility: ResourceVisibility) =
when (resourceVisibility) {
ResourceVisibility.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<Overlayable>,
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,20 +731,20 @@ 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 styleString = macroBuilder.styleStringBuilder
val styleString = Resources.StyleString.newBuilder()
styleString.str = macro.styleString!!.str
for (span in macro.styleString!!.spans) {
val spansBuilder = styleString.addSpansBuilder()
val spansBuilder = Resources.StyleString.Span.newBuilder()
spansBuilder.name = span.name
spansBuilder.startIndex = span.firstChar
spansBuilder.endIndex = span.lastChar
@@ -653,14 +753,14 @@ fun serializeMacroToPb(macro: Macro): Resources.MacroBody {
macroBuilder.styleString = styleString.build()
for (untranslatables in macro.untranslatables) {
val section = macroBuilder.addUntranslatableSectionsBuilder()
val section = Resources.UntranslatableSection.newBuilder()
section.startIndex = untranslatables.startIndex.toLong()
section.endIndex = untranslatables.endIndex.toLong()
macroBuilder.addUntranslatableSections(section.build())
}
for (aliasNamespace in macro.aliasNamespaces) {
val namespace = macroBuilder.addNamespaceStackBuilder()
val namespace = Resources.NamespaceAlias.newBuilder()
namespace.prefix = aliasNamespace.alias
namespace.packageName = aliasNamespace.packageName
namespace.isPrivate = aliasNamespace.isPrivate
@@ -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;
}
@@ -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.
@@ -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 <overlayable> 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 <public> 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 <overlayable>.
string name = 1;
// The location of the <overlayable> declaration in the source.
Source source = 2;
// The component responsible for enabling and disabling overlays targeting this <overlayable>.
string actor = 3;
}
// Represents an overlayable <item> declaration within an <overlayable> 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 <item> declaration in source.
Source source = 1;
// Any comment associated with the declaration.
string comment = 2;
// The policy defined by the enclosing <policy> tag of this <item>.
repeated Policy policy = 3;
// The index into overlayable list that points to the <overlayable> tag that contains
// this <item>.
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 <declare-styleable> 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 <declare-styleable> 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 <element> 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;
}
@@ -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;
}