Skip to content

Commit

Permalink
修复AGP 8.0.0以上生成的activity未插入AndroidManifest.xml问题
Browse files Browse the repository at this point in the history
  • Loading branch information
qq549631030 committed Jul 15, 2023
1 parent 71cc25e commit dbeb26b
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 2 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
2 changes: 1 addition & 1 deletion library/gradle.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class AndroidJunkCodePlugin implements Plugin<Project> {
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Project> {

@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)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n")
sb.append(" <application>\n")
for (i in 0..<activityList.size()) {
sb.append(" <activity android:name=\"${activityList.get(i)}\"/>\n")
}
sb.append(" </application>\n")
sb.append("</manifest>")
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<String> 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("<resources>\n")
for (i in 0..<stringList.size()) {
sb.append("<string name=\"${stringList.get(i)}\">${stringList.get(i)}</string>\n")
}
sb.append("</resources>\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("<resources xmlns:tools=\"http://schemas.android.com/tools\"\n" +
" tools:keep=\"@layout/${config.resPrefix}*, @drawable/${config.resPrefix}*\" />\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())
}
}
Original file line number Diff line number Diff line change
@@ -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("<application>") + "<application>".length(), genManifest.indexOf("</application>"))
String manifest = new String(getMergedManifest().get().asFile.readBytes())
manifest = manifest.replace("</application>", "$genManifest\n</application>")
getUpdatedManifest().get().asFile.write(manifest)
}
}
Loading

0 comments on commit dbeb26b

Please sign in to comment.