From dbeb26b9102d9a5d59cfb8558761b80c92a7529a Mon Sep 17 00:00:00 2001 From: huangx <549631030@qq.com> Date: Sat, 15 Jul 2023 17:09:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAGP=208.0.0=E4=BB=A5=E4=B8=8A?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=9A=84activity=E6=9C=AA=E6=8F=92=E5=85=A5A?= =?UTF-8?q?ndroidManifest.xml=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- library/build.gradle | 1 + library/gradle.properties | 2 +- .../plugin/AndroidJunkCodePlugin.groovy | 7 + .../plugin/NewVariantApiPlugin.groovy | 56 +++++ .../junkcode/task/GenerateJunkCodeTask.groovy | 214 ++++++++++++++++++ .../junkcode/task/ManifestMergeTask.groovy | 28 +++ .../hx/plugin/junkcode/utils/JunkUtil.groovy | 121 ++++++++++ 8 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 library/src/main/groovy/cn/hx/plugin/junkcode/plugin/NewVariantApiPlugin.groovy create mode 100644 library/src/main/groovy/cn/hx/plugin/junkcode/task/GenerateJunkCodeTask.groovy create mode 100644 library/src/main/groovy/cn/hx/plugin/junkcode/task/ManifestMergeTask.groovy create mode 100644 library/src/main/groovy/cn/hx/plugin/junkcode/utils/JunkUtil.groovy diff --git a/build.gradle b/build.gradle index a56b7f69..fed88c69 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:3.0.1" if (PLUGIN_ENABLE.toBoolean()) { - classpath "com.github.qq549631030:android-junk-code:1.2.6" + classpath "com.github.qq549631030:android-junk-code:1.2.8" } // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/library/build.gradle b/library/build.gradle index c14ef475..636bebc7 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -19,4 +19,5 @@ java { dependencies { implementation gradleApi() implementation 'com.squareup:javapoet:1.13.0' + compileOnly 'com.android.tools.build:gradle-api:7.1.0' } \ No newline at end of file diff --git a/library/gradle.properties b/library/gradle.properties index aa56a465..9d9c9c2c 100644 --- a/library/gradle.properties +++ b/library/gradle.properties @@ -1,6 +1,6 @@ #project GROUP=com.github.qq549631030 -VERSION_NAME=1.2.6 +VERSION_NAME=1.2.8 POM_ARTIFACT_ID=android-junk-code POM_NAME=AndroidJunkCode diff --git a/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/AndroidJunkCodePlugin.groovy b/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/AndroidJunkCodePlugin.groovy index 030e2659..5388d241 100644 --- a/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/AndroidJunkCodePlugin.groovy +++ b/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/AndroidJunkCodePlugin.groovy @@ -14,6 +14,13 @@ class AndroidJunkCodePlugin implements Plugin { if (!android || !android.hasProperty("applicationVariants")) { throw IllegalArgumentException("must apply this plugin after 'com.android.application'") } + def androidComponents = project.extensions.findByName("androidComponents") + //AGP 7.4.0+ + if (androidComponents && androidComponents.hasProperty("pluginVersion") + && (androidComponents.pluginVersion.major > 7 || androidComponents.pluginVersion.minor >= 4)) { + new NewVariantApiPlugin().apply(project) + return + } def generateJunkCodeExt = project.extensions.create("androidJunkCode", AndroidJunkCodeExt, project.container(JunkCodeConfig)) android.applicationVariants.all { variant -> def variantName = variant.name diff --git a/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/NewVariantApiPlugin.groovy b/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/NewVariantApiPlugin.groovy new file mode 100644 index 00000000..5f8e6ca5 --- /dev/null +++ b/library/src/main/groovy/cn/hx/plugin/junkcode/plugin/NewVariantApiPlugin.groovy @@ -0,0 +1,56 @@ +package cn.hx.plugin.junkcode.plugin + +import cn.hx.plugin.junkcode.ext.AndroidJunkCodeExt +import cn.hx.plugin.junkcode.ext.JunkCodeConfig +import cn.hx.plugin.junkcode.task.ManifestMergeTask +import cn.hx.plugin.junkcode.task.GenerateJunkCodeTask +import com.android.build.api.artifact.SingleArtifact +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.TaskProvider + +class NewVariantApiPlugin implements Plugin { + + @Override + void apply(Project project) { + def android = project.extensions.findByName("android") + def androidComponents = project.extensions.findByName("androidComponents") + def generateJunkCodeExt = project.extensions.create("androidJunkCode", AndroidJunkCodeExt, project.container(JunkCodeConfig)) + androidComponents.onVariants(androidComponents.selector().all(), { variant -> + def variantName = variant.name + def junkCodeConfig = generateJunkCodeExt.variantConfig.findByName(variantName) + if (generateJunkCodeExt.debug) { + println("AndroidJunkCode: generate code for variant $variantName? ${junkCodeConfig != null}") + } + if (junkCodeConfig) { + def junkCodeOutDir = new File(project.buildDir, "generated/source/junk/${variantName}") + def generateJunkCodeTaskProvider = project.tasks.register("generate${variantName.capitalize()}JunkCode", GenerateJunkCodeTask) { + config = junkCodeConfig + namespace = android.namespace + javaOutputFolder.set(new File(junkCodeOutDir, "java")) + resOutputFolder.set(new File(junkCodeOutDir, "res")) + manifestOutputFile.set(new File(junkCodeOutDir, "AndroidManifest.xml")) + } + if (variant.sources.java) { + variant.sources.java.addGeneratedSourceDirectory(generateJunkCodeTaskProvider, { + it.javaOutputFolder + }) + } + if (variant.sources.res) { + variant.sources.res.addGeneratedSourceDirectory(generateJunkCodeTaskProvider, { + it.resOutputFolder + }) + } + TaskProvider manifestUpdater = project.tasks.register('merge' + variantName.capitalize() + 'JunkCodeManifest', ManifestMergeTask) { + it.genManifestFile.set(generateJunkCodeTaskProvider.flatMap { + it.manifestOutputFile + }) + } + variant.artifacts.use(manifestUpdater) + .wiredWithFiles({ it.mergedManifest }, + { it.updatedManifest }) + .toTransform(SingleArtifact.MERGED_MANIFEST.INSTANCE) + } + }) + } +} \ No newline at end of file diff --git a/library/src/main/groovy/cn/hx/plugin/junkcode/task/GenerateJunkCodeTask.groovy b/library/src/main/groovy/cn/hx/plugin/junkcode/task/GenerateJunkCodeTask.groovy new file mode 100644 index 00000000..c48fa149 --- /dev/null +++ b/library/src/main/groovy/cn/hx/plugin/junkcode/task/GenerateJunkCodeTask.groovy @@ -0,0 +1,214 @@ +package cn.hx.plugin.junkcode.task + +import cn.hx.plugin.junkcode.ext.JunkCodeConfig +import cn.hx.plugin.junkcode.template.ResTemplate +import cn.hx.plugin.junkcode.utils.JunkUtil +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.TypeSpec +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.* + +import javax.lang.model.element.Modifier +import java.nio.file.Files +import java.nio.file.Path + +abstract class GenerateJunkCodeTask extends DefaultTask { + + @Nested + abstract JunkCodeConfig config + + @Input + String namespace + + @OutputDirectory + abstract DirectoryProperty getJavaOutputFolder() + + @OutputDirectory + abstract DirectoryProperty getResOutputFolder() + + @OutputFile + abstract RegularFileProperty getManifestOutputFile() + + @Internal + List activityList = new ArrayList<>() + + @TaskAction + void taskAction() { + getJavaOutputFolder().get().asFile.deleteDir() + getResOutputFolder().get().asFile.deleteDir() + for (int i = 0; i < config.packageCount; i++) { + String packageName + if (config.packageBase.isEmpty()) { + packageName = JunkUtil.generateName(i) + } else { + packageName = config.packageBase + "." + JunkUtil.generateName(i) + } + generateActivity(packageName) + generateOtherClass(packageName) + } + generateManifest() + generateDrawable() + generateStringsFile() + generateKeep() + } + + private void generateActivity(String packageName) { + for (int i = 0; i < config.activityCountPerPackage; i++) { + def activityPreName = JunkUtil.generateName(i) + def className = activityPreName.capitalize() + "Activity" + def layoutName = "${config.resPrefix.toLowerCase()}${packageName.replace(".", "_")}_activity_${activityPreName}" + generateLayout(layoutName) + if (!config.excludeActivityJavaFile) { + def typeBuilder = TypeSpec.classBuilder(className) + typeBuilder.superclass(ClassName.get("android.app", "Activity")) + typeBuilder.addModifiers(Modifier.PUBLIC) + //onCreate方法 + def bundleClassName = ClassName.get("android.os", "Bundle") + typeBuilder.addMethod(MethodSpec.methodBuilder("onCreate") + .addAnnotation(Override.class) + .addModifiers(Modifier.PROTECTED) + .addParameter(bundleClassName, "savedInstanceState") + .addStatement("super.onCreate(savedInstanceState)") + .addStatement("setContentView(\$T.layout.${layoutName})", ClassName.get(namespace, "R")) + .build()) + if (config.typeGenerator) { + config.typeGenerator.execute(typeBuilder) + } else { + //其它方法 + for (int j = 0; j < config.methodCountPerClass; j++) { + def methodName = JunkUtil.generateName(j) + def methodBuilder = MethodSpec.methodBuilder(methodName) + if (config.methodGenerator) { + config.methodGenerator.execute(methodBuilder) + } else { + JunkUtil.generateMethods(methodBuilder) + } + typeBuilder.addMethod(methodBuilder.build()) + } + } + def javaFile = JavaFile.builder(packageName, typeBuilder.build()).build() + writeJavaFile(javaFile) + activityList.add(packageName + "." + className) + } + } + } + + + /** + * 生成Manifest + */ + private void generateManifest() { + def manifestFile = getManifestOutputFile().get().asFile + StringBuilder sb = new StringBuilder() + sb.append("\n") + sb.append(" \n") + for (i in 0..\n") + } + sb.append(" \n") + sb.append("") + JunkUtil.writeStringToFile(manifestFile, sb.toString()) + } + + private void generateOtherClass(String packageName) { + for (int j = 0; j < config.otherCountPerPackage; j++) { + def className = JunkUtil.generateName(j).capitalize() + def typeBuilder = TypeSpec.classBuilder(className) + if (config.typeGenerator) { + config.typeGenerator.execute(typeBuilder) + } else { + typeBuilder.addModifiers(Modifier.PUBLIC) + for (int k = 0; k < config.methodCountPerClass; k++) { + def methodName = JunkUtil.generateName(k) + def methodBuilder = MethodSpec.methodBuilder(methodName) + if (config.methodGenerator) { + config.methodGenerator.execute(methodBuilder) + } else { + JunkUtil.generateMethods(methodBuilder) + } + typeBuilder.addMethod(methodBuilder.build()) + } + } + def javaFile = JavaFile.builder(packageName, typeBuilder.build()).build() + writeJavaFile(javaFile) + } + } + + /** + * 生成layout + * @param layoutName + */ + private void generateLayout(String layoutName) { + def layoutFile = new File(getResOutputFolder().get().asFile, "layout/${layoutName}.xml") + if (config.layoutGenerator) { + def builder = new StringBuilder() + config.layoutGenerator.execute(builder) + JunkUtil.writeStringToFile(layoutFile, builder.toString()) + } else { + def layoutStr = String.format(ResTemplate.LAYOUT_TEMPLATE, JunkUtil.generateId()) + JunkUtil.writeStringToFile(layoutFile, layoutStr) + } + } + + + /** + * 生成drawable + * @param drawableName + */ + void generateDrawable() { + for (int i = 0; i < config.drawableCount; i++) { + def drawableName = "${config.resPrefix.toLowerCase()}${JunkUtil.generateName(i)}" + def drawableFile = new File(getResOutputFolder().get().asFile, "drawable/${drawableName}.xml") + if (config.drawableGenerator) { + def builder = new StringBuilder() + config.drawableGenerator.execute(builder) + JunkUtil.writeStringToFile(drawableFile, builder.toString()) + } else { + def drawableStr = String.format(ResTemplate.DRAWABLE, JunkUtil.generateColor()) + JunkUtil.writeStringToFile(drawableFile, drawableStr) + } + } + } + + /** + * 生成strings.xml + */ + void generateStringsFile() { + List stringList = new ArrayList<>() + for (int i = 0; i < config.stringCount; i++) { + stringList.add("${config.resPrefix.toLowerCase()}${JunkUtil.generateName(i)}") + } + def stringFile = new File(getResOutputFolder().get().asFile, "values/strings.xml") + StringBuilder sb = new StringBuilder() + sb.append("\n") + for (i in 0..${stringList.get(i)}\n") + } + sb.append("\n") + JunkUtil.writeStringToFile(stringFile, sb.toString()) + } + + private void generateKeep() { + def keepFile = new File(getResOutputFolder().get().asFile, "raw/android_junk_code_keep.xml") + StringBuilder sb = new StringBuilder() + sb.append("\n") + JunkUtil.writeStringToFile(keepFile, sb.toString()) + } + + private void writeJavaFile(JavaFile javaFile) { + def outputDirectory = new File(getJavaOutputFolder().get().asFile, "java").toPath() + if (!javaFile.packageName.isEmpty()) { + for (String packageComponent : javaFile.packageName.split("\\.")) { + outputDirectory = outputDirectory.resolve(packageComponent); + } + Files.createDirectories(outputDirectory); + } + Path outputPath = outputDirectory.resolve(javaFile.typeSpec.name + ".java"); + JunkUtil.writeStringToFile(outputPath.toFile(), javaFile.toString()) + } +} \ No newline at end of file diff --git a/library/src/main/groovy/cn/hx/plugin/junkcode/task/ManifestMergeTask.groovy b/library/src/main/groovy/cn/hx/plugin/junkcode/task/ManifestMergeTask.groovy new file mode 100644 index 00000000..cfa6a16b --- /dev/null +++ b/library/src/main/groovy/cn/hx/plugin/junkcode/task/ManifestMergeTask.groovy @@ -0,0 +1,28 @@ +package cn.hx.plugin.junkcode.task + +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +abstract class ManifestMergeTask extends DefaultTask { + + @InputFile + abstract RegularFileProperty getGenManifestFile() + + @InputFile + abstract RegularFileProperty getMergedManifest() + + @OutputFile + abstract RegularFileProperty getUpdatedManifest() + + @TaskAction + void taskAction() { + String genManifest = new String(getGenManifestFile().get().asFile.readBytes()) + genManifest = genManifest.substring(genManifest.indexOf("") + "".length(), genManifest.indexOf("")) + String manifest = new String(getMergedManifest().get().asFile.readBytes()) + manifest = manifest.replace("", "$genManifest\n") + getUpdatedManifest().get().asFile.write(manifest) + } +} \ No newline at end of file diff --git a/library/src/main/groovy/cn/hx/plugin/junkcode/utils/JunkUtil.groovy b/library/src/main/groovy/cn/hx/plugin/junkcode/utils/JunkUtil.groovy new file mode 100644 index 00000000..513a7e48 --- /dev/null +++ b/library/src/main/groovy/cn/hx/plugin/junkcode/utils/JunkUtil.groovy @@ -0,0 +1,121 @@ +package cn.hx.plugin.junkcode.utils + +import com.squareup.javapoet.MethodSpec + +import javax.lang.model.element.Modifier + +class JunkUtil { + + static random = new Random() + + static abc = "abcdefghijklmnopqrstuvwxyz".toCharArray() + static color = "0123456789abcdef".toCharArray() + + /** + * 生成名称 + * @param index + * @return + */ + static String generateName(int index) { + def sb = new StringBuilder() + for (i in 0..4) { + sb.append(abc[random.nextInt(abc.size())]) + } + int temp = index + while (temp >= 0) { + sb.append(abc[temp % abc.size()]) + temp = temp / abc.size() + if (temp == 0) { + temp = -1 + } + } + sb.append(index.toString()) + return sb.toString() + } + + + /** + * 生成随机方法 + * @param methodBuilder + */ + static void generateMethods(MethodSpec.Builder methodBuilder) { + switch (random.nextInt(5)) { + case 0: + methodBuilder.addStatement("long now = \$T.currentTimeMillis()", System.class) + .beginControlFlow("if (\$T.currentTimeMillis() < now)", System.class) + .addStatement("\$T.out.println(\$S)", System.class, "Time travelling, woo hoo!") + .nextControlFlow("else if (\$T.currentTimeMillis() == now)", System.class) + .addStatement("\$T.out.println(\$S)", System.class, "Time stood still!") + .nextControlFlow("else") + .addStatement("\$T.out.println(\$S)", System.class, "Ok, time still moving forward") + .endControlFlow() + break + case 1: + methodBuilder.addCode("" + "int total = 0;\n" + "for (int i = 0; i < 10; i++) {\n" + " total += i;\n" + "}\n") + break + case 2: + methodBuilder.beginControlFlow("try") + .addStatement("throw new Exception(\$S)", "Failed") + .nextControlFlow("catch (\$T e)", Exception.class) + .addStatement("throw new \$T(e)", RuntimeException.class) + .endControlFlow() + break + case 3: + methodBuilder.returns(Date.class) + .addStatement("return new \$T()", Date.class) + break + case 4: + methodBuilder.addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addParameter(String[].class, "args") + .addStatement("\$T.out.println(\$S)", System.class, "Hello") + break + default: + methodBuilder.addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(void.class) + .addParameter(String[].class, "args") + .addStatement("\$T.out.println(\$S)", System.class, "Hello") + } + } + + /** + * 生成颜色代码 + * @return + */ + static String generateColor() { + def sb = new StringBuilder() + sb.append("#") + for (i in 0..5) { + sb.append(color[random.nextInt(color.size())]) + } + return sb.toString() + } + /** + * 生成id代码 + * @return + */ + static String generateId() { + def sb = new StringBuilder() + for (i in 0..5) { + sb.append(abc[random.nextInt(abc.size())]) + } + return sb.toString() + } + + static void writeStringToFile(File file, String data) { + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs() + } + FileWriter writer + try { + writer = new FileWriter(file) + writer.write(data) + } catch (Exception e) { + e.printStackTrace() + } finally { + if (writer != null) { + writer.close() + } + } + } +} \ No newline at end of file