diff --git a/README.md b/README.md index 27049d39..ab36f366 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,20 @@ embed('com.facebook.fresco:fresco:1.11.0') { **More usage see [example](./example).** +### Resource prefixing (experimental) +You can enable prefixing of library resources. This can be useful if your fat-aar has a lot of resources that cannot be renamed at the source level. The plugin will add the specified prefix to all internal library resources during build time. + +```groovy +fataar { + + /** + * Add prefix for all library resources. To prevent conflicts with a library clients. + * @since 1.3.7 + */ + resourcePrefix = "lib_main_" +} +``` + ## About AAR File AAR is a file format for android library. diff --git a/example/app/libs/fat-aar-final.aar b/example/app/libs/fat-aar-final.aar index b23a3154..b40bde33 100644 Binary files a/example/app/libs/fat-aar-final.aar and b/example/app/libs/fat-aar-final.aar differ diff --git a/example/app/src/main/java/com/fataar/demo/MainActivity.java b/example/app/src/main/java/com/fataar/demo/MainActivity.java index a1591236..66876e09 100644 --- a/example/app/src/main/java/com/fataar/demo/MainActivity.java +++ b/example/app/src/main/java/com/fataar/demo/MainActivity.java @@ -7,6 +7,7 @@ import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; @@ -30,13 +31,16 @@ import com.kezong.demo.libaar.AarFlavor; import com.kezong.demo.libaar.AarLibClass; import com.kezong.demo.libaar.KotlinTest2; +import com.kezong.demo.libaar.LibCountries; import com.kezong.demo.libaar.TestActivity; import com.kezong.demo.libaar2.Aar2LibClass; import com.kezong.demo.libaarlocal.AarLocalLibClass; import com.kezong.demo.libaarlocal2.AarLocal2LibClass; +import com.kezong.demo.lib.R.*; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; /** * @author kezong on 2020/12/11. @@ -65,6 +69,11 @@ protected void onCreate(Bundle savedInstanceState) { testAssetsMerge(); testRemoteAar(); testFlavor(); + testEnum(); + } + + private void testEnum() { + addTestView("enums", "lib flag ee", LibCountries.ESTONIA.getFlagRes() == R.drawable.lib_main_flag_ee); } private void testFlavor() { @@ -162,12 +171,17 @@ public void testKotlinTopLevel2() { } public void testResourceMerge() { + + for (Field field : R.string.class.getFields()) { + Log.d("R class name:", field.getName()); + } + String text = new AarLibClass().getLibName(this); - addTestView("resource", text, TextUtils.equals(text, "lib-aar")); + addTestView("resource", text, TextUtils.equals(text, "lib-aar eng")); } public void testResourceMerge2() { - String text = this.getResources().getString(R.string.app_name_aar2); + String text = this.getResources().getString(R.string.lib_main_app_name_aar2); addTestView("resource2", text, TextUtils.equals(text, "lib-aar2")); } diff --git a/example/build.gradle b/example/build.gradle index 338a6144..2540820e 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -7,8 +7,8 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:7.0.2' - classpath 'com.github.kezong:fat-aar:1.3.6' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72" + classpath 'com.github.kezong:fat-aar:1.3.7' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30" } } diff --git a/example/gradle.properties b/example/gradle.properties index d015431a..e4ed8faf 100644 --- a/example/gradle.properties +++ b/example/gradle.properties @@ -1,2 +1,4 @@ android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 + diff --git a/example/lib-aar/src/main/AndroidManifest.xml b/example/lib-aar/src/main/AndroidManifest.xml index 52c10332..80e7c2f2 100644 --- a/example/lib-aar/src/main/AndroidManifest.xml +++ b/example/lib-aar/src/main/AndroidManifest.xml @@ -7,6 +7,6 @@ + android:theme="@style/LibAarTheme" /> diff --git a/example/lib-aar/src/main/java/com/kezong/demo/libaar/AarLibClass.java b/example/lib-aar/src/main/java/com/kezong/demo/libaar/AarLibClass.java index bdf29fd9..6e8c569e 100644 --- a/example/lib-aar/src/main/java/com/kezong/demo/libaar/AarLibClass.java +++ b/example/lib-aar/src/main/java/com/kezong/demo/libaar/AarLibClass.java @@ -12,4 +12,8 @@ public class AarLibClass { public String getLibName(Context ctx) { return ctx.getResources().getString(R.string.app_name_aar); } + + public String getTestString(Context ctx) { + return ctx.getResources().getString(R.string.lib_aar_test_string1); + } } diff --git a/example/lib-aar/src/main/java/com/kezong/demo/libaar/LibCountries.kt b/example/lib-aar/src/main/java/com/kezong/demo/libaar/LibCountries.kt new file mode 100644 index 00000000..90634c09 --- /dev/null +++ b/example/lib-aar/src/main/java/com/kezong/demo/libaar/LibCountries.kt @@ -0,0 +1,25 @@ +package com.kezong.demo.libaar + +import android.content.Context +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import java.io.Serializable + +enum class LibCountries( + val code: String, + @StringRes val nameRes: Int, + @DrawableRes val flagRes: Int, + vararg phonePrefix: String, +) : Serializable { + + ESTONIA("ee", R.string.country_ee, R.drawable.flag_ee, "+397"), + USA("us", R.string.country_us, R.drawable.flag_us, "+1"); + + fun getName(context: Context): String = context.getString(nameRes) + + companion object { + + @JvmStatic + fun findByCode(countryCode: String?) = values().find { it.code.equals(countryCode, true) } + } +} \ No newline at end of file diff --git a/example/lib-aar/src/main/java/com/kezong/demo/libaar/TestActivity.java b/example/lib-aar/src/main/java/com/kezong/demo/libaar/TestActivity.java index 0618496e..330e1bab 100644 --- a/example/lib-aar/src/main/java/com/kezong/demo/libaar/TestActivity.java +++ b/example/lib-aar/src/main/java/com/kezong/demo/libaar/TestActivity.java @@ -4,6 +4,10 @@ import androidx.databinding.DataBindingUtil; import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; import com.kezong.demo.libaar.databinding.DatabindingBinding; @@ -13,11 +17,30 @@ public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { + setTheme(R.style.LibAarTheme_Main); super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.databinding); User user = new User(); user.setName("Hello World"); user.setSex("[success][dataBinding] male"); binding.setUser(user); + + for (LibCountries country : LibCountries.values()) { + binding.container.addView(getCountryView(country)); + } + } + + private View getCountryView(LibCountries country) { + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.HORIZONTAL); + TextView textView = new TextView(this); + textView.setText(country.getName(this)); + ImageView imageView = new ImageView(this); + imageView.setImageResource(country.getFlagRes()); + + layout.addView(textView); + layout.addView(imageView); + + return layout; } } diff --git a/example/lib-aar/src/main/res/drawable-hdpi/flag_ee.png b/example/lib-aar/src/main/res/drawable-hdpi/flag_ee.png new file mode 100644 index 00000000..facef6c7 Binary files /dev/null and b/example/lib-aar/src/main/res/drawable-hdpi/flag_ee.png differ diff --git a/example/lib-aar/src/main/res/drawable-hdpi/flag_us.png b/example/lib-aar/src/main/res/drawable-hdpi/flag_us.png new file mode 100644 index 00000000..310d216c Binary files /dev/null and b/example/lib-aar/src/main/res/drawable-hdpi/flag_us.png differ diff --git a/example/lib-aar/src/main/res/drawable/lib_background.xml b/example/lib-aar/src/main/res/drawable/lib_background.xml new file mode 100644 index 00000000..8659eb59 --- /dev/null +++ b/example/lib-aar/src/main/res/drawable/lib_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/example/lib-aar/src/main/res/layout/databinding.xml b/example/lib-aar/src/main/res/layout/databinding.xml index f4c534c9..3beb1c92 100644 --- a/example/lib-aar/src/main/res/layout/databinding.xml +++ b/example/lib-aar/src/main/res/layout/databinding.xml @@ -1,19 +1,29 @@ - + + diff --git a/example/lib-aar/src/main/res/layout/test_layout.xml b/example/lib-aar/src/main/res/layout/test_layout.xml index 8c1f1488..61a0028a 100644 --- a/example/lib-aar/src/main/res/layout/test_layout.xml +++ b/example/lib-aar/src/main/res/layout/test_layout.xml @@ -1,10 +1,14 @@ - + android:layout_height="match_parent" + android:orientation="horizontal"> + \ No newline at end of file diff --git a/example/lib-aar/src/main/res/values-en/strings.xml b/example/lib-aar/src/main/res/values-en/strings.xml new file mode 100644 index 00000000..53c557ff --- /dev/null +++ b/example/lib-aar/src/main/res/values-en/strings.xml @@ -0,0 +1,6 @@ + + + lib-aar eng + lib aar test string value eng + @string/lib_aar_test_string1 + \ No newline at end of file diff --git a/example/lib-aar/src/main/res/values/colors.xml b/example/lib-aar/src/main/res/values/colors.xml new file mode 100644 index 00000000..78f6bae4 --- /dev/null +++ b/example/lib-aar/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + @android:color/white + #ff0000 + \ No newline at end of file diff --git a/example/lib-aar/src/main/res/values/dimens.xml b/example/lib-aar/src/main/res/values/dimens.xml new file mode 100644 index 00000000..8119e451 --- /dev/null +++ b/example/lib-aar/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 10dp + @dimen/corner_radius + \ No newline at end of file diff --git a/example/lib-aar/src/main/res/values/strings.xml b/example/lib-aar/src/main/res/values/strings.xml index 87f8203e..b17082de 100644 --- a/example/lib-aar/src/main/res/values/strings.xml +++ b/example/lib-aar/src/main/res/values/strings.xml @@ -1,3 +1,7 @@ lib-aar + lib aar test string value + @string/lib_aar_test_string1 + Estonia + USA diff --git a/example/lib-aar/src/main/res/values/style.xml b/example/lib-aar/src/main/res/values/style.xml new file mode 100644 index 00000000..69ff03c7 --- /dev/null +++ b/example/lib-aar/src/main/res/values/style.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/example/lib-aar2/src/main/res/drawable/im_cat.jpg b/example/lib-aar2/src/main/res/drawable/im_cat.jpg new file mode 100644 index 00000000..ce05f88d Binary files /dev/null and b/example/lib-aar2/src/main/res/drawable/im_cat.jpg differ diff --git a/example/lib-aar2/src/main/res/drawable/simple_drawable.xml b/example/lib-aar2/src/main/res/drawable/simple_drawable.xml new file mode 100644 index 00000000..a8b409b1 --- /dev/null +++ b/example/lib-aar2/src/main/res/drawable/simple_drawable.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/example/lib-aar2/src/main/res/layout/simple_layout.xml b/example/lib-aar2/src/main/res/layout/simple_layout.xml new file mode 100644 index 00000000..0b2ce591 --- /dev/null +++ b/example/lib-aar2/src/main/res/layout/simple_layout.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/example/lib-aar2/src/main/res/values/colors.xml b/example/lib-aar2/src/main/res/values/colors.xml new file mode 100644 index 00000000..eb68020b --- /dev/null +++ b/example/lib-aar2/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + @android:color/black + @color/my_test_color + \ No newline at end of file diff --git a/example/lib-aar2/src/main/res/values/strings.xml b/example/lib-aar2/src/main/res/values/strings.xml index 605cddd8..f39fa94a 100644 --- a/example/lib-aar2/src/main/res/values/strings.xml +++ b/example/lib-aar2/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ lib-aar2 + lib string 1 diff --git a/example/lib-aar2/src/main/res/values/styles.xml b/example/lib-aar2/src/main/res/values/styles.xml new file mode 100644 index 00000000..f8ebf05d --- /dev/null +++ b/example/lib-aar2/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/example/lib-main/build.gradle b/example/lib-main/build.gradle index 28ba820b..7f2c9f50 100644 --- a/example/lib-main/build.gradle +++ b/example/lib-main/build.gradle @@ -10,11 +10,11 @@ repositories { } android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 31 versionCode 1 versionName "1.0.0" } @@ -44,6 +44,15 @@ android { exclude 'lib/armeabi/*.so' exclude 'lib/arm64-v8a/*.so' } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11 + } } afterEvaluate { @@ -71,6 +80,12 @@ fataar { * @since 1.3.0 */ transitive = true + + /** + * Add prefix for all library resources. To prevent conflicts with a library clients. + * @since 1.3.7 + */ + resourcePrefix = "lib_main_" } dependencies { diff --git a/source/gradle.properties b/source/gradle.properties index 0e311803..12dccc6c 100644 --- a/source/gradle.properties +++ b/source/gradle.properties @@ -1,3 +1,3 @@ PUBLISH_GROUP_ID=com.github.kezong PUBLISH_ARTIFACT_ID=fat-aar -PUBLISH_VERSION=1.3.6 +PUBLISH_VERSION=1.3.7 diff --git a/source/src/main/groovy/com/kezong/fataar/FatAarExtension.groovy b/source/src/main/groovy/com/kezong/fataar/FatAarExtension.groovy index 00e25716..432c9069 100644 --- a/source/src/main/groovy/com/kezong/fataar/FatAarExtension.groovy +++ b/source/src/main/groovy/com/kezong/fataar/FatAarExtension.groovy @@ -25,4 +25,10 @@ class FatAarExtension { * @since 1.3.0 */ boolean transitive = false + + /** + * Add prefix for all library resources. To prevent conflicts with a library clients. + * @since 1.3.7 + */ + String resourcePrefix = "" } diff --git a/source/src/main/groovy/com/kezong/fataar/VariantProcessor.groovy b/source/src/main/groovy/com/kezong/fataar/VariantProcessor.groovy index 63d71eda..8609e4b3 100755 --- a/source/src/main/groovy/com/kezong/fataar/VariantProcessor.groovy +++ b/source/src/main/groovy/com/kezong/fataar/VariantProcessor.groovy @@ -3,6 +3,8 @@ package com.kezong.fataar import com.android.build.gradle.api.LibraryVariant import com.android.build.gradle.internal.api.DefaultAndroidSourceSet import com.android.build.gradle.tasks.ManifestProcessorTask +import groovy.xml.XmlParser +import groovy.xml.XmlUtil import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.ResolvedArtifact @@ -35,6 +37,8 @@ class VariantProcessor { private Collection mExplodeTasks = new ArrayList<>() + private HashSet renamedResources = new HashSet() + private VersionAdapter mVersionAdapter private TaskProvider mMergeClassTask @@ -70,6 +74,7 @@ class VariantProcessor { } processManifest() processResources() + processResourcePrefixing() processAssets() processJniLibs() processConsumerProguard() @@ -79,7 +84,7 @@ class VariantProcessor { } private static void printEmbedArtifacts(Collection artifacts, - Collection dependencies) { + Collection dependencies) { Collection moduleNames = artifacts.stream().map { it.moduleVersion.id.name }.collect() dependencies.each { dependency -> if (!moduleNames.contains(dependency.moduleName)) { @@ -192,17 +197,19 @@ class VariantProcessor { private void transformRClasses(RClassesTransform transform, TaskProvider transformTask, TaskProvider bundleTask, TaskProvider reBundleTask) { transform.putTargetPackage(mVariant.name, mVariant.getApplicationId()) + transform.putResourcesPrefix(mProject.fataar.resourcePrefix) transformTask.configure { - doFirst { - // library package name parsed by aar's AndroidManifest.xml - // so must put after explode tasks perform. - Collection libraryPackages = mAndroidArchiveLibraries - .stream() - .map { it.packageName } - .collect() - transform.putLibraryPackages(mVariant.name, libraryPackages); - } - } + doFirst { + // library package name parsed by aar's AndroidManifest.xml + // so must put after explode tasks perform. + Collection libraryPackages = mAndroidArchiveLibraries + .stream() + .map { it.packageName } + .collect() + transform.putLibraryPackages(mVariant.name, libraryPackages); + transform.putRenamedResources(renamedResources) + } + } bundleTask.configure { finalizedBy(reBundleTask) } @@ -251,7 +258,7 @@ class VariantProcessor { static def getTaskDependency(ResolvedArtifact artifact) { try { return artifact.buildDependencies - } catch(MissingPropertyException ignore) { + } catch (MissingPropertyException ignore) { // since gradle 6.8.0, property is changed; return artifact.builtBy } @@ -313,13 +320,8 @@ class VariantProcessor { } } - /** - * merge manifest - */ - private void processManifest() { - ManifestProcessorTask processManifestTask = mVersionAdapter.getProcessManifest() - - File manifestOutput + private File getManifestOutputDir() { + def manifestOutput if (FatUtils.compareVersion(VersionAdapter.AGPVersion, "4.2.0-alpha07") >= 0) { manifestOutput = mProject.file("${mProject.buildDir.path}/intermediates/merged_manifest/${mVariant.name}/AndroidManifest.xml") } else if (FatUtils.compareVersion(VersionAdapter.AGPVersion, "3.3.0") >= 0) { @@ -328,6 +330,17 @@ class VariantProcessor { manifestOutput = mProject.file(processManifestTask.getManifestOutputDirectory().absolutePath + "/AndroidManifest.xml") } + return manifestOutput + } + + /** + * merge manifest + */ + private void processManifest() { + ManifestProcessorTask processManifestTask = mVersionAdapter.getProcessManifest() + + File manifestOutput = getManifestOutputDir() + final List inputManifests = new ArrayList<>() for (archiveLibrary in mAndroidArchiveLibraries) { inputManifests.add(archiveLibrary.getManifest()) @@ -346,6 +359,12 @@ class VariantProcessor { processManifestTask.doLast { // Merge manifests manifestsMergeTask.get().doTaskAction() + + String prefix = mProject.fataar.resourcePrefix + + if (prefix != null && !prefix.isEmpty()) { + modifyResourceUsage(getManifestOutputDir(), prefix) + } } } @@ -359,7 +378,7 @@ class VariantProcessor { if (kotlinCompile != null) { dependsOn(kotlinCompile) } - } catch(Exception ignore) { + } catch (Exception ignore) { } @@ -476,6 +495,7 @@ class VariantProcessor { if (sourceSet.name == mVariant.name) { for (archiveLibrary in mAndroidArchiveLibraries) { FatUtils.logInfo("Merge resource,Library res:${archiveLibrary.resFolder}") + sourceSet.res.srcDir(archiveLibrary.resFolder) } } @@ -483,6 +503,171 @@ class VariantProcessor { } } + private void processResourcePrefixing() { + + String prefix = mProject.fataar.resourcePrefix + + if (prefix.isEmpty()) { + println("[fat-aar] Resource prefix is empty. Skip resource modifying.") + return + } + + String preBuildPath = "package" + mVariant.name.capitalize() + "Resources" + TaskProvider packageResourcesTask = mProject.tasks.named(preBuildPath) + if (packageResourcesTask == null) { + throw new RuntimeException("Can not find task ${taskPath}!") + } + + packageResourcesTask.configure { + + doFirst { + def renamedResCount = 0 + + mProject.android.sourceSets.each { DefaultAndroidSourceSet sourceSet -> + if (sourceSet.name == mVariant.name) { + sourceSet.res.sourceFiles.files.each { resFile -> + renamedResCount += addPrefixByFile(resFile, prefix) + } + } + } + + mProject.android.sourceSets.each { DefaultAndroidSourceSet sourceSet -> + if (sourceSet.name == mVariant.name) { + sourceSet.res.sourceFiles.files.each { resFile -> + modifyResourceUsage(resFile, prefix) + } + } + } + + println("[fat-aar] Prefix was added to $renamedResCount resources") + } + } + } + + private int addPrefixByFile(File file, String prefix) { + def renamedResCount = 0 + renamedResources.add(file.name.split("\\.").first()) + + File newFile + if (!file.name.startsWith(prefix)) { + + String newPath = file.path.replace(file.name, prefix + file.name) + newFile = new File(newPath) + file.renameTo(newFile) + + renamedResCount++ + } else { + newFile = file + } + + if (newFile.name.endsWith(".xml")) { + renamedResCount += addPrefixForResources(newFile, prefix) + } + + return renamedResCount + } + + private int addPrefixForResources(File file, String prefix) { + + def renamedResCount = 0 + + if (prefix.isEmpty()) return renamedResCount + + def parser = new XmlParser() + def root = parser.parse(file) + def wasModified = false + + if (root.name() == "resources") { + root.each { resourceElement -> + String name = resourceElement.attribute("name") + + if (resourceTypes.contains(resourceElement.name()) + && name != null + && !name.isEmpty() + && !name.startsWith(prefix)) { + + renamedResources.add(name) + renamedResources.add(name.replace(".", "_")) + resourceElement.@name = prefix + name + renamedResCount++ + wasModified = true + } + } + } + + if (wasModified) { + file.withWriter { outWriter -> + XmlUtil.serialize(root, outWriter) + } + } + + return renamedResCount + } + + private void modifyResourceUsage(File file, String prefix) { + if (!file.name.endsWith(".xml")) return + + def parser = new XmlParser() + def root = parser.parse(file) + def wasModified = false + + wasModified = addPrefixForAllNodeResUsage(root, prefix) + if (wasModified) { + file.withWriter { outWriter -> + XmlUtil.serialize(root, outWriter) + } + } + } + + private boolean addPrefixForAllNodeResUsage(Node rootNode, String prefix) { + def wasModified = false + def nodeValue = rootNode.text() + + rootNode.attributes().each { attr -> + def isParentStyle = attr.key == "parent" && renamedResources.contains(attr.value) + + if (isParentStyle || usesInternalResource(attr.value)) { + attr.value = addPrefixForResourceUsage(attr.value, prefix) + wasModified = true + } + } + + if (usesInternalResource(nodeValue)) { + rootNode.setValue(addPrefixForResourceUsage(nodeValue, prefix)) + wasModified = true + } else { + rootNode.each { subNode -> + if (subNode instanceof Node) + wasModified = addPrefixForAllNodeResUsage(subNode, prefix) || wasModified + } + } + return wasModified + } + + private boolean usesInternalResource(String tagValue) { + def splitedName = tagValue.split("/") + def resType = splitedName[0] + if (resType != null && !resType.isEmpty() && resType.startsWith("@")) { + + def key = resType.replace("@", "") + .replace("+", "") + .toLowerCase() + + return renamedResources.contains(splitedName.last()) && resourceTypes.contains(key) + } + return false + } + + private static String addPrefixForResourceUsage(String resRef, String prefix) { + def resPath = resRef.split("/") + def resName = resPath.last() + return resRef.replace(resName, prefix + resName) + } + + private static Set resourceTypes = new HashSet(Arrays.asList("anim", "style", "animator", "array", "bool", "color", "dimen", + "drawable", "font", "fraction", "id", "integer", "interpolator", "layout", "menu", "mipmap", "navigation", + "plurals", "raw", "string", "styleable", "transition", "xml")) // except attr + /** * merge assets * @@ -578,7 +763,7 @@ class VariantProcessor { try { String mergeName = 'merge' + mVariant.name.capitalize() + 'GeneratedProguardFiles' mergeGenerateProguardTask = mProject.tasks.named(mergeName) - } catch(Exception ignore) { + } catch (Exception ignore) { return } diff --git a/source/src/main/java/com/kezong/fataar/RClassesTransform.java b/source/src/main/java/com/kezong/fataar/RClassesTransform.java index c21a72d0..a2f33820 100644 --- a/source/src/main/java/com/kezong/fataar/RClassesTransform.java +++ b/source/src/main/java/com/kezong/fataar/RClassesTransform.java @@ -15,12 +15,16 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -59,6 +63,10 @@ public class RClassesTransform extends Transform { private final Map> libraryPackageMap = new HashMap<>(); + private String resourcePrefix = null; + + private HashSet renamedResources = null; + public RClassesTransform(final Project project) { this.project = project; } @@ -66,16 +74,26 @@ public RClassesTransform(final Project project) { /** * Different variants have different package names. * So targetPackageName must set after evaluate - * @param variantName variant name + * + * @param variantName variant name * @param targetPackage main module's package name */ public void putTargetPackage(String variantName, String targetPackage) { targetPackageMap.put(variantName, targetPackage); } + public void putResourcesPrefix(String prefix) { + this.resourcePrefix = prefix; + } + + public void putRenamedResources(HashSet renamedResources) { + this.renamedResources = renamedResources; + } + /** * library packages name must set after exploded task perform - * @param variantName variant name + * + * @param variantName variant name * @param libraryPackages sub module's package name, read from AndroidManifest.xml */ public void putLibraryPackages(String variantName, Collection libraryPackages) { @@ -136,7 +154,9 @@ public void transform(TransformInvocation transformInvocation) throws Interrupte ClassFile classFile = ctClass.getClassFile(); ConstPool constPool = classFile.getConstPool(); constPool.renameClass(transformTable); + addPrefixForResourceReads(constPool, resourcePrefix, transformTable.values()); } + ctClass.writeFile(outputDir.getAbsolutePath()); } catch (CannotCompileException | NotFoundException | IOException e) { e.printStackTrace(); @@ -168,6 +188,55 @@ public void transform(TransformInvocation transformInvocation) throws Interrupte + "ms"); } + private void addPrefixForResourceReads(ConstPool constPool, String resourcePrefix, Collection libRClasses) { + + if (resourcePrefix == null || resourcePrefix.isEmpty()) return; + + final List resourceTypes = Arrays.asList("anim", "animator", "array", "bool", "color", "dimen", + "drawable", "font", "fraction", "id", "integer", "interpolator", "layout", "menu", "mipmap", "navigation", + "plurals", "raw", "string", "styleable", "transition", "xml", "style"); // except, attr + + for (int i = 1; i < constPool.getSize(); i++) { + try { + String constClassName = constPool.getFieldrefClassName(i).replace(".", "/"); + + if (libRClasses.contains(constClassName) + && resourceTypes.contains(constClassName.split("\\$")[1])) { + + String name = constPool.getFieldrefName(i); + String newName = resourcePrefix + name; + constPool.renameClass(name, newName); + int nameId = constPool.getNameAndTypeName(constPool.getFieldrefNameAndType(i)); + + try { + Method getItemMethod = constPool.getClass() + .getDeclaredMethod("getItem", int.class); + getItemMethod.setAccessible(true); + + Object utf8Info = getItemMethod.invoke(constPool, nameId); + + Field classPoolUtf8NameField = utf8Info.getClass() + .getDeclaredField("string"); + + classPoolUtf8NameField.setAccessible(true); + String classPoolResourceName = (String) classPoolUtf8NameField.get(utf8Info); + + if (renamedResources.contains(classPoolResourceName)) { + classPoolUtf8NameField.set(utf8Info, newName); + } + } catch (IllegalAccessException + | InvocationTargetException + | NoSuchMethodException + | NoSuchFieldException e) { + e.printStackTrace(); + } + } + } catch (ClassCastException e) { + // ignore this + } + } + } + private Map buildTransformTable(String variantName) { String targetPackage = targetPackageMap.get(variantName); Collection libraryPackages = libraryPackageMap.get(variantName); diff --git a/source/upload.gradle b/source/upload.gradle index 03cbfd67..af831ab0 100644 --- a/source/upload.gradle +++ b/source/upload.gradle @@ -105,6 +105,7 @@ publishing { } } } + signing { sign publishing.publications } \ No newline at end of file