Skip to content

Commit dbeb26b

Browse files
committed
修复AGP 8.0.0以上生成的activity未插入AndroidManifest.xml问题
1 parent 71cc25e commit dbeb26b

File tree

8 files changed

+429
-2
lines changed

8 files changed

+429
-2
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ buildscript {
1111
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1212
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:3.0.1"
1313
if (PLUGIN_ENABLE.toBoolean()) {
14-
classpath "com.github.qq549631030:android-junk-code:1.2.6"
14+
classpath "com.github.qq549631030:android-junk-code:1.2.8"
1515
}
1616
// NOTE: Do not place your application dependencies here; they belong
1717
// in the individual module build.gradle files

library/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ java {
1919
dependencies {
2020
implementation gradleApi()
2121
implementation 'com.squareup:javapoet:1.13.0'
22+
compileOnly 'com.android.tools.build:gradle-api:7.1.0'
2223
}

library/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#project
22
GROUP=com.github.qq549631030
3-
VERSION_NAME=1.2.6
3+
VERSION_NAME=1.2.8
44

55
POM_ARTIFACT_ID=android-junk-code
66
POM_NAME=AndroidJunkCode

library/src/main/groovy/cn/hx/plugin/junkcode/plugin/AndroidJunkCodePlugin.groovy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ class AndroidJunkCodePlugin implements Plugin<Project> {
1414
if (!android || !android.hasProperty("applicationVariants")) {
1515
throw IllegalArgumentException("must apply this plugin after 'com.android.application'")
1616
}
17+
def androidComponents = project.extensions.findByName("androidComponents")
18+
//AGP 7.4.0+
19+
if (androidComponents && androidComponents.hasProperty("pluginVersion")
20+
&& (androidComponents.pluginVersion.major > 7 || androidComponents.pluginVersion.minor >= 4)) {
21+
new NewVariantApiPlugin().apply(project)
22+
return
23+
}
1724
def generateJunkCodeExt = project.extensions.create("androidJunkCode", AndroidJunkCodeExt, project.container(JunkCodeConfig))
1825
android.applicationVariants.all { variant ->
1926
def variantName = variant.name
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package cn.hx.plugin.junkcode.plugin
2+
3+
import cn.hx.plugin.junkcode.ext.AndroidJunkCodeExt
4+
import cn.hx.plugin.junkcode.ext.JunkCodeConfig
5+
import cn.hx.plugin.junkcode.task.ManifestMergeTask
6+
import cn.hx.plugin.junkcode.task.GenerateJunkCodeTask
7+
import com.android.build.api.artifact.SingleArtifact
8+
import org.gradle.api.Plugin
9+
import org.gradle.api.Project
10+
import org.gradle.api.tasks.TaskProvider
11+
12+
class NewVariantApiPlugin implements Plugin<Project> {
13+
14+
@Override
15+
void apply(Project project) {
16+
def android = project.extensions.findByName("android")
17+
def androidComponents = project.extensions.findByName("androidComponents")
18+
def generateJunkCodeExt = project.extensions.create("androidJunkCode", AndroidJunkCodeExt, project.container(JunkCodeConfig))
19+
androidComponents.onVariants(androidComponents.selector().all(), { variant ->
20+
def variantName = variant.name
21+
def junkCodeConfig = generateJunkCodeExt.variantConfig.findByName(variantName)
22+
if (generateJunkCodeExt.debug) {
23+
println("AndroidJunkCode: generate code for variant $variantName? ${junkCodeConfig != null}")
24+
}
25+
if (junkCodeConfig) {
26+
def junkCodeOutDir = new File(project.buildDir, "generated/source/junk/${variantName}")
27+
def generateJunkCodeTaskProvider = project.tasks.register("generate${variantName.capitalize()}JunkCode", GenerateJunkCodeTask) {
28+
config = junkCodeConfig
29+
namespace = android.namespace
30+
javaOutputFolder.set(new File(junkCodeOutDir, "java"))
31+
resOutputFolder.set(new File(junkCodeOutDir, "res"))
32+
manifestOutputFile.set(new File(junkCodeOutDir, "AndroidManifest.xml"))
33+
}
34+
if (variant.sources.java) {
35+
variant.sources.java.addGeneratedSourceDirectory(generateJunkCodeTaskProvider, {
36+
it.javaOutputFolder
37+
})
38+
}
39+
if (variant.sources.res) {
40+
variant.sources.res.addGeneratedSourceDirectory(generateJunkCodeTaskProvider, {
41+
it.resOutputFolder
42+
})
43+
}
44+
TaskProvider manifestUpdater = project.tasks.register('merge' + variantName.capitalize() + 'JunkCodeManifest', ManifestMergeTask) {
45+
it.genManifestFile.set(generateJunkCodeTaskProvider.flatMap {
46+
it.manifestOutputFile
47+
})
48+
}
49+
variant.artifacts.use(manifestUpdater)
50+
.wiredWithFiles({ it.mergedManifest },
51+
{ it.updatedManifest })
52+
.toTransform(SingleArtifact.MERGED_MANIFEST.INSTANCE)
53+
}
54+
})
55+
}
56+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package cn.hx.plugin.junkcode.task
2+
3+
import cn.hx.plugin.junkcode.ext.JunkCodeConfig
4+
import cn.hx.plugin.junkcode.template.ResTemplate
5+
import cn.hx.plugin.junkcode.utils.JunkUtil
6+
import com.squareup.javapoet.ClassName
7+
import com.squareup.javapoet.JavaFile
8+
import com.squareup.javapoet.MethodSpec
9+
import com.squareup.javapoet.TypeSpec
10+
import org.gradle.api.DefaultTask
11+
import org.gradle.api.file.DirectoryProperty
12+
import org.gradle.api.file.RegularFileProperty
13+
import org.gradle.api.tasks.*
14+
15+
import javax.lang.model.element.Modifier
16+
import java.nio.file.Files
17+
import java.nio.file.Path
18+
19+
abstract class GenerateJunkCodeTask extends DefaultTask {
20+
21+
@Nested
22+
abstract JunkCodeConfig config
23+
24+
@Input
25+
String namespace
26+
27+
@OutputDirectory
28+
abstract DirectoryProperty getJavaOutputFolder()
29+
30+
@OutputDirectory
31+
abstract DirectoryProperty getResOutputFolder()
32+
33+
@OutputFile
34+
abstract RegularFileProperty getManifestOutputFile()
35+
36+
@Internal
37+
List<String> activityList = new ArrayList<>()
38+
39+
@TaskAction
40+
void taskAction() {
41+
getJavaOutputFolder().get().asFile.deleteDir()
42+
getResOutputFolder().get().asFile.deleteDir()
43+
for (int i = 0; i < config.packageCount; i++) {
44+
String packageName
45+
if (config.packageBase.isEmpty()) {
46+
packageName = JunkUtil.generateName(i)
47+
} else {
48+
packageName = config.packageBase + "." + JunkUtil.generateName(i)
49+
}
50+
generateActivity(packageName)
51+
generateOtherClass(packageName)
52+
}
53+
generateManifest()
54+
generateDrawable()
55+
generateStringsFile()
56+
generateKeep()
57+
}
58+
59+
private void generateActivity(String packageName) {
60+
for (int i = 0; i < config.activityCountPerPackage; i++) {
61+
def activityPreName = JunkUtil.generateName(i)
62+
def className = activityPreName.capitalize() + "Activity"
63+
def layoutName = "${config.resPrefix.toLowerCase()}${packageName.replace(".", "_")}_activity_${activityPreName}"
64+
generateLayout(layoutName)
65+
if (!config.excludeActivityJavaFile) {
66+
def typeBuilder = TypeSpec.classBuilder(className)
67+
typeBuilder.superclass(ClassName.get("android.app", "Activity"))
68+
typeBuilder.addModifiers(Modifier.PUBLIC)
69+
//onCreate方法
70+
def bundleClassName = ClassName.get("android.os", "Bundle")
71+
typeBuilder.addMethod(MethodSpec.methodBuilder("onCreate")
72+
.addAnnotation(Override.class)
73+
.addModifiers(Modifier.PROTECTED)
74+
.addParameter(bundleClassName, "savedInstanceState")
75+
.addStatement("super.onCreate(savedInstanceState)")
76+
.addStatement("setContentView(\$T.layout.${layoutName})", ClassName.get(namespace, "R"))
77+
.build())
78+
if (config.typeGenerator) {
79+
config.typeGenerator.execute(typeBuilder)
80+
} else {
81+
//其它方法
82+
for (int j = 0; j < config.methodCountPerClass; j++) {
83+
def methodName = JunkUtil.generateName(j)
84+
def methodBuilder = MethodSpec.methodBuilder(methodName)
85+
if (config.methodGenerator) {
86+
config.methodGenerator.execute(methodBuilder)
87+
} else {
88+
JunkUtil.generateMethods(methodBuilder)
89+
}
90+
typeBuilder.addMethod(methodBuilder.build())
91+
}
92+
}
93+
def javaFile = JavaFile.builder(packageName, typeBuilder.build()).build()
94+
writeJavaFile(javaFile)
95+
activityList.add(packageName + "." + className)
96+
}
97+
}
98+
}
99+
100+
101+
/**
102+
* 生成Manifest
103+
*/
104+
private void generateManifest() {
105+
def manifestFile = getManifestOutputFile().get().asFile
106+
StringBuilder sb = new StringBuilder()
107+
sb.append("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n")
108+
sb.append(" <application>\n")
109+
for (i in 0..<activityList.size()) {
110+
sb.append(" <activity android:name=\"${activityList.get(i)}\"/>\n")
111+
}
112+
sb.append(" </application>\n")
113+
sb.append("</manifest>")
114+
JunkUtil.writeStringToFile(manifestFile, sb.toString())
115+
}
116+
117+
private void generateOtherClass(String packageName) {
118+
for (int j = 0; j < config.otherCountPerPackage; j++) {
119+
def className = JunkUtil.generateName(j).capitalize()
120+
def typeBuilder = TypeSpec.classBuilder(className)
121+
if (config.typeGenerator) {
122+
config.typeGenerator.execute(typeBuilder)
123+
} else {
124+
typeBuilder.addModifiers(Modifier.PUBLIC)
125+
for (int k = 0; k < config.methodCountPerClass; k++) {
126+
def methodName = JunkUtil.generateName(k)
127+
def methodBuilder = MethodSpec.methodBuilder(methodName)
128+
if (config.methodGenerator) {
129+
config.methodGenerator.execute(methodBuilder)
130+
} else {
131+
JunkUtil.generateMethods(methodBuilder)
132+
}
133+
typeBuilder.addMethod(methodBuilder.build())
134+
}
135+
}
136+
def javaFile = JavaFile.builder(packageName, typeBuilder.build()).build()
137+
writeJavaFile(javaFile)
138+
}
139+
}
140+
141+
/**
142+
* 生成layout
143+
* @param layoutName
144+
*/
145+
private void generateLayout(String layoutName) {
146+
def layoutFile = new File(getResOutputFolder().get().asFile, "layout/${layoutName}.xml")
147+
if (config.layoutGenerator) {
148+
def builder = new StringBuilder()
149+
config.layoutGenerator.execute(builder)
150+
JunkUtil.writeStringToFile(layoutFile, builder.toString())
151+
} else {
152+
def layoutStr = String.format(ResTemplate.LAYOUT_TEMPLATE, JunkUtil.generateId())
153+
JunkUtil.writeStringToFile(layoutFile, layoutStr)
154+
}
155+
}
156+
157+
158+
/**
159+
* 生成drawable
160+
* @param drawableName
161+
*/
162+
void generateDrawable() {
163+
for (int i = 0; i < config.drawableCount; i++) {
164+
def drawableName = "${config.resPrefix.toLowerCase()}${JunkUtil.generateName(i)}"
165+
def drawableFile = new File(getResOutputFolder().get().asFile, "drawable/${drawableName}.xml")
166+
if (config.drawableGenerator) {
167+
def builder = new StringBuilder()
168+
config.drawableGenerator.execute(builder)
169+
JunkUtil.writeStringToFile(drawableFile, builder.toString())
170+
} else {
171+
def drawableStr = String.format(ResTemplate.DRAWABLE, JunkUtil.generateColor())
172+
JunkUtil.writeStringToFile(drawableFile, drawableStr)
173+
}
174+
}
175+
}
176+
177+
/**
178+
* 生成strings.xml
179+
*/
180+
void generateStringsFile() {
181+
List<String> stringList = new ArrayList<>()
182+
for (int i = 0; i < config.stringCount; i++) {
183+
stringList.add("${config.resPrefix.toLowerCase()}${JunkUtil.generateName(i)}")
184+
}
185+
def stringFile = new File(getResOutputFolder().get().asFile, "values/strings.xml")
186+
StringBuilder sb = new StringBuilder()
187+
sb.append("<resources>\n")
188+
for (i in 0..<stringList.size()) {
189+
sb.append("<string name=\"${stringList.get(i)}\">${stringList.get(i)}</string>\n")
190+
}
191+
sb.append("</resources>\n")
192+
JunkUtil.writeStringToFile(stringFile, sb.toString())
193+
}
194+
195+
private void generateKeep() {
196+
def keepFile = new File(getResOutputFolder().get().asFile, "raw/android_junk_code_keep.xml")
197+
StringBuilder sb = new StringBuilder()
198+
sb.append("<resources xmlns:tools=\"http://schemas.android.com/tools\"\n" +
199+
" tools:keep=\"@layout/${config.resPrefix}*, @drawable/${config.resPrefix}*\" />\n")
200+
JunkUtil.writeStringToFile(keepFile, sb.toString())
201+
}
202+
203+
private void writeJavaFile(JavaFile javaFile) {
204+
def outputDirectory = new File(getJavaOutputFolder().get().asFile, "java").toPath()
205+
if (!javaFile.packageName.isEmpty()) {
206+
for (String packageComponent : javaFile.packageName.split("\\.")) {
207+
outputDirectory = outputDirectory.resolve(packageComponent);
208+
}
209+
Files.createDirectories(outputDirectory);
210+
}
211+
Path outputPath = outputDirectory.resolve(javaFile.typeSpec.name + ".java");
212+
JunkUtil.writeStringToFile(outputPath.toFile(), javaFile.toString())
213+
}
214+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package cn.hx.plugin.junkcode.task
2+
3+
import org.gradle.api.DefaultTask
4+
import org.gradle.api.file.RegularFileProperty
5+
import org.gradle.api.tasks.InputFile
6+
import org.gradle.api.tasks.OutputFile
7+
import org.gradle.api.tasks.TaskAction
8+
9+
abstract class ManifestMergeTask extends DefaultTask {
10+
11+
@InputFile
12+
abstract RegularFileProperty getGenManifestFile()
13+
14+
@InputFile
15+
abstract RegularFileProperty getMergedManifest()
16+
17+
@OutputFile
18+
abstract RegularFileProperty getUpdatedManifest()
19+
20+
@TaskAction
21+
void taskAction() {
22+
String genManifest = new String(getGenManifestFile().get().asFile.readBytes())
23+
genManifest = genManifest.substring(genManifest.indexOf("<application>") + "<application>".length(), genManifest.indexOf("</application>"))
24+
String manifest = new String(getMergedManifest().get().asFile.readBytes())
25+
manifest = manifest.replace("</application>", "$genManifest\n</application>")
26+
getUpdatedManifest().get().asFile.write(manifest)
27+
}
28+
}

0 commit comments

Comments
 (0)