From 563ec417e2e34be007093504faaa72b646c94c8b Mon Sep 17 00:00:00 2001 From: guidongqi Date: Fri, 8 Nov 2019 23:00:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=94=AF=E6=8C=81androidx-v1.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +- gradle.properties | 4 +- library/build.gradle | 2 +- plugin/build.gradle | 4 +- .../plugin/MultiLanguagesTransform.groovy | 12 +- .../plugin/PluginExtension.groovy | 2 +- .../plugin/ActivityServiceClassVisitor.java | 84 ++++++++++++-- .../plugin/ApplyOverrideConfigurationMV.java | 25 +++++ .../plugin/MethodVisitorUtil.java | 106 ++++++++++++++++-- 9 files changed, 219 insertions(+), 25 deletions(-) create mode 100644 plugin/src/main/java/com/github/jokar/multilanguages/plugin/ApplyOverrideConfigurationMV.java diff --git a/build.gradle b/build.gradle index 36ede65..67e96a7 100644 --- a/build.gradle +++ b/build.gradle @@ -7,11 +7,12 @@ buildscript { jcenter() mavenCentral() maven {url 'https://dl.bintray.com/a10188755550/maven'} + } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.novoda:bintray-release:0.9.1' - classpath 'com.github.jokar:multi-languages.plugin:0.0.5' + classpath "com.github.jokar:multi-languages.plugin:0.0.5" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle.properties b/gradle.properties index 0d9492a..1cb8512 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,4 +11,6 @@ org.gradle.jvmargs=-Xmx1536m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -android.enableD8=true \ No newline at end of file +android.enableD8=true + +version=0.0.6 \ No newline at end of file diff --git a/library/build.gradle b/library/build.gradle index 0ed0740..823808d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -33,7 +33,7 @@ publish { userOrg = 'a10188755550' groupId = 'com.github.jokar' artifactId = 'multi-languages' - publishVersion = '0.0.5' + publishVersion = "$version" desc = 'android multi language support (support android O+)' website = 'https://github.com/MichaelJokAr/MultiLanguages' } \ No newline at end of file diff --git a/plugin/build.gradle b/plugin/build.gradle index ae4880b..1cd9f62 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -21,7 +21,7 @@ publish { userOrg = 'a10188755550' groupId = 'com.github.jokar' artifactId = 'multi-languages.plugin' - publishVersion = '0.0.5' + publishVersion = "$version" desc = 'android multi language support (support android O+)' website = 'https://github.com/MichaelJokAr/MultiLanguages' -} \ No newline at end of file +} diff --git a/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/MultiLanguagesTransform.groovy b/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/MultiLanguagesTransform.groovy index 7e02565..c6602ec 100644 --- a/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/MultiLanguagesTransform.groovy +++ b/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/MultiLanguagesTransform.groovy @@ -123,13 +123,19 @@ class MultiLanguagesTransform extends Transform { private static void addAttachMethod(ActivityServiceClassVisitor cv, name, ClassWriter classWriter) { if (cv.needAddAttach()) { println("add attach method to ${name}") - if(cv.activity) { + //添加attachBaseContext方法 + if (cv.activity) { MethodVisitorUtil.addActivityAttach(classWriter) - }else if(cv.service){ + } else if (cv.service) { MethodVisitorUtil.addServiceAttach(classWriter) - }else if(cv.intentService){ + } else if (cv.intentService) { MethodVisitorUtil.addIntentServiceAttach(classWriter) } + //添加applyOverrideConfiguration方法 + if (cv.needAddACMethod()) { + println("add applyOverrideConfiguration method to ${name}") + MethodVisitorUtil.addApplyOverrideConfiguration(classWriter, cv.className) + } } } diff --git a/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/PluginExtension.groovy b/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/PluginExtension.groovy index 4df6457..65542ee 100644 --- a/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/PluginExtension.groovy +++ b/plugin/src/main/groovy/com/github/jokar/multilanguages/plugin/PluginExtension.groovy @@ -7,6 +7,6 @@ class PluginExtension { String toString() { return "PluginExtension{" + "enable=" + enable + - '}'; + '}' } } \ No newline at end of file diff --git a/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ActivityServiceClassVisitor.java b/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ActivityServiceClassVisitor.java index 74168aa..e4b756a 100644 --- a/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ActivityServiceClassVisitor.java +++ b/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ActivityServiceClassVisitor.java @@ -10,6 +10,11 @@ */ public class ActivityServiceClassVisitor extends ClassVisitor implements Opcodes { private String superClassName; + private String className; + /** + * 是否有applyOverrideConfiguration方法 + */ + private boolean hasACMethod; public ActivityServiceClassVisitor(ClassWriter cv) { super(Opcodes.ASM5, cv); @@ -20,36 +25,99 @@ public void visit(int version, int access, String name, String signature, String String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); superClassName = superName; + this.className = name; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - //删除原有 attachBaseContext 方法 - if (needAddAttach() && name.equals("attachBaseContext")) { - return null; + if (needAddAttach()) { + hasACMethod = name.equals("applyOverrideConfiguration"); + if (name.equals("attachBaseContext")) { + //删除原有 attachBaseContext 方法 + return null; + } else if (isAndroidxActivity() && name.equals("applyOverrideConfiguration")) { + //是继承androidx.AppCompatActivity的activity,在 applyOverrideConfiguration + //添加 overrideConfiguration.setTo(this.getBaseContext().getResources().getConfiguration()); + return new ApplyOverrideConfigurationMV(cv.visitMethod(access, name, descriptor, + signature, exceptions), this.className); + } } return super.visitMethod(access, name, descriptor, signature, exceptions); } + /** + * 是否需要添加 + * + * @return + */ public boolean needAddAttach() { return isActivity() || isService() || isIntentService(); } + /** + * 是否需要添加applyOverrideConfiguration方法 + * + * @return + */ + public boolean needAddACMethod() { + return isAndroidxActivity() && !hasACMethod; + } + /** + * 是否是Activity类 + * + * @return + */ public boolean isActivity() { - return superClassName.equals("android/support/v4/app/FragmentActivity") + if (className == null || superClassName == null) { + return false; + } + return (superClassName.equals("android/support/v4/app/FragmentActivity") || superClassName.equals("android/support/v7/app/AppCompatActivity") - || superClassName.equals("android/app/Activity"); + || superClassName.equals("android/app/Activity") + || isAndroidxActivity()) + && !isAndroidxPackageName(); //排除androidx包里的 } + /** + * 是否是继承androidx.AppCompatActivity activity + * + * @return + */ + private boolean isAndroidxActivity() { + if (superClassName == null) { + return false; + } + return superClassName.equals("androidx/appcompat/app/AppCompatActivity"); + } + + /** + * 是否是androidx包名下类 + * + * @return + */ + public boolean isAndroidxPackageName() { + return className.contains("androidx/core/app"); + } public boolean isService() { - return superClassName.equals("android/app/Service"); + if (className == null || superClassName == null) { + return false; + } + return superClassName.equals("android/app/Service") + && !isAndroidxPackageName(); //排除androidx包里的 } + public boolean isIntentService() { + if (className == null || superClassName == null) { + return false; + } + return superClassName.equals("android/app/IntentService") + && !isAndroidxPackageName(); //排除androidx包里的 + } - public boolean isIntentService(){ - return superClassName.equals("android/app/IntentService"); + public String getClassName() { + return className; } } diff --git a/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ApplyOverrideConfigurationMV.java b/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ApplyOverrideConfigurationMV.java new file mode 100644 index 0000000..73c860d --- /dev/null +++ b/plugin/src/main/java/com/github/jokar/multilanguages/plugin/ApplyOverrideConfigurationMV.java @@ -0,0 +1,25 @@ +package com.github.jokar.multilanguages.plugin; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * applyOverrideConfiguration方法 + * Create by JokAr. on 2019-11-08. + */ +public class ApplyOverrideConfigurationMV extends MethodVisitor { + private MethodVisitor mv; + private String className; + + public ApplyOverrideConfigurationMV(MethodVisitor mv, String className) { + super(Opcodes.ASM4, mv); + this.mv = mv; + this.className = className; + } + + @Override + public void visitCode() { + MethodVisitorUtil.addSetTo(mv, className); + super.visitCode(); + } +} diff --git a/plugin/src/main/java/com/github/jokar/multilanguages/plugin/MethodVisitorUtil.java b/plugin/src/main/java/com/github/jokar/multilanguages/plugin/MethodVisitorUtil.java index 624c7dc..51dc6f2 100644 --- a/plugin/src/main/java/com/github/jokar/multilanguages/plugin/MethodVisitorUtil.java +++ b/plugin/src/main/java/com/github/jokar/multilanguages/plugin/MethodVisitorUtil.java @@ -3,11 +3,15 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import static org.objectweb.asm.Opcodes.ACC_PROTECTED; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.RETURN; /** @@ -15,8 +19,14 @@ */ public class MethodVisitorUtil { + /** + * 添加activity下的attachBaseContext + * + * @param cw + */ public static void addActivityAttach(ClassWriter cw) { - MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "attachBaseContext", "(Landroid/content/Context;)V", + MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "attachBaseContext", + "(Landroid/content/Context;)V", null, null); mv.visitCode(); Label l0 = new Label(); @@ -39,7 +49,11 @@ public static void addActivityAttach(ClassWriter cw) { mv.visitEnd(); } - + /** + * 添加intentService类下的attachBaseContext + * + * @param cw + */ public static void addIntentServiceAttach(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "attachBaseContext", "(Landroid/content/Context;)V", null, null); @@ -57,20 +71,28 @@ public static void addIntentServiceAttach(ClassWriter cw) { mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); - mv.visitLocalVariable("newBase", "Landroid/content/Context;", null, l0, l2, 1); + mv.visitLocalVariable("newBase", "Landroid/content/Context;", + null, l0, l2, 1); mv.visitMaxs(2, 2); mv.visitEnd(); } - public static void addServiceAttach(ClassWriter cw){ - MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "attachBaseContext", "(Landroid/content/Context;)V", null, null); + /** + * 添加service下的attachBaseContext + * + * @param cw + */ + public static void addServiceAttach(ClassWriter cw) { + MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "attachBaseContext", + "(Landroid/content/Context;)V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKESTATIC, "com/github/jokar/multilanguages/library/MultiLanguage", - "setLocal", "(Landroid/content/Context;)Landroid/content/Context;", false); + "setLocal", "(Landroid/content/Context;)Landroid/content/Context;", + false); mv.visitMethodInsn(INVOKESPECIAL, "android/app/Service", "attachBaseContext", "(Landroid/content/Context;)V", false); Label l1 = new Label(); @@ -78,8 +100,78 @@ public static void addServiceAttach(ClassWriter cw){ mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); - mv.visitLocalVariable("base", "Landroid/content/Context;", null, l0, l2, 1); + mv.visitLocalVariable("base", "Landroid/content/Context;", + null, l0, l2, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + /** + * 添加applyOverrideConfiguration方法 + * + * @param cw + * @param className + */ + public static void addApplyOverrideConfiguration(ClassWriter cw, String className) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "applyOverrideConfiguration", + "(Landroid/content/res/Configuration;)V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitVarInsn(ALOAD, 1); + Label l1 = new Label(); + mv.visitJumpInsn(IFNULL, l1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, className, "getBaseContext", + "()Landroid/content/Context;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/Context", "getResources", + "()Landroid/content/res/Resources;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/res/Resources", "getConfiguration", + "()Landroid/content/res/Configuration;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/res/Configuration", "setTo", + "(Landroid/content/res/Configuration;)V", false); + mv.visitLabel(l1); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKESPECIAL, "androidx/appcompat/app/AppCompatActivity", + "applyOverrideConfiguration", "(Landroid/content/res/Configuration;)V", false); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "L" + className + ";", null, l0, l4, 0); + mv.visitLocalVariable("overrideConfiguration", "Landroid/content/res/Configuration;", + null, l0, l4, 1); mv.visitMaxs(2, 2); mv.visitEnd(); } + + /** + * 添加 overrideConfiguration.setTo(this.getBaseContext().getResources().getConfiguration()); + */ + public static void addSetTo(MethodVisitor mv, String className) { + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitVarInsn(ALOAD, 1); + Label l1 = new Label(); + mv.visitJumpInsn(IFNULL, l1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, className, + "getBaseContext", "()Landroid/content/Context;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/Context", "getResources", + "()Landroid/content/res/Resources;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/res/Resources", + "getConfiguration", "()Landroid/content/res/Configuration;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "android/content/res/Configuration", + "setTo", "(Landroid/content/res/Configuration;)V", false); + mv.visitLabel(l1); + } }