From 1a8735157e38336917b993b9c8b745c727f46f3b Mon Sep 17 00:00:00 2001 From: yanglw Date: Sun, 15 Feb 2026 02:39:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(core.gradle-plugin):=20=E6=94=AF=E6=8C=81A?= =?UTF-8?q?GP=208=E5=90=8E=E5=BC=80=E5=90=AFshrinkResources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当在 AGP 8.9.0 版本中开启 shrinkResources 功能后,ap_ 文件中的 AndroidManifest.xml 为新的二进制格式,且资源文件引用也不会改为 ID 。新的方案使用合并后的 AndroidManifest.xml 和 R.txt 生成 PluginManifest 文件。 新的方案自动应用在 AGP 8 且开启shrinkResources时。 --- .../tencent/shadow/core/gradle/AGPCompat.kt | 4 + .../shadow/core/gradle/AGPCompatImpl.kt | 103 ++++++++++++++++ .../shadow/core/gradle/ShadowPlugin.kt | 92 ++++++++++++--- .../core/manifest_parser/ManifestParser.kt | 67 ++++++++++- .../PluginManifestGenerator.kt | 111 ++++++++++++++++-- .../stub-project/build.gradle | 17 ++- .../stub-project/proguard-rules.pro | 1 + 7 files changed, 363 insertions(+), 32 deletions(-) create mode 100644 projects/test/gradle-plugin-agp-compat-test/stub-project/proguard-rules.pro diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt index e527b5e58..5f7998ec5 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompat.kt @@ -22,6 +22,7 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.ApplicationVariant import com.android.build.gradle.api.BaseVariantOutput import com.android.build.gradle.internal.dsl.ProductFlavor +import org.gradle.api.Project import org.gradle.api.Task import java.io.File @@ -36,4 +37,7 @@ internal interface AGPCompat { fun getAaptAdditionalParameters(processResourcesTask: Task): List fun getMinSdkVersion(pluginVariant: ApplicationVariant): Int fun hasDeprecatedTransformApi(): Boolean + fun getProcessManifestTask(output: BaseVariantOutput): Task + fun getProcessManifestFile(project: Project, pluginVariant: ApplicationVariant, output: BaseVariantOutput): File + fun getRTxtFile(project: Project, processResourcesTask: Task?, variantName: String): File } \ No newline at end of file diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt index a12f8846c..ea1afefce 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/AGPCompatImpl.kt @@ -8,6 +8,7 @@ import com.android.build.gradle.internal.dsl.ProductFlavor import com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask import com.android.build.gradle.internal.scope.InternalArtifactType import com.android.sdklib.AndroidVersion.VersionCodes +import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty @@ -144,6 +145,108 @@ internal class AGPCompatImpl : AGPCompat { return true } + /** + * 获取生成最终 AndroidManifest.xml 文件的任务。 + */ + override fun getProcessManifestTask(output: BaseVariantOutput): Task { + return try { + output.processManifestProvider.get() + } catch (_: Error) { + output.processManifest + } + } + + /** + * 获取合并后的 AndroidManifest.xml 文件。 + * + * 优先从 processManifest 任务输出获取,否则搜索 intermediates 目录。 + */ + override fun getProcessManifestFile( + project: Project, + pluginVariant: ApplicationVariant, + output: BaseVariantOutput + ): File { + // 1. 优先从任务输出获取 + try { + output.processManifestProvider.get().outputs.files.files.forEach { + findFileByName(it, "AndroidManifest.xml")?.let { file -> return file } + } + } catch (_: Exception) { + // 忽略 + } + + val variantName = pluginVariant.name + + // 2. 搜索中间产物目录 + return listOf( + "intermediates/merged_manifests/$variantName", // AGP 4.x/7.x/8.x + "intermediates/manifests/full/$variantName", // AGP 3.x + ) + .map { File(project.buildDir, it) } + .first { + findFileByName(it, "AndroidManifest.xml") != null + } + } + + /** + * 获取 R.txt 文件。 + * + * 优先从 processResources 任务的输出获取(最准确), 否则搜索 intermediates 目录。 + */ + override fun getRTxtFile( + project: Project, + processResourcesTask: Task?, + variantName: String + ): File { + // 1. 优先尝试从任务输出中查找 + if (processResourcesTask != null) { + try { + processResourcesTask.outputs.files.files.forEach { + findFileByName(it, "R.txt")?.let { file -> return file } + } + } catch (_: Exception) { + // 忽略解析错误,继续走备选路径 + } + } + + // 2. 根据 AGP 版本已知的中间产物路径搜索 + return listOf( + "intermediates/runtime_symbol_list/$variantName", // AGP 4.x/7.x/8.x + "intermediates/symbols/$variantName", + "intermediates/bundles/$variantName" + ) + .map { File(project.buildDir, it) } + .first { + findFileByName(it, "R.txt") != null + } + } + + /** + * 搜索指定目录下指定文件名的文件。 + * + * @return 文件对象,若找不到则返回 null 。 + */ + private fun findFileByName(file: File, fileName:String): File? { + if (!file.exists()) { + return null + } + if (file.isFile && file.name == fileName) { + return file + } + if (file.isDirectory) { + val subFiles = file.listFiles() + if (subFiles != null) { + for (subFile in subFiles) { + val resultFile = findFileByName(subFile, fileName) + if (resultFile != null) { + return resultFile + } + } + } + } + return null + } + companion object { fun getStringFromProperty(x: Any?): String { return when (x) { diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt index 1f54a2685..e49600af8 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt @@ -26,6 +26,7 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.ApplicationVariant import com.android.sdklib.AndroidVersion.VersionCodes import com.tencent.shadow.core.gradle.extensions.PackagePluginExtension +import com.tencent.shadow.core.manifest_parser.createResourceMapper import com.tencent.shadow.core.manifest_parser.generatePluginManifest import com.tencent.shadow.core.transform.DeprecatedTransformWrapper import com.tencent.shadow.core.transform.GradleTransformWrapper @@ -33,6 +34,7 @@ import com.tencent.shadow.core.transform.ShadowTransform import com.tencent.shadow.core.transform_kit.AndroidClassPoolBuilder import com.tencent.shadow.core.transform_kit.ClassPoolBuilder import org.gradle.api.* +import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.compile.JavaCompile import java.io.File import java.net.URLClassLoader @@ -103,8 +105,9 @@ class ShadowPlugin : Plugin { initAndroidClassPoolBuilder(baseExtension, project) createPackagePluginTasks(project) - - addLocateApkanalyzerTask(project) + if (agpCompat.hasDeprecatedTransformApi()) { + addLocateApkanalyzerTask(project) + } onEachPluginVariant(project) { pluginVariant -> checkAaptPackageIdConfig(pluginVariant) @@ -214,6 +217,77 @@ class ShadowPlugin : Plugin { appExtension: AppExtension, pluginVariant: ApplicationVariant ) { + val variantName = pluginVariant.name + val capitalizeVariantName = variantName.capitalize() + + // 添加生成PluginManifest.java任务 + val pluginManifestSourceDir = + File(project.buildDir, "generated/source/pluginManifest/$variantName") + + val javacTask = project.tasks.getByName("compile${capitalizeVariantName}JavaWithJavac") + + // AGP 4.0.0 不支持下面的写法,转成 ()-> TaskProvider? 再包一层的实现。 + // val func = if (agpCompat.hasDeprecatedTransformApi()) this::createGeneratePluginManifestTaskOld else this::createGeneratePluginManifestTaskNew + // val generatePluginManifestTask = func.invoke(project, pluginVariant, pluginManifestSourceDir) + val func: () -> TaskProvider? = if (agpCompat.hasDeprecatedTransformApi()) { + { + createGeneratePluginManifestTaskOld(project, pluginVariant, pluginManifestSourceDir) + } + } else { + { + createGeneratePluginManifestTaskNew(project, pluginVariant, pluginManifestSourceDir) + } + } + val generatePluginManifestTask = func.invoke() + javacTask.dependsOn(generatePluginManifestTask) + + // 把PluginManifest.java添加为源码 + val relativePath = + project.projectDir.toPath().relativize(pluginManifestSourceDir.toPath()).toString() + (javacTask as JavaCompile).source(project.fileTree(relativePath)) + } + + private fun createGeneratePluginManifestTaskNew( + project: Project, + pluginVariant: ApplicationVariant, + pluginManifestSourceDir: File, + ): TaskProvider? { + val output = pluginVariant.outputs.first() + + val variantName = pluginVariant.name + val capitalizeVariantName = variantName.capitalize() + + return project.tasks.register("generate${capitalizeVariantName}PluginManifest") { + // 依赖 processManifest 任务以获取最终的 AndroidManifest.xml + val processManifestTask = agpCompat.getProcessManifestTask(output) + // 依赖 processResources 任务以获取 R.txt + val processResourcesTask = agpCompat.getProcessResourcesTask(output) + it.dependsOn(processManifestTask) + it.dependsOn(processResourcesTask) + + it.outputs.dir(pluginManifestSourceDir).withPropertyName("pluginManifestSourceDir") + + it.doLast { + // 解析合并后的 AndroidManifest.xml + R.txt + // 这种方案直接解析 XML 格式的 AndroidManifest.xml ,不再依赖 aapt2 产生的二进制产物 + val mergedManifest = agpCompat.getProcessManifestFile(project, pluginVariant, output) + val rTxt = agpCompat.getRTxtFile(project, processResourcesTask, variantName) + val resourceMapper = createResourceMapper(rTxt) + generatePluginManifest( + mergedManifest, + pluginManifestSourceDir, + "com.tencent.shadow.core.manifest_parser", + resourceMapper + ) + } + } + } + + private fun createGeneratePluginManifestTaskOld( + project: Project, + pluginVariant: ApplicationVariant, + pluginManifestSourceDir: File, + ): TaskProvider? { val output = pluginVariant.outputs.first() val variantName = pluginVariant.name @@ -249,12 +323,7 @@ class ShadowPlugin : Plugin { } } - - // 添加生成PluginManifest.java任务 - val pluginManifestSourceDir = - File(project.buildDir, "generated/source/pluginManifest/$variantName") - val generatePluginManifestTask = - project.tasks.register("generate${capitalizeVariantName}PluginManifest") { + return project.tasks.register("generate${capitalizeVariantName}PluginManifest") { it.dependsOn(decodeBinaryManifestTask) it.inputs.file(decodeXml) it.outputs.dir(pluginManifestSourceDir).withPropertyName("pluginManifestSourceDir") @@ -267,13 +336,6 @@ class ShadowPlugin : Plugin { ) } } - val javacTask = project.tasks.getByName("compile${capitalizeVariantName}JavaWithJavac") - javacTask.dependsOn(generatePluginManifestTask) - - // 把PluginManifest.java添加为源码 - val relativePath = - project.projectDir.toPath().relativize(pluginManifestSourceDir.toPath()).toString() - (javacTask as JavaCompile).source(project.fileTree(relativePath)) } /** diff --git a/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/ManifestParser.kt b/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/ManifestParser.kt index 0b389ce2e..3829ca506 100644 --- a/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/ManifestParser.kt +++ b/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/ManifestParser.kt @@ -1,6 +1,7 @@ package com.tencent.shadow.core.manifest_parser import java.io.File +import java.util.Collections /** * manifest-parser的入口方法 @@ -9,13 +10,73 @@ import java.io.File * 一般位于apk工程的build/intermediates/merged_manifest目录中。 * @param outputDir 生成文件的输出目录 * @param packageName 生成类的包名 + * @param resourceMapper 资源映射器 */ fun generatePluginManifest( xmlFile: File, outputDir: File, - packageName: String + packageName: String, + resourceMapper: ((String) -> String)? = null ) { val androidManifest = AndroidManifestReader().read(xmlFile) val generator = PluginManifestGenerator() - generator.generate(androidManifest, outputDir, packageName) -} \ No newline at end of file + generator.generate(androidManifest, outputDir, packageName, resourceMapper) +} + +/** + * 创建资源映射器。 + * + * @param rTxt R.txt文件 + * @return 资源映射器 + */ +fun createResourceMapper(rTxt: File): (String) -> String { + val rTxtMap = parseRTxt(rTxt) + + return { resName -> + if (resName.startsWith("@android:")) { + // @android:style/Theme.NoTitleBar -> android.R.style.Theme_NoTitleBar + val parts = resName.substringAfter("@android:").split("/") + val type = parts[0] + val name = parts[1].replace(".", "_") + "android.R.$type.$name" + } else { + // @[package:]type/name -> id 值 + var raw = resName.substringAfter("@") + if (raw.contains(":")) { + raw = raw.substringAfter(":") + } + val parts = raw.split("/") + val type = parts[0] + val name = parts[1].replace('.', '_') + val key = "@$type/$name" + rTxtMap[key] ?: throw IllegalArgumentException("Resource not found in R.txt: $resName (normalized: $key)") + } + } +} + +/** + * 解析 R.txt 文件并生成资源 ID 映射表。 R.txt 包含项目引用的所有资源 ID。 + * + * @param rTxtFile R.txt 文件对象 + * @return 资源全称(如 @string/app_name)到 ID 的映射 + */ +fun parseRTxt(rTxtFile: File): Map { + if (!rTxtFile.exists()) return Collections.emptyMap() + + val map = mutableMapOf() + rTxtFile.useLines { + it.forEach { line -> + if (!(line.startsWith("int "))) { + return@forEach + } + val parts = line.split(Regex("\\s+")).filter { it.isNotBlank() } + if (parts.size == 4 && parts[0] == "int") { + val type = parts[1] + val name = parts[2] + val idStr = parts[3] + map["@$type/$name"] = idStr + } + } + } + return map +} diff --git a/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/PluginManifestGenerator.kt b/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/PluginManifestGenerator.kt index 0a84018c3..b57d5d6af 100644 --- a/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/PluginManifestGenerator.kt +++ b/projects/sdk/core/manifest-parser/src/main/kotlin/com/tencent/shadow/core/manifest_parser/PluginManifestGenerator.kt @@ -3,7 +3,6 @@ package com.tencent.shadow.core.manifest_parser import com.squareup.javapoet.* import com.tencent.shadow.core.runtime.PluginManifest import java.io.File -import java.util.* import javax.lang.model.element.Modifier /** @@ -21,9 +20,15 @@ class PluginManifestGenerator { * @param manifestMap AndroidManifestReader#read的输出Map * @param outputDir 生成文件的输出目录 * @param packageName 生成类的包名 + * @param resourceMapper 资源映射器。用于将资源名称映射为资源 ID 值 */ - fun generate(manifestMap: ManifestMap, outputDir: File, packageName: String) { - val pluginManifestBuilder = PluginManifestBuilder(manifestMap) + fun generate( + manifestMap: ManifestMap, + outputDir: File, + packageName: String, + resourceMapper: ((String) -> String)? = null + ) { + val pluginManifestBuilder = PluginManifestBuilder(manifestMap, resourceMapper) val pluginManifest = pluginManifestBuilder.build() JavaFile.builder(packageName, pluginManifest) .build() @@ -31,7 +36,10 @@ class PluginManifestGenerator { } } -private class PluginManifestBuilder(val manifestMap: ManifestMap) { +private class PluginManifestBuilder( + val manifestMap: ManifestMap, + val resourceMapper: ((String) -> String)? = null +) { val classBuilder: TypeSpec.Builder = TypeSpec.classBuilder("PluginManifest") .addSuperinterface(ClassName.get(PluginManifest::class.java)) @@ -172,7 +180,7 @@ private class PluginManifestBuilder(val manifestMap: ManifestMap) { manifestValue: Any, ): FieldSpec { - val resIdLiteral = themeStringToResId(manifestValue) + val resIdLiteral = themeStringToResId(manifestValue, resourceMapper) return privateStaticFinalIntFieldBuilder(fieldName) .initializer( CodeBlock.of("$1L", resIdLiteral) @@ -196,17 +204,17 @@ private class PluginManifestBuilder(val manifestMap: ManifestMap) { } val themeLiteral = makeResIdLiteral(AndroidManifestKeys.theme) { - themeStringToResId(it) + themeStringToResId(it, resourceMapper) } val configChangesLiteral = makeResIdLiteral(AndroidManifestKeys.configChanges) { - it + parseConfigChanges(it) } val softInputModeLiteral = makeResIdLiteral(AndroidManifestKeys.windowSoftInputMode) { - it + parseSoftInputMode(it) } val screenOrientation = makeResIdLiteral(AndroidManifestKeys.screenOrientation, "-1") { - it + parseScreenOrientation(it) } return "new com.tencent.shadow.core.runtime.PluginManifest" + @@ -279,14 +287,91 @@ private class PluginManifestBuilder(val manifestMap: ManifestMap) { fun nullCodeBlock() = CodeBlock.of("null")!! - fun themeStringToResId(manifestValue: Any): String { + fun themeStringToResId(manifestValue: Any, resourceMapper: ((String) -> String)? = null): String { val formatValue = manifestValue as String // for example: @ref/0x7e0b009e if (formatValue.startsWith("@ref/")) { return formatValue.removePrefix("@ref/") - } else { - // 其余格式:https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:apkparser/analyzer/src/main/java/com/android/tools/apk/analyzer/BinaryXmlParser.java;l=193 - throw TODO("不支持其他格式") + } else if (formatValue.startsWith("@")) { + // 对于 AGP 8 以及以上版本,resourceMapper 不会为 null 。 + // 对于 AGP 8 以下版本解析的 ap_ 文件中的 AndroidManifest.xml ,不会出现以 @ 打头却不是 @ref 的情况。 + if (resourceMapper != null) { + // @style/Theme.AppCompat --> id 值 + return resourceMapper.invoke(formatValue) + } + } + // 其余格式:https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:apkparser/analyzer/src/main/java/com/android/tools/apk/analyzer/BinaryXmlParser.java;l=193 + throw TODO("不支持其他格式: $formatValue") + } + + fun parseConfigChanges(value: String): String { + if (value.startsWith("0x") || value.toIntOrNull() != null) return value + return value.split("|").joinToString("|") { + val constant = when (it) { + "mcc" -> "CONFIG_MCC" + "mnc" -> "CONFIG_MNC" + "locale" -> "CONFIG_LOCALE" + "touchscreen" -> "CONFIG_TOUCHSCREEN" + "keyboard" -> "CONFIG_KEYBOARD" + "keyboardHidden" -> "CONFIG_KEYBOARD_HIDDEN" + "navigation" -> "CONFIG_NAVIGATION" + "orientation" -> "CONFIG_ORIENTATION" + "screenLayout" -> "CONFIG_SCREEN_LAYOUT" + "uiMode" -> "CONFIG_UI_MODE" + "screenSize" -> "CONFIG_SCREEN_SIZE" + "smallestScreenSize" -> "CONFIG_SMALLEST_SCREEN_SIZE" + "density" -> "CONFIG_DENSITY" + "layoutDirection" -> "CONFIG_LAYOUT_DIRECTION" + "fontScale" -> "CONFIG_FONT_SCALE" + "colorMode" -> "CONFIG_COLOR_MODE" // Added in API 26 + else -> throw IllegalArgumentException("Unknown configChanges: $it") + } + "android.content.pm.ActivityInfo.$constant" + } + } + + fun parseSoftInputMode(value: String): String { + if (value.startsWith("0x") || value.toIntOrNull() != null) return value + return value.split("|").joinToString("|") { + val constant = when (it) { + "stateUnspecified" -> "SOFT_INPUT_STATE_UNSPECIFIED" + "stateUnchanged" -> "SOFT_INPUT_STATE_UNCHANGED" + "stateHidden" -> "SOFT_INPUT_STATE_HIDDEN" + "stateAlwaysHidden" -> "SOFT_INPUT_STATE_ALWAYS_HIDDEN" + "stateVisible" -> "SOFT_INPUT_STATE_VISIBLE" + "stateAlwaysVisible" -> "SOFT_INPUT_STATE_ALWAYS_VISIBLE" + "adjustUnspecified" -> "SOFT_INPUT_ADJUST_UNSPECIFIED" + "adjustResize" -> "SOFT_INPUT_ADJUST_RESIZE" + "adjustPan" -> "SOFT_INPUT_ADJUST_PAN" + "adjustNothing" -> "SOFT_INPUT_ADJUST_NOTHING" + "isForwardNavigation" -> "SOFT_INPUT_IS_FORWARD_NAVIGATION" + else -> throw IllegalArgumentException("Unknown windowSoftInputMode: $it") + } + "android.view.WindowManager.LayoutParams.$constant" + } + } + + fun parseScreenOrientation(value: String): String { + if (value.startsWith("0x") || value.toIntOrNull() != null) return value + val constant = when (value) { + "unspecified" -> "SCREEN_ORIENTATION_UNSPECIFIED" + "landscape" -> "SCREEN_ORIENTATION_LANDSCAPE" + "portrait" -> "SCREEN_ORIENTATION_PORTRAIT" + "user" -> "SCREEN_ORIENTATION_USER" + "behind" -> "SCREEN_ORIENTATION_BEHIND" + "sensor" -> "SCREEN_ORIENTATION_SENSOR" + "nosensor" -> "SCREEN_ORIENTATION_NOSENSOR" + "sensorLandscape" -> "SCREEN_ORIENTATION_SENSOR_LANDSCAPE" + "sensorPortrait" -> "SCREEN_ORIENTATION_SENSOR_PORTRAIT" + "reverseLandscape" -> "SCREEN_ORIENTATION_REVERSE_LANDSCAPE" + "reversePortrait" -> "SCREEN_ORIENTATION_REVERSE_PORTRAIT" + "fullSensor" -> "SCREEN_ORIENTATION_FULL_SENSOR" + "userLandscape" -> "SCREEN_ORIENTATION_USER_LANDSCAPE" + "userPortrait" -> "SCREEN_ORIENTATION_USER_PORTRAIT" + "fullUser" -> "SCREEN_ORIENTATION_FULL_USER" + "locked" -> "SCREEN_ORIENTATION_LOCKED" + else -> throw IllegalArgumentException("Unknown screenOrientation: $value") } + return "android.content.pm.ActivityInfo.$constant" } } } diff --git a/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle b/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle index 4aaacabea..3206b7499 100644 --- a/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle +++ b/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle @@ -53,7 +53,13 @@ android { // AGP 8之前找不到namespace这个方法的,不用设置。 } - compileSdkVersion COMPILE_SDK_VERSION + // 对于 3.4.0 以及以下版本,compileSdkVersion 不能为 SDK 33 。 + // 因为 SDK 33 中的 android.car.jar 是由 JDK 11 编译的。而这些版本 AGP 中的 R8 不支持 JDK 11 。 + if (TestAGPVersion.matches("3\\.[1234]\\.0")) { + compileSdkVersion 32 + } else { + compileSdkVersion COMPILE_SDK_VERSION + } defaultConfig { applicationId "com.tencent.shadow.test.gradle.stub_project" @@ -63,6 +69,15 @@ android { versionName VERSION_NAME } + buildTypes { + debug { + minifyEnabled true + shrinkResources true + + proguardFiles 'proguard-rules.pro' + } + } + // 测试插件项目存在自定义flavorDimensions flavorDimensions(*flavorDimensionList, 'DimensionA', 'DimensionB') productFlavors { diff --git a/projects/test/gradle-plugin-agp-compat-test/stub-project/proguard-rules.pro b/projects/test/gradle-plugin-agp-compat-test/stub-project/proguard-rules.pro new file mode 100644 index 000000000..8a9f0f179 --- /dev/null +++ b/projects/test/gradle-plugin-agp-compat-test/stub-project/proguard-rules.pro @@ -0,0 +1 @@ +-keep class PluginManifestIncludeTest \ No newline at end of file