-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
325 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
com.qmuiteam.qmuidemo.fragment.lab.QDArchTestFragment | ||
QDArchTestFragment,$0.mTitleTv.setTag(com.qmuiteam.qmui.R.id.qmui_skin_value, "background:app_skin_common_background") | ||
; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
include ':qmuidemo', ':qmui', ':lib', ':compiler', ':lint', ':lintrule', ':arch', ':arch-compiler', | ||
':arch-annotation', ':skin-maker' | ||
':arch-annotation', ':skin-maker', ':skin-maker-plugin' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
plugins { | ||
id 'java-gradle-plugin' | ||
id 'groovy' | ||
id "com.gradle.plugin-publish" version "0.10.1" | ||
} | ||
|
||
version = QMUI_SKIN_MAKER_VERSION | ||
|
||
dependencies { | ||
implementation fileTree(dir: 'libs', include: ['*.jar']) | ||
implementation gradleApi() | ||
implementation localGroovy() | ||
|
||
implementation 'org.javassist:javassist:3.18.2-GA' | ||
implementation 'com.android.tools.build:gradle-api:3.5.1' | ||
implementation 'com.android.tools.build:gradle:3.5.1' | ||
implementation 'com.squareup:javapoet:1.10.0' | ||
implementation 'commons-io:commons-io:2.6' | ||
|
||
testImplementation 'junit:junit:4.12' | ||
} | ||
|
||
sourceCompatibility = "1.7" | ||
targetCompatibility = "1.7" | ||
|
||
gradlePlugin { | ||
plugins { | ||
skinMakerPlugin { | ||
id = 'com.qmuiteam.qmui.skinMaker' | ||
implementationClass = 'com.qmuiteam.qmui.SkinMakerPlugin' | ||
} | ||
} | ||
} | ||
|
||
|
||
File deployConfig = rootProject.file('gradle/deploy.properties') | ||
if (deployConfig.exists()) { | ||
apply from: rootProject.file('gradle/deploy.gradle') | ||
} |
34 changes: 34 additions & 0 deletions
34
skin-maker-plugin/src/main/groovy/com/qmuiteam/qmui/SkinMakerPlugin.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Tencent is pleased to support the open source community by making QMUI_Android available. | ||
* | ||
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved. | ||
* | ||
* Licensed under the MIT License (the "License"); you may not use this file except in | ||
* compliance with the License. You may obtain a copy of the License at | ||
* | ||
* http://opensource.org/licenses/MIT | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License is | ||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.qmuiteam.qmui | ||
|
||
import org.gradle.api.Plugin | ||
import org.gradle.api.Project | ||
|
||
class SkinMakerPlugin implements Plugin<Project> { | ||
|
||
@Override | ||
void apply(Project target) { | ||
def extension = target.extensions.create("skinMaker", SkinMaker.class) | ||
target.android.registerTransform(new SkinMakerTransform(target, extension)) | ||
} | ||
|
||
static class SkinMaker { | ||
File file | ||
} | ||
} | ||
|
200 changes: 200 additions & 0 deletions
200
skin-maker-plugin/src/main/groovy/com/qmuiteam/qmui/SkinMakerTransform.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
/* | ||
* Tencent is pleased to support the open source community by making QMUI_Android available. | ||
* | ||
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved. | ||
* | ||
* Licensed under the MIT License (the "License"); you may not use this file except in | ||
* compliance with the License. You may obtain a copy of the License at | ||
* | ||
* http://opensource.org/licenses/MIT | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License is | ||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.qmuiteam.qmui | ||
|
||
import com.android.build.api.transform.* | ||
import com.android.build.gradle.internal.pipeline.TransformManager | ||
import javassist.ClassPool | ||
import javassist.CtClass | ||
import javassist.CtMethod | ||
import org.apache.commons.codec.digest.DigestUtils | ||
import org.apache.commons.io.FileUtils | ||
import org.gradle.api.Project | ||
import org.gradle.api.logging.LogLevel | ||
|
||
class SkinMakerTransform extends Transform { | ||
|
||
private Project mProject | ||
private SkinMakerPlugin.SkinMaker mSkinMaker | ||
|
||
SkinMakerTransform(Project project, SkinMakerPlugin.SkinMaker skinMaker) { | ||
mProject = project | ||
mSkinMaker = skinMaker | ||
} | ||
|
||
|
||
@Override | ||
String getName() { | ||
return "skin-maker" | ||
} | ||
|
||
@Override | ||
Set<QualifiedContent.ContentType> getInputTypes() { | ||
return TransformManager.CONTENT_CLASS | ||
} | ||
|
||
@Override | ||
Set<? super QualifiedContent.Scope> getScopes() { | ||
return TransformManager.SCOPE_FULL_PROJECT | ||
} | ||
|
||
@Override | ||
boolean isIncremental() { | ||
return false | ||
} | ||
|
||
@Override | ||
void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { | ||
File sourceFile = mSkinMaker.file | ||
if (sourceFile == null || !sourceFile.exists()) { | ||
return | ||
} | ||
mProject.logger.log(LogLevel.INFO, "skin code source: " + sourceFile.path) | ||
|
||
def injectCode = new InjectCode() | ||
injectCode.parseFile(sourceFile) | ||
|
||
def androidJar = mProject.android.bootClasspath[0].toString() | ||
|
||
def externalDepsJars = new ArrayList<File>() | ||
def externalDepsDirs = new ArrayList<File>() | ||
|
||
transformInvocation.referencedInputs.forEach { transformInput -> | ||
externalDepsJars += transformInput.jarInputs.map { it.file } | ||
externalDepsDirs += transformInput.directoryInputs.map { it.file } | ||
} | ||
|
||
transformInvocation.outputProvider.deleteAll() | ||
|
||
transformInvocation.inputs.each { input -> | ||
input.directoryInputs.each { directoryInput -> | ||
|
||
def baseDir = directoryInput.file | ||
ClassPool pool = new ClassPool() | ||
pool.appendSystemPath() | ||
pool.appendClassPath(baseDir.absolutePath) | ||
pool.appendClassPath(androidJar) | ||
externalDepsJars.each { pool.insertClassPath(it.absolutePath) } | ||
externalDepsDirs.each { pool.insertClassPath(it.absolutePath) } | ||
|
||
|
||
directoryInput.file.eachFileRecurse { file -> | ||
String filePath = file.absolutePath | ||
if (filePath.endsWith(".class")) { | ||
def className = filePath.substring(directoryInput.file.absolutePath.length() + 1, filePath.length() - 6) | ||
.replace('/', '.') | ||
def codes = injectCode.getCode(className) | ||
if (codes != null && !codes.isEmpty()) { | ||
CtClass ctClass = pool.getCtClass(className) | ||
if (ctClass.isFrozen()) { | ||
ctClass.defrost() | ||
} | ||
codes.keySet().each { prefix -> | ||
def sb = new StringBuilder() | ||
sb.append("public void skinMaker") | ||
sb.append(prefix) | ||
sb.append("(){") | ||
codes.get(prefix).each { text -> | ||
sb.append(text) | ||
sb.append(";") | ||
} | ||
sb.append("}") | ||
CtMethod newMethod = CtMethod.make(sb.toString(), ctClass) | ||
ctClass.addMethod(newMethod) | ||
} | ||
|
||
if (className.endsWith("Fragment")) { | ||
CtMethod ctMethod = ctClass.getDeclaredMethod("onViewCreated", pool.get("android.view.View")) | ||
ctMethod.insertAfter("skinMaker" + className.split("\\.").last() + "();") | ||
} else if (className.endsWith("Activity")) { | ||
CtMethod ctMethod = ctClass.getDeclaredMethod("onCreate", pool.get("android.os.Bundle")) | ||
ctMethod.insertAfter("skinMaker" + className.split("\\.").last() + "();") | ||
} | ||
|
||
ctClass.writeFile(baseDir.absolutePath) | ||
ctClass.detach() | ||
} | ||
} | ||
} | ||
|
||
def dest = transformInvocation.outputProvider.getContentLocation( | ||
directoryInput.name, | ||
directoryInput.contentTypes, | ||
directoryInput.scopes, | ||
Format.DIRECTORY) | ||
|
||
FileUtils.copyDirectory(directoryInput.file, dest) | ||
} | ||
|
||
//遍历jar文件 对jar不操作,但是要输出到out路径 | ||
input.jarInputs.each { jarInput -> | ||
def jarName = jarInput.name | ||
def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath()) | ||
if (jarName.endsWith(".jar")) { | ||
jarName = jarName.substring(0, jarName.length() - 4) | ||
} | ||
def dest = transformInvocation.outputProvider.getContentLocation( | ||
jarName + md5Name, | ||
jarInput.contentTypes, | ||
jarInput.scopes, | ||
Format.JAR) | ||
FileUtils.copyFile(jarInput.file, dest) | ||
} | ||
} | ||
} | ||
|
||
|
||
class InjectCode { | ||
private HashMap<String, HashMap<String, ArrayList<String>>> mCodeMap = new HashMap<>() | ||
|
||
private String mCurrentClassName = null | ||
private HashMap<String, ArrayList<String>> mCurrentCodes = null | ||
|
||
void parseFile(File file) { | ||
file.newReader().lines().each { text -> | ||
if (text != null) { | ||
text = text.trim() | ||
if (!text.isBlank()) { | ||
if (mCurrentClassName == null) { | ||
mCurrentClassName = text | ||
mCurrentCodes = new HashMap<String, ArrayList<String>>() | ||
mCodeMap.put(mCurrentClassName, mCurrentCodes) | ||
} else if (text != ";") { | ||
int split = text.indexOf(",") | ||
if (split > 0 && split < text.length()) { | ||
String key = text.substring(0, split) | ||
ArrayList<String> codes = mCurrentCodes.get(key) | ||
if (codes == null) { | ||
codes = new ArrayList<String>() | ||
mCurrentCodes.put(key, codes) | ||
} | ||
codes.add(text.substring(split + 1, text.length())) | ||
} | ||
} else { | ||
mCurrentClassName = null | ||
mCurrentCodes = null | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
HashMap<String, ArrayList<String>> getCode(String className) { | ||
return mCodeMap.get(className) | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
skin-maker-plugin/src/test/java/com/qmuiteam/qmui/skinMaker/ExampleUnitTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Tencent is pleased to support the open source community by making QMUI_Android available. | ||
* | ||
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved. | ||
* | ||
* Licensed under the MIT License (the "License"); you may not use this file except in | ||
* compliance with the License. You may obtain a copy of the License at | ||
* | ||
* http://opensource.org/licenses/MIT | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License is | ||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
* either express or implied. See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.qmuiteam.qmui.skinMaker; | ||
|
||
import org.junit.Test; | ||
|
||
import static org.junit.Assert.*; | ||
|
||
/** | ||
* Example local unit test, which will execute on the development machine (host). | ||
* | ||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | ||
*/ | ||
public class ExampleUnitTest { | ||
@Test | ||
public void addition_isCorrect() { | ||
assertEquals(4, 2 + 2); | ||
} | ||
} |