Compare commits
7 Commits
fix/GHZSCY
...
feat/AGP8-
| Author | SHA1 | Date | |
|---|---|---|---|
| 93d16d3f56 | |||
| 31c8aad3c4 | |||
| e805be14ce | |||
| 12e2418e09 | |||
| 4db861c9f6 | |||
| 8925e935f1 | |||
| df1247e173 |
@ -5,10 +5,12 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'therouter'
|
||||
apply plugin: 'com.sensorsdata.analytics.android'
|
||||
|
||||
import groovy.xml.XmlUtil
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter'
|
||||
|
||||
String CONFIG_ID = ""
|
||||
String FIRST_LAUNCH = ""
|
||||
@ -21,6 +23,7 @@ android {
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
dataBinding true
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@ -183,6 +186,16 @@ android {
|
||||
|
||||
flavorDimensions("env", "region")
|
||||
|
||||
sensorsAnalytics {
|
||||
sdk {
|
||||
disableIMEI = true
|
||||
disableCarrier = true
|
||||
disableMacAddress = true
|
||||
}
|
||||
|
||||
disableModules = ['AUTOTRACK', 'PUSH']
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
debug {
|
||||
|
||||
@ -7,6 +7,61 @@
|
||||
# Keep Attribute
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable
|
||||
|
||||
# Remove log related code
|
||||
-assumenosideeffects class android.util.Log {
|
||||
public static *** v(...);
|
||||
public static *** d(...);
|
||||
public static *** i(...);
|
||||
public static *** w(...);
|
||||
public static *** e(...);
|
||||
public static *** println(...);
|
||||
public static *** isLoggable(...);
|
||||
}
|
||||
-assumenosideeffects class java.lang.Throwable {
|
||||
public *** printStackTrace(...);
|
||||
}
|
||||
-assumenosideeffects class java.io.PrintStream {
|
||||
public *** println(...);
|
||||
public *** print(...);
|
||||
}
|
||||
-assumenosideeffects class com.google.devtools.build.android.desugar.runtime.ThrowableExtension {
|
||||
public *** printStackTrace(...);
|
||||
}
|
||||
-assumenosideeffects class com.lightgame.utils.Utils {
|
||||
public static *** log(...);
|
||||
}
|
||||
-assumenosideeffects class com.gh.gamecenter.core.utils.MtaHelper {
|
||||
public static *** onEvent(...);
|
||||
public static *** onEventWithTime(...);
|
||||
public static *** onEventWithBasicDeviceInfo(...);
|
||||
}
|
||||
# Remove all logging calls via JDK Loggers. They are generally from
|
||||
# unused parts of third-party libraries.
|
||||
-assumenosideeffects class java.util.logging.Logger {
|
||||
void finest(...);
|
||||
void finer(...);
|
||||
void fine(...);
|
||||
void info(...);
|
||||
void warning(...);
|
||||
void severe(...);
|
||||
void throwing(...);
|
||||
void log(...);
|
||||
void logp(...);
|
||||
static java.util.logging.Logger getLogger(...) return _NONNULL_;
|
||||
boolean isLoggable(...) return false;
|
||||
}
|
||||
# Remove accesses to Level.<thing> that go unused.
|
||||
-assumenosideeffects class java.util.logging.Level {
|
||||
<fields>;
|
||||
# Flogger uses Level objects, so do not set a return value for intValue().
|
||||
int intValue();
|
||||
}
|
||||
# Remove fields of type Logger.
|
||||
-assumenosideeffects class * {
|
||||
java.util.logging.Logger * return _NONNULL_;
|
||||
}
|
||||
|
||||
|
||||
# OrmLite
|
||||
-keep class com.j256.*
|
||||
-keepclassmembers class com.j256.* { *; }
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<queries>
|
||||
<package android:name="com.gh.gamecenter" />
|
||||
|
||||
@ -16,13 +16,13 @@ import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.ndownload.suspendwindow.NDownloadDrawOverlayPermissionWindowController;
|
||||
import com.gh.ndownload.suspendwindow.NDownloadSuspendWindowController;
|
||||
import com.gh.ndownload.suspendwindow.utils.NDownloadSuspendWindowHelper;
|
||||
import com.lightgame.BuildConfig;
|
||||
import com.lightgame.download.DownloadConfig;
|
||||
import com.lightgame.download.DownloadDao;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
|
||||
19
build.gradle
19
build.gradle
@ -18,16 +18,16 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.1.3"
|
||||
classpath 'com.android.tools.build:gradle:8.0.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.sensorsdata.analytics.android:android-gradle-plugin2:3.5.3'
|
||||
classpath 'com.sensorsdata.analytics.android:android-gradle-plugin2:4.0.6'
|
||||
classpath "com.lg.shadow.core:gradle-plugin:$shadow_version"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.google.devtools.ksp' version '1.9.24-1.0.20' apply false
|
||||
id 'cn.therouter' version '1.2.2' apply false
|
||||
id 'cn.therouter.agp8' version '1.2.4' apply false
|
||||
}
|
||||
apply from: 'dependencies.gradle'
|
||||
apply from: 'vspace-bridge/config.gradle'
|
||||
@ -64,6 +64,19 @@ allprojects {
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
subprojects {
|
||||
afterEvaluate { project ->
|
||||
if (project.hasProperty('android')) {
|
||||
project.android {
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
subproject ->
|
||||
afterEvaluate {
|
||||
|
||||
@ -18,7 +18,7 @@ apply plugin: 'kotlin'
|
||||
dependencies {
|
||||
implementation gradleApi()
|
||||
implementation localGroovy()
|
||||
implementation "com.android.tools.build:gradle:7.1.3"
|
||||
implementation 'com.android.tools.build:gradle:8.0.2'
|
||||
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
implementation "commons-io:commons-io:2.4"
|
||||
implementation "org.javassist:javassist:3.25.0-GA"
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
abstract class ActivityStartActivityForResultVisitorFactory :
|
||||
AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor,
|
||||
): ClassVisitor {
|
||||
return ActivityStartActivityForResultVisitor(nextClassVisitor)
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return classData.className.contains("androidx.activity.ComponentActivity")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ActivityStartActivityForResultVisitor(nextVisitor: ClassVisitor) :
|
||||
ClassVisitor(Opcodes.ASM9, nextVisitor) {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?,
|
||||
): MethodVisitor {
|
||||
val originalMv = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
|
||||
// Only modify the run() method
|
||||
return if (name == "startActivityForResult") {
|
||||
println("ActivityStartActivityForResultVisitor found startActivityForResult method")
|
||||
TryCatchMethodVisitor(Opcodes.ASM9, originalMv, access, name, descriptor)
|
||||
} else {
|
||||
originalMv
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
abstract class AppCompatEditTextVisitorFactory
|
||||
: AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor,
|
||||
): ClassVisitor {
|
||||
return AppCompatEditTextVisitor(nextClassVisitor)
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return classData.className.contains("androidx.appcompat.widget.AppCompatEditText")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AppCompatEditTextVisitor(nextVisitor: ClassVisitor) :
|
||||
ClassVisitor(Opcodes.ASM9, nextVisitor) {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?,
|
||||
): MethodVisitor {
|
||||
val originalMv = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
|
||||
return if (name == "onTextContextMenuItem") {
|
||||
TryCatchMethodVisitor(Opcodes.ASM9, originalMv, access, name, descriptor, true)
|
||||
} else {
|
||||
originalMv
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
abstract class DuplicateClassVisitorFactory : AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor
|
||||
): ClassVisitor {
|
||||
val className = classContext.currentClassData.className.replace('.', '/')
|
||||
return DuplicateClassVisitor(nextClassVisitor, className)
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class DuplicateClassVisitor(nextVisitor: ClassVisitor, private val className: String)
|
||||
: ClassVisitor(Opcodes.ASM9, nextVisitor) {
|
||||
|
||||
private val duplicatedPackages = listOf(
|
||||
"com/google/android/material/chip",
|
||||
"com/google/android/material/textfield",
|
||||
"com/google/android/material/navigation",
|
||||
"com/google/android/material/datepicker",
|
||||
"com/google/android/material/circularreveal",
|
||||
"com/google/android/material/timepicker",
|
||||
"com/google/android/material/divider",
|
||||
"com/google/android/material/slider",
|
||||
"com/google/android/material/card",
|
||||
"com/google/android/material/transformation",
|
||||
"com/google/android/material/snackbar",
|
||||
"com/google/android/material/switchmaterial",
|
||||
"com/google/android/material/bottomappbar",
|
||||
"com/google/android/material/behavior",
|
||||
"com/google/android/material/floatingactionbutton",
|
||||
)
|
||||
|
||||
override fun visitEnd() {
|
||||
val isDuplicated = duplicatedPackages.any { className.contains(it) }
|
||||
if (isDuplicated) {
|
||||
println("DuplicateClassVisitor found duplicated class $className")
|
||||
} else {
|
||||
super.visitEnd()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,20 +1,55 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.gradle.AppExtension
|
||||
import com.android.build.api.instrumentation.FramesComputationMode
|
||||
import com.android.build.api.instrumentation.InstrumentationScope
|
||||
import com.android.build.api.variant.AndroidComponentsExtension
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
class GhPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
val log = project.logger
|
||||
log.error("========================")
|
||||
log.error("光环 transfrom 插件启动")
|
||||
log.error("========================")
|
||||
project.extensions.findByType(AppExtension::class.java)?.registerTransform(GhTransform(project))
|
||||
project.extensions.findByType(AppExtension::class.java)?.registerTransform(PluginAssetTransform())
|
||||
log.error("========================")
|
||||
log.error("光环 transfrom 插件结束")
|
||||
log.error("========================")
|
||||
val appExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)
|
||||
|
||||
appExtension.onVariants { variant ->
|
||||
// 移除无用的类
|
||||
// variant.instrumentation.transformClassesWith(
|
||||
// DuplicateClassVisitorFactory::class.java,
|
||||
// InstrumentationScope.ALL
|
||||
// ) {}
|
||||
|
||||
// plugin asset 目录处理
|
||||
variant.instrumentation.transformClassesWith(
|
||||
PluginAssetClassVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) {}
|
||||
|
||||
// Room 闪退 try catch 处理
|
||||
variant.instrumentation.transformClassesWith(
|
||||
RoomClassVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) {}
|
||||
|
||||
// ComponentActivity startActivityForResult 闪退 try catch 处理
|
||||
variant.instrumentation.transformClassesWith(
|
||||
ActivityStartActivityForResultVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) {}
|
||||
|
||||
// AppCompatEditText 闪退 try catch 处理 (主要出现在 VIVO 设备上)
|
||||
variant.instrumentation.transformClassesWith(
|
||||
AppCompatEditTextVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) {}
|
||||
|
||||
// QQ 小游戏获取系统 UA 的处理,避免提前初始化 WebView 导致启动耗时
|
||||
variant.instrumentation.transformClassesWith(
|
||||
MiniGameWebViewVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) {}
|
||||
|
||||
variant.instrumentation.setAsmFramesComputationMode(FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,202 +1,202 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.transform.Format
|
||||
import com.android.build.api.transform.QualifiedContent
|
||||
import com.android.build.api.transform.Transform
|
||||
import com.android.build.api.transform.TransformInvocation
|
||||
import com.android.build.gradle.AppExtension
|
||||
import com.android.build.gradle.internal.pipeline.TransformManager
|
||||
import com.gh.gamecenter.plugin.transform.*
|
||||
import javassist.ClassPool
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.gradle.api.Project
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.util.jar.JarEntry
|
||||
import java.util.jar.JarFile
|
||||
import java.util.jar.JarOutputStream
|
||||
|
||||
class GhTransform(var project: Project) : Transform() {
|
||||
|
||||
private val mClassPool = ClassPool.getDefault()
|
||||
private val mTransformHelper = GhTransformHelper()
|
||||
private val mExcludePackages = listOf(
|
||||
"com/google/android/material/chip",
|
||||
"com/google/android/material/textfield",
|
||||
"com/google/android/material/navigation",
|
||||
"com/google/android/material/datepicker",
|
||||
"com/google/android/material/circularreveal",
|
||||
"com/google/android/material/timepicker",
|
||||
"com/google/android/material/divider",
|
||||
"com/google/android/material/slider",
|
||||
"com/google/android/material/card",
|
||||
"com/google/android/material/transformation",
|
||||
"com/google/android/material/snackbar",
|
||||
"com/google/android/material/switchmaterial",
|
||||
"com/google/android/material/bottomappbar",
|
||||
"com/google/android/material/behavior",
|
||||
"com/google/android/material/floatingactionbutton",
|
||||
)
|
||||
|
||||
override fun getName() = "GhTransform"
|
||||
|
||||
init {
|
||||
mTransformHelper.addTransformer(ExoSourceManagerTransformer())
|
||||
mTransformHelper.addTransformer(DiskLruCacheTransformer())
|
||||
mTransformHelper.addTransformer(RoomTransformer())
|
||||
mTransformHelper.addTransformer(ActivityTransformer())
|
||||
mTransformHelper.addTransformer(AppCompatEditTextTransformer())
|
||||
mTransformHelper.addTransformer(MiniGameWebViewTransformer())
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要处理的数据类型,目前 ContentType有六种枚举类型,通常我们使用比较频繁的有前两种:
|
||||
* 1、CONTENT_CLASS:表示需要处理 java 的 class 文件。
|
||||
* 2、CONTENT_JARS:表示需要处理 java 的 class 与 资源文件。
|
||||
* 3、CONTENT_RESOURCES:表示需要处理 java 的资源文件。
|
||||
* 4、CONTENT_NATIVE_LIBS:表示需要处理 native 库的代码。
|
||||
* 5、CONTENT_DEX:表示需要处理 DEX 文件。
|
||||
* 6、CONTENT_DEX_WITH_RESOURCES:表示需要处理 DEX 与 java 的资源文件。
|
||||
*/
|
||||
override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> {
|
||||
return TransformManager.CONTENT_CLASS
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否增量编译
|
||||
*/
|
||||
// TODO Why not?
|
||||
override fun isIncremental() = false
|
||||
|
||||
/**
|
||||
* Transform 要操作的内容范围,目前 Scope 有五种基本类型:
|
||||
* 1、PROJECT:只有项目内容
|
||||
* 2、SUB_PROJECTS:只有子项目
|
||||
* 3、EXTERNAL_LIBRARIES:只有外部库
|
||||
* 4、TESTED_CODE:由当前变体(包括依赖项)所测试的代码
|
||||
* 5、PROVIDED_ONLY:只提供本地或远程依赖项
|
||||
* SCOPE_FULL_PROJECT 是一个Scope集合,包含Scope.PROJECT,Scope.SUB_PROJECTS,Scope.EXTERNAL_LIBRARIES 这三项,即当前Transform的作用域包括当前项目、子项目以及外部的依赖库
|
||||
*/
|
||||
override fun getScopes(): MutableSet<in QualifiedContent.Scope> {
|
||||
// 通常我们使用 SCOPE_FULL_PROJECT
|
||||
return TransformManager.SCOPE_FULL_PROJECT
|
||||
}
|
||||
|
||||
override fun transform(transformInvocation: TransformInvocation?) {
|
||||
super.transform(transformInvocation)
|
||||
|
||||
// 添加 android.jar path
|
||||
mClassPool?.appendClassPath((project.extensions.findByType(AppExtension::class.java)!!.bootClasspath[0].toString()))
|
||||
|
||||
val outputProvider = transformInvocation?.outputProvider
|
||||
outputProvider?.deleteAll()
|
||||
|
||||
transformInvocation?.inputs?.forEach { input ->
|
||||
input.directoryInputs.forEach { dirInput ->
|
||||
handleDirectory(dirInput.file)
|
||||
|
||||
// 将input的目录复制到output指定目录
|
||||
val dest = outputProvider?.getContentLocation(
|
||||
dirInput.name,
|
||||
dirInput.contentTypes,
|
||||
dirInput.scopes,
|
||||
Format.DIRECTORY
|
||||
)
|
||||
FileUtils.copyDirectory(dirInput.file, dest)
|
||||
}
|
||||
}
|
||||
|
||||
var index = 0
|
||||
transformInvocation?.inputs?.forEach { input ->
|
||||
input.jarInputs.forEach { jarInput ->
|
||||
if (jarInput.file.exists()) {
|
||||
val srcFile = handleJar(jarInput.file)
|
||||
// 必须给jar重新命名,否则会冲突
|
||||
var jarName = jarInput.name
|
||||
if (jarName.endsWith(".jar")) {
|
||||
jarName = jarName.substring(0, jarName.length - 4)
|
||||
}
|
||||
val dest = outputProvider?.getContentLocation(
|
||||
jarName + "_" + index,
|
||||
jarInput.contentTypes,
|
||||
jarInput.scopes,
|
||||
Format.JAR
|
||||
)
|
||||
FileUtils.copyFile(srcFile, dest)
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDirectory(dir: File) {
|
||||
// 将类路径添加到 classPool 中
|
||||
mClassPool.insertClassPath(dir.absolutePath)
|
||||
|
||||
if (dir.isDirectory) {
|
||||
dir.walkTopDown().forEach { file ->
|
||||
val filePath = file.absolutePath
|
||||
mClassPool.insertClassPath(filePath)
|
||||
if (shouldModify(filePath)) {
|
||||
val inputStream = FileInputStream(file)
|
||||
mTransformHelper.proceedModifyDir(filePath, inputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主要步骤:
|
||||
* 1.遍历所有jar文件
|
||||
* 2.解压jar然后遍历所有的class
|
||||
* 3.读取class的输入流并使用javassit修改,然后保存到新的jar文件中
|
||||
*/
|
||||
fun handleJar(jarFile: File): File {
|
||||
mClassPool.appendClassPath(jarFile.absolutePath)
|
||||
val inputJarFile = JarFile(jarFile)
|
||||
val entries = inputJarFile.entries()
|
||||
//创建一个新的文件
|
||||
val outputJarFile = File(jarFile.parentFile, "temp_" + jarFile.name)
|
||||
if (outputJarFile.exists()) outputJarFile.delete()
|
||||
val jarOutputStream = JarOutputStream(BufferedOutputStream(FileOutputStream(outputJarFile)))
|
||||
while (entries.hasMoreElements()) {
|
||||
val jarInputEntry = entries.nextElement()
|
||||
val jarInputEntryName = jarInputEntry.name
|
||||
var isIgnore = false
|
||||
for (i in mExcludePackages.indices){
|
||||
if (jarInputEntryName.contains(mExcludePackages[i])) {
|
||||
isIgnore = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (isIgnore) continue
|
||||
val outputJarEntry = JarEntry(jarInputEntryName)
|
||||
jarOutputStream.putNextEntry(outputJarEntry)
|
||||
|
||||
val inputStream = inputJarFile.getInputStream(jarInputEntry)
|
||||
if (!shouldModify(jarInputEntryName)) {
|
||||
jarOutputStream.write(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
continue
|
||||
}
|
||||
mTransformHelper.proceedModifyJar(jarInputEntryName, jarOutputStream, inputStream)
|
||||
}
|
||||
inputJarFile.close()
|
||||
jarOutputStream.closeEntry()
|
||||
jarOutputStream.flush()
|
||||
jarOutputStream.close()
|
||||
return outputJarFile
|
||||
}
|
||||
|
||||
private fun shouldModify(filePath: String): Boolean {
|
||||
return filePath.endsWith(".class")
|
||||
&& !filePath.contains("R.class")
|
||||
// && !filePath.contains("$")
|
||||
&& !filePath.contains("R$")
|
||||
&& !filePath.contains("BuildConfig.class")
|
||||
}
|
||||
|
||||
}
|
||||
//package com.gh.gamecenter.plugin
|
||||
//
|
||||
//import com.android.build.api.transform.Format
|
||||
//import com.android.build.api.transform.QualifiedContent
|
||||
//import com.android.build.api.transform.Transform
|
||||
//import com.android.build.api.transform.TransformInvocation
|
||||
//import com.android.build.gradle.AppExtension
|
||||
//import com.android.build.gradle.internal.pipeline.TransformManager
|
||||
//import com.gh.gamecenter.plugin.transform.*
|
||||
//import javassist.ClassPool
|
||||
//import org.apache.commons.io.FileUtils
|
||||
//import org.apache.commons.io.IOUtils
|
||||
//import org.gradle.api.Project
|
||||
//import java.io.BufferedOutputStream
|
||||
//import java.io.File
|
||||
//import java.io.FileInputStream
|
||||
//import java.io.FileOutputStream
|
||||
//import java.util.jar.JarEntry
|
||||
//import java.util.jar.JarFile
|
||||
//import java.util.jar.JarOutputStream
|
||||
//
|
||||
//class GhTransform(var project: Project) : Transform() {
|
||||
//
|
||||
// private val mClassPool = ClassPool.getDefault()
|
||||
// private val mTransformHelper = GhTransformHelper()
|
||||
// private val mExcludePackages = listOf(
|
||||
// "com/google/android/material/chip",
|
||||
// "com/google/android/material/textfield",
|
||||
// "com/google/android/material/navigation",
|
||||
// "com/google/android/material/datepicker",
|
||||
// "com/google/android/material/circularreveal",
|
||||
// "com/google/android/material/timepicker",
|
||||
// "com/google/android/material/divider",
|
||||
// "com/google/android/material/slider",
|
||||
// "com/google/android/material/card",
|
||||
// "com/google/android/material/transformation",
|
||||
// "com/google/android/material/snackbar",
|
||||
// "com/google/android/material/switchmaterial",
|
||||
// "com/google/android/material/bottomappbar",
|
||||
// "com/google/android/material/behavior",
|
||||
// "com/google/android/material/floatingactionbutton",
|
||||
// )
|
||||
//
|
||||
// override fun getName() = "GhTransform"
|
||||
//
|
||||
// init {
|
||||
// mTransformHelper.addTransformer(ExoSourceManagerTransformer())
|
||||
// mTransformHelper.addTransformer(DiskLruCacheTransformer())
|
||||
// mTransformHelper.addTransformer(RoomTransformer())
|
||||
// mTransformHelper.addTransformer(ActivityTransformer())
|
||||
// mTransformHelper.addTransformer(AppCompatEditTextTransformer())
|
||||
// mTransformHelper.addTransformer(MiniGameWebViewTransformer())
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 需要处理的数据类型,目前 ContentType有六种枚举类型,通常我们使用比较频繁的有前两种:
|
||||
// * 1、CONTENT_CLASS:表示需要处理 java 的 class 文件。
|
||||
// * 2、CONTENT_JARS:表示需要处理 java 的 class 与 资源文件。
|
||||
// * 3、CONTENT_RESOURCES:表示需要处理 java 的资源文件。
|
||||
// * 4、CONTENT_NATIVE_LIBS:表示需要处理 native 库的代码。
|
||||
// * 5、CONTENT_DEX:表示需要处理 DEX 文件。
|
||||
// * 6、CONTENT_DEX_WITH_RESOURCES:表示需要处理 DEX 与 java 的资源文件。
|
||||
// */
|
||||
// override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> {
|
||||
// return TransformManager.CONTENT_CLASS
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 是否增量编译
|
||||
// */
|
||||
// // TODO Why not?
|
||||
// override fun isIncremental() = false
|
||||
//
|
||||
// /**
|
||||
// * Transform 要操作的内容范围,目前 Scope 有五种基本类型:
|
||||
// * 1、PROJECT:只有项目内容
|
||||
// * 2、SUB_PROJECTS:只有子项目
|
||||
// * 3、EXTERNAL_LIBRARIES:只有外部库
|
||||
// * 4、TESTED_CODE:由当前变体(包括依赖项)所测试的代码
|
||||
// * 5、PROVIDED_ONLY:只提供本地或远程依赖项
|
||||
// * SCOPE_FULL_PROJECT 是一个Scope集合,包含Scope.PROJECT,Scope.SUB_PROJECTS,Scope.EXTERNAL_LIBRARIES 这三项,即当前Transform的作用域包括当前项目、子项目以及外部的依赖库
|
||||
// */
|
||||
// override fun getScopes(): MutableSet<in QualifiedContent.Scope> {
|
||||
// // 通常我们使用 SCOPE_FULL_PROJECT
|
||||
// return TransformManager.SCOPE_FULL_PROJECT
|
||||
// }
|
||||
//
|
||||
// override fun transform(transformInvocation: TransformInvocation?) {
|
||||
// super.transform(transformInvocation)
|
||||
//
|
||||
// // 添加 android.jar path
|
||||
// mClassPool?.appendClassPath((project.extensions.findByType(AppExtension::class.java)!!.bootClasspath[0].toString()))
|
||||
//
|
||||
// val outputProvider = transformInvocation?.outputProvider
|
||||
// outputProvider?.deleteAll()
|
||||
//
|
||||
// transformInvocation?.inputs?.forEach { input ->
|
||||
// input.directoryInputs.forEach { dirInput ->
|
||||
// handleDirectory(dirInput.file)
|
||||
//
|
||||
// // 将input的目录复制到output指定目录
|
||||
// val dest = outputProvider?.getContentLocation(
|
||||
// dirInput.name,
|
||||
// dirInput.contentTypes,
|
||||
// dirInput.scopes,
|
||||
// Format.DIRECTORY
|
||||
// )
|
||||
// FileUtils.copyDirectory(dirInput.file, dest)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var index = 0
|
||||
// transformInvocation?.inputs?.forEach { input ->
|
||||
// input.jarInputs.forEach { jarInput ->
|
||||
// if (jarInput.file.exists()) {
|
||||
// val srcFile = handleJar(jarInput.file)
|
||||
// // 必须给jar重新命名,否则会冲突
|
||||
// var jarName = jarInput.name
|
||||
// if (jarName.endsWith(".jar")) {
|
||||
// jarName = jarName.substring(0, jarName.length - 4)
|
||||
// }
|
||||
// val dest = outputProvider?.getContentLocation(
|
||||
// jarName + "_" + index,
|
||||
// jarInput.contentTypes,
|
||||
// jarInput.scopes,
|
||||
// Format.JAR
|
||||
// )
|
||||
// FileUtils.copyFile(srcFile, dest)
|
||||
// index++
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun handleDirectory(dir: File) {
|
||||
// // 将类路径添加到 classPool 中
|
||||
// mClassPool.insertClassPath(dir.absolutePath)
|
||||
//
|
||||
// if (dir.isDirectory) {
|
||||
// dir.walkTopDown().forEach { file ->
|
||||
// val filePath = file.absolutePath
|
||||
// mClassPool.insertClassPath(filePath)
|
||||
// if (shouldModify(filePath)) {
|
||||
// val inputStream = FileInputStream(file)
|
||||
// mTransformHelper.proceedModifyDir(filePath, inputStream)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 主要步骤:
|
||||
// * 1.遍历所有jar文件
|
||||
// * 2.解压jar然后遍历所有的class
|
||||
// * 3.读取class的输入流并使用javassit修改,然后保存到新的jar文件中
|
||||
// */
|
||||
// fun handleJar(jarFile: File): File {
|
||||
// mClassPool.appendClassPath(jarFile.absolutePath)
|
||||
// val inputJarFile = JarFile(jarFile)
|
||||
// val entries = inputJarFile.entries()
|
||||
// //创建一个新的文件
|
||||
// val outputJarFile = File(jarFile.parentFile, "temp_" + jarFile.name)
|
||||
// if (outputJarFile.exists()) outputJarFile.delete()
|
||||
// val jarOutputStream = JarOutputStream(BufferedOutputStream(FileOutputStream(outputJarFile)))
|
||||
// while (entries.hasMoreElements()) {
|
||||
// val jarInputEntry = entries.nextElement()
|
||||
// val jarInputEntryName = jarInputEntry.name
|
||||
// var isIgnore = false
|
||||
// for (i in mExcludePackages.indices){
|
||||
// if (jarInputEntryName.contains(mExcludePackages[i])) {
|
||||
// isIgnore = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if (isIgnore) continue
|
||||
// val outputJarEntry = JarEntry(jarInputEntryName)
|
||||
// jarOutputStream.putNextEntry(outputJarEntry)
|
||||
//
|
||||
// val inputStream = inputJarFile.getInputStream(jarInputEntry)
|
||||
// if (!shouldModify(jarInputEntryName)) {
|
||||
// jarOutputStream.write(IOUtils.toByteArray(inputStream))
|
||||
// inputStream.close()
|
||||
// continue
|
||||
// }
|
||||
// mTransformHelper.proceedModifyJar(jarInputEntryName, jarOutputStream, inputStream)
|
||||
// }
|
||||
// inputJarFile.close()
|
||||
// jarOutputStream.closeEntry()
|
||||
// jarOutputStream.flush()
|
||||
// jarOutputStream.close()
|
||||
// return outputJarFile
|
||||
// }
|
||||
//
|
||||
// private fun shouldModify(filePath: String): Boolean {
|
||||
// return filePath.endsWith(".class")
|
||||
// && !filePath.contains("R.class")
|
||||
//// && !filePath.contains("$")
|
||||
// && !filePath.contains("R$")
|
||||
// && !filePath.contains("BuildConfig.class")
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@ -1,56 +0,0 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.gh.gamecenter.plugin.transform.Transformer
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.jar.JarOutputStream
|
||||
|
||||
class GhTransformHelper {
|
||||
|
||||
private val transformers = ArrayList<Transformer>()
|
||||
|
||||
fun addTransformer(transformer: Transformer) {
|
||||
transformers.add(transformer)
|
||||
}
|
||||
|
||||
fun proceedModifyDir(filePath: String, inputStream: InputStream) {
|
||||
transformers.forEach {
|
||||
val ctClass = it.modifyClass(filePath, inputStream)
|
||||
ctClass?.let {
|
||||
ctClass.writeFile()
|
||||
|
||||
// 释放内存
|
||||
ctClass.detach()
|
||||
}
|
||||
}
|
||||
inputStream.close()
|
||||
}
|
||||
|
||||
fun proceedModifyJar(filePath: String, jarOutputStream: JarOutputStream, iStream: InputStream) {
|
||||
var byteCode: ByteArray? = null
|
||||
var inputStream = iStream
|
||||
transformers.forEach {
|
||||
if (byteCode != null) {
|
||||
inputStream = ByteArrayInputStream(byteCode)
|
||||
}
|
||||
|
||||
val ctClass = it.modifyClass(filePath, inputStream)
|
||||
ctClass?.let {
|
||||
byteCode = ctClass.toBytecode()
|
||||
|
||||
// 释放内存
|
||||
ctClass.detach()
|
||||
}
|
||||
}
|
||||
|
||||
if (byteCode == null) {
|
||||
byteCode = IOUtils.toByteArray(inputStream)
|
||||
}
|
||||
|
||||
jarOutputStream.write(byteCode!!)
|
||||
jarOutputStream.flush()
|
||||
inputStream.close()
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.commons.AdviceAdapter
|
||||
|
||||
abstract class MiniGameWebViewVisitorFactory :
|
||||
AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor,
|
||||
): ClassVisitor {
|
||||
val targetClass = classContext.currentClassData.className.replace('.', '/')
|
||||
return MiniGameWebViewVisitor(nextClassVisitor, targetClass)
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return classData.className.contains("QUAUtil")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MiniGameWebViewVisitor(nextVisitor: ClassVisitor, private val targetClass: String) :
|
||||
ClassVisitor(Opcodes.ASM9, nextVisitor) {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?,
|
||||
): MethodVisitor {
|
||||
val originalMv = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
|
||||
// Only modify the run() method
|
||||
return if (name == "getSystemUA" && descriptor.equals("()Ljava/lang/String;")) {
|
||||
MiniGameWebViewAdviceAdapter(
|
||||
Opcodes.ASM9,
|
||||
originalMv,
|
||||
access,
|
||||
name,
|
||||
descriptor,
|
||||
targetClass
|
||||
)
|
||||
} else {
|
||||
originalMv
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MiniGameWebViewAdviceAdapter(
|
||||
api: Int,
|
||||
mv: MethodVisitor,
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String?,
|
||||
private val targetClass: String,
|
||||
) : AdviceAdapter(api, mv, access, name, descriptor) {
|
||||
|
||||
override fun onMethodEnter() {
|
||||
val l0 = newLabel()
|
||||
val l1 = newLabel()
|
||||
|
||||
// if (systemUA != null) {
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, targetClass, "systemUA", "Ljava/lang/String;")
|
||||
mv.visitJumpInsn(Opcodes.IFNULL, l0)
|
||||
|
||||
// return systemUA;
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, targetClass, "systemUA", "Ljava/lang/String;")
|
||||
mv.visitInsn(Opcodes.ARETURN)
|
||||
|
||||
mv.visitLabel(l0)
|
||||
|
||||
// String property = System.getProperty("http.agent");
|
||||
mv.visitLdcInsn("http.agent")
|
||||
mv.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/lang/System",
|
||||
"getProperty",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;",
|
||||
false
|
||||
)
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 1) // Store the property in a local variable
|
||||
|
||||
// if (property == null) { return null; }
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1)
|
||||
mv.visitJumpInsn(Opcodes.IFNULL, l1)
|
||||
|
||||
// systemUA = java.net.URLEncoder.encode(property, "UTF-8");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1)
|
||||
mv.visitLdcInsn("UTF-8")
|
||||
mv.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/net/URLEncoder",
|
||||
"encode",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
|
||||
false
|
||||
)
|
||||
mv.visitFieldInsn(Opcodes.PUTSTATIC, targetClass, "systemUA", "Ljava/lang/String;")
|
||||
|
||||
// return systemUA;
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, targetClass, "systemUA", "Ljava/lang/String;")
|
||||
mv.visitInsn(Opcodes.ARETURN)
|
||||
|
||||
mv.visitLabel(l1)
|
||||
mv.visitInsn(Opcodes.ACONST_NULL)
|
||||
mv.visitFieldInsn(Opcodes.PUTSTATIC, targetClass, "systemUA", "Ljava/lang/String;")
|
||||
|
||||
mv.visitInsn(Opcodes.ACONST_NULL)
|
||||
mv.visitInsn(Opcodes.ARETURN)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
abstract class PluginAssetClassVisitorFactory:
|
||||
AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor,
|
||||
): ClassVisitor {
|
||||
return PluginAssetClassVisitor(nextClassVisitor)
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return !classData.className.contains("PluginRedirectHelper")
|
||||
&& !classData.className.contains("BuildConfig")
|
||||
&& !classData.className.endsWith(".R")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PluginAssetClassVisitor(nextVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM9, nextVisitor) {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?,
|
||||
): MethodVisitor {
|
||||
val mv = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
return AssetOpenMethodVisitor(mv)
|
||||
}
|
||||
|
||||
class AssetOpenMethodVisitor(mv: MethodVisitor) : MethodVisitor(Opcodes.ASM9, mv) {
|
||||
override fun visitMethodInsn(
|
||||
opcode: Int, owner: String, name: String,
|
||||
descriptor: String, isInterface: Boolean,
|
||||
) {
|
||||
|
||||
// Print all method call in AssetManager
|
||||
if ("android/content/res/AssetManager" == owner) {
|
||||
println("owener:$owner method:$name opcode:$opcode desc:$descriptor")
|
||||
}
|
||||
|
||||
// Replace AssetManager.open with com.gh.gamecenter.core.utils.PluginRedirectHelper.openAsset
|
||||
if ("android/content/res/AssetManager" == owner
|
||||
&& "open" == name
|
||||
&& opcode == Opcodes.INVOKEVIRTUAL
|
||||
&& "(Ljava/lang/String;)Ljava/io/InputStream;" == descriptor
|
||||
) {
|
||||
println("owener:$owner method:$name opcode:$opcode desc:$descriptor make it!")
|
||||
|
||||
return super.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"com/gh/gamecenter/core/utils/PluginRedirectHelper",
|
||||
"openAsset",
|
||||
"(Landroid/content/res/AssetManager;Ljava/lang/String;)Ljava/io/InputStream;",
|
||||
isInterface
|
||||
)
|
||||
}
|
||||
|
||||
// Replace AssetManager.list with com.gh.gamecenter.core.utils.PluginRedirectHelper.listAsset
|
||||
if ("android/content/res/AssetManager" == owner
|
||||
&& "list" == name
|
||||
&& opcode == Opcodes.INVOKEVIRTUAL
|
||||
&& "(Ljava/lang/String;)[Ljava/lang/String;" == descriptor
|
||||
) {
|
||||
|
||||
return super.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"com/gh/gamecenter/core/utils/PluginRedirectHelper",
|
||||
"listAsset",
|
||||
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
|
||||
isInterface
|
||||
)
|
||||
}
|
||||
return super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,201 +0,0 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.transform.Format
|
||||
import com.android.build.api.transform.Transform
|
||||
import com.android.build.api.transform.TransformInvocation
|
||||
import com.android.build.gradle.internal.pipeline.TransformManager
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.util.jar.JarFile
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
|
||||
class PluginAssetTransform : Transform() {
|
||||
|
||||
override fun getName() = "PluginTransform"
|
||||
|
||||
override fun getInputTypes() = TransformManager.CONTENT_CLASS
|
||||
|
||||
override fun getScopes() = TransformManager.SCOPE_FULL_PROJECT
|
||||
|
||||
override fun isIncremental() = false
|
||||
|
||||
override fun transform(transformInvocation: TransformInvocation) {
|
||||
if (!isIncremental) {
|
||||
transformInvocation.outputProvider.deleteAll()
|
||||
}
|
||||
|
||||
var index = 0
|
||||
|
||||
transformInvocation.inputs.forEach { input ->
|
||||
// 处理 dir 部分
|
||||
input.directoryInputs.forEach { directoryInput ->
|
||||
val outputDir = transformInvocation.outputProvider
|
||||
.getContentLocation(
|
||||
directoryInput.name,
|
||||
directoryInput.contentTypes,
|
||||
directoryInput.scopes,
|
||||
Format.DIRECTORY
|
||||
)
|
||||
|
||||
FileUtils.copyDirectory(directoryInput.file, outputDir)
|
||||
|
||||
directoryInput.file.walkTopDown().forEach { file ->
|
||||
if (file.isFile && file.extension == "class") {
|
||||
val classReader = ClassReader(FileInputStream(file))
|
||||
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
|
||||
val classVisitor = AssetOpenClassVisitor(classWriter)
|
||||
|
||||
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
|
||||
|
||||
val fos = FileOutputStream(File(outputDir, file.relativeTo(directoryInput.file).path))
|
||||
fos.write(classWriter.toByteArray())
|
||||
fos.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 jar 部分
|
||||
input.jarInputs.forEach { jarInput ->
|
||||
val jarFile = jarInput.file
|
||||
if (jarFile.exists() && jarFile.extension == "jar") {
|
||||
val modifiedJarFile = modifyJar(jarFile)
|
||||
// 必须给 jar 重新命名,否则会冲突
|
||||
var jarName = jarFile.name
|
||||
if (jarName.endsWith(".jar")) {
|
||||
jarName = jarName.substring(0, jarName.length - 4)
|
||||
}
|
||||
val dest = transformInvocation.outputProvider?.getContentLocation(
|
||||
jarName + "_" + index,
|
||||
jarInput.contentTypes,
|
||||
jarInput.scopes,
|
||||
Format.JAR
|
||||
)
|
||||
FileUtils.copyFile(modifiedJarFile, dest)
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主要步骤:
|
||||
* 1.遍历所有jar文件
|
||||
* 2.解压jar然后遍历所有的class
|
||||
* 3.读取class的输入流并使用 ASM 修改,然后保存到新的 jar 文件中
|
||||
*/
|
||||
fun modifyJar(jarFile: File): File {
|
||||
val jarInput = JarFile(jarFile)
|
||||
val jarInputEntries = jarInput.entries()
|
||||
|
||||
// 创建一个新的文件
|
||||
val jarOutput = File(jarFile.parentFile, "temp_" + jarFile.name)
|
||||
if (jarOutput.exists()) jarOutput.delete()
|
||||
|
||||
val jarOutputStream = JarOutputStream(BufferedOutputStream(FileOutputStream(jarOutput)))
|
||||
|
||||
while (jarInputEntries.hasMoreElements()) {
|
||||
val jarInputEntry = jarInputEntries.nextElement()
|
||||
val jarInputEntryName = jarInputEntry.name
|
||||
|
||||
val jarInputStream = jarInput.getInputStream(jarInputEntry)
|
||||
|
||||
val zipEntry = ZipEntry(jarInputEntryName)
|
||||
|
||||
jarOutputStream.putNextEntry(zipEntry)
|
||||
|
||||
if (shouldModify(jarInputEntryName)) {
|
||||
val classReader = ClassReader(IOUtils.toByteArray(jarInputStream))
|
||||
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
|
||||
val classVisitor = AssetOpenClassVisitor(classWriter)
|
||||
|
||||
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
|
||||
|
||||
jarOutputStream.write(classWriter.toByteArray())
|
||||
} else {
|
||||
jarOutputStream.write(IOUtils.toByteArray(jarInputStream))
|
||||
}
|
||||
|
||||
jarOutputStream.closeEntry()
|
||||
|
||||
jarInputStream.close()
|
||||
}
|
||||
jarInput.close()
|
||||
jarOutputStream.flush()
|
||||
jarOutputStream.close()
|
||||
return jarOutput
|
||||
}
|
||||
|
||||
private fun shouldModify(filePath: String): Boolean {
|
||||
return filePath.endsWith(".class")
|
||||
&& !filePath.contains("PluginRedirectHelper.class")
|
||||
&& !filePath.contains("R.class")
|
||||
&& !filePath.contains("R$")
|
||||
&& !filePath.contains("BuildConfig.class")
|
||||
}
|
||||
|
||||
class AssetOpenClassVisitor(cv: ClassVisitor) : ClassVisitor(Opcodes.ASM9, cv) {
|
||||
override fun visitMethod(
|
||||
access: Int, name: String, descriptor: String,
|
||||
signature: String?, exceptions: Array<String>?
|
||||
): MethodVisitor {
|
||||
val mv = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
return AssetOpenMethodVisitor(mv)
|
||||
}
|
||||
}
|
||||
|
||||
class AssetOpenMethodVisitor(mv: MethodVisitor) : MethodVisitor(Opcodes.ASM9, mv) {
|
||||
override fun visitMethodInsn(
|
||||
opcode: Int, owner: String, name: String,
|
||||
descriptor: String, isInterface: Boolean
|
||||
) {
|
||||
|
||||
// Print all method call in AssetManager
|
||||
if ("android/content/res/AssetManager" == owner) {
|
||||
println("owener:$owner method:$name opcode:$opcode desc:$descriptor")
|
||||
}
|
||||
|
||||
// Replace AssetManager.open with com.gh.gamecenter.core.utils.PluginRedirectHelper.openAsset
|
||||
if ("android/content/res/AssetManager" == owner
|
||||
&& "open" == name
|
||||
&& opcode == Opcodes.INVOKEVIRTUAL
|
||||
&& "(Ljava/lang/String;)Ljava/io/InputStream;" == descriptor) {
|
||||
|
||||
println("owener:$owner method:$name opcode:$opcode desc:$descriptor make it!")
|
||||
|
||||
return super.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"com/gh/gamecenter/core/utils/PluginRedirectHelper",
|
||||
"openAsset",
|
||||
"(Landroid/content/res/AssetManager;Ljava/lang/String;)Ljava/io/InputStream;",
|
||||
isInterface
|
||||
)
|
||||
}
|
||||
|
||||
// Replace AssetManager.list with com.gh.gamecenter.core.utils.PluginRedirectHelper.listAsset
|
||||
if ("android/content/res/AssetManager" == owner
|
||||
&& "list" == name
|
||||
&& opcode == Opcodes.INVOKEVIRTUAL
|
||||
&& "(Ljava/lang/String;)[Ljava/lang/String;" == descriptor) {
|
||||
|
||||
return super.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"com/gh/gamecenter/core/utils/PluginRedirectHelper",
|
||||
"listAsset",
|
||||
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
|
||||
isInterface)
|
||||
}
|
||||
return super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.commons.AdviceAdapter
|
||||
|
||||
abstract class RoomClassVisitorFactory : AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor,
|
||||
): ClassVisitor {
|
||||
return RoomClassVisitor(nextClassVisitor)
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return classData.className.contains("androidx.room.InvalidationTracker")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RoomClassVisitor(nextVisitor: ClassVisitor) :
|
||||
ClassVisitor(Opcodes.ASM9, nextVisitor) {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?,
|
||||
): MethodVisitor {
|
||||
val originalMv = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
|
||||
// Only modify the run() method
|
||||
return if (name == "run" && descriptor == "()V") {
|
||||
println("RoomClassVisitor found run method on $name")
|
||||
TryCatchMethodVisitor(Opcodes.ASM9, originalMv, access, name, descriptor)
|
||||
} else {
|
||||
originalMv
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.gh.gamecenter.plugin
|
||||
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.commons.AdviceAdapter
|
||||
|
||||
/**
|
||||
* A [MethodVisitor] that adds a try-catch block around the original method's code.
|
||||
* @param returnBoolean, if true, the catch block will return a default boolean value instead of void
|
||||
*/
|
||||
class TryCatchMethodVisitor(
|
||||
api: Int,
|
||||
mv: MethodVisitor,
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
private val returnBoolean: Boolean = false,
|
||||
) : AdviceAdapter(api, mv, access, name, descriptor) {
|
||||
|
||||
private val startLabel = org.objectweb.asm.Label()
|
||||
private val endLabel = org.objectweb.asm.Label()
|
||||
private val handlerLabel = org.objectweb.asm.Label()
|
||||
|
||||
override fun onMethodEnter() {
|
||||
// Start of the try block
|
||||
mv.visitLabel(startLabel)
|
||||
}
|
||||
|
||||
override fun onMethodExit(opcode: Int) {
|
||||
// End of try block. Only put it here if the opcode is not ATHROW
|
||||
if (opcode != Opcodes.ATHROW) {
|
||||
mv.visitLabel(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
|
||||
// Exception handler (placed after the original method's code)
|
||||
mv.visitLabel(handlerLabel)
|
||||
|
||||
// Store the exception in a local variable (slot 1)
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 1)
|
||||
|
||||
// Print the stack trace of the exception
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1)
|
||||
mv.visitMethodInsn(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Throwable",
|
||||
"printStackTrace",
|
||||
"()V",
|
||||
false
|
||||
)
|
||||
|
||||
if (!returnBoolean) {
|
||||
mv.visitInsn(Opcodes.RETURN)
|
||||
} else {
|
||||
mv.visitInsn(Opcodes.ICONST_0)
|
||||
mv.visitInsn(Opcodes.IRETURN)
|
||||
}
|
||||
|
||||
// Add the try-catch block to the method
|
||||
// Catch all Throwable exceptions
|
||||
mv.visitTryCatchBlock(
|
||||
startLabel,
|
||||
endLabel,
|
||||
handlerLabel,
|
||||
"java/lang/Throwable"
|
||||
)
|
||||
|
||||
// Adjust maxStack and maxLocals. The exact values might need fine-tuning
|
||||
// depending on the original method's complexity. We've added a few
|
||||
// instructions, so we need to increase these values.
|
||||
super.visitMaxs(maxStack + 2, maxLocals + 1)
|
||||
}
|
||||
}
|
||||
@ -118,14 +118,14 @@ ext {
|
||||
sentry = "6.20.0"
|
||||
|
||||
autoServiceVersion = "1.0-rc7"
|
||||
routerVersion = "1.2.2"
|
||||
routerVersion = "1.2.4"
|
||||
|
||||
composeVersion = "1.2.1"
|
||||
activityComposeVersion = "1.6.0"
|
||||
composeCompilerVersion = "1.5.14"
|
||||
constraintlayoutCompose = "1.0.1"
|
||||
|
||||
sensorsDataVersion = "6.8.0"
|
||||
sensorsDataVersion = "6.8.4"
|
||||
|
||||
documentfile = "1.0.1"
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.accelerator">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.csjad'
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.csjad">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!--必要权限-->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@ -5,6 +5,7 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.floatingwindow'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.floatingwindow">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
</application>
|
||||
|
||||
@ -6,6 +6,8 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.jg.push'
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.jg.push">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.hihonor.push.sdk,com.heytap.mcssdk" />
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "kotlin-kapt"
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.selector'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.selector">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
|
||||
@ -9,6 +9,7 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.feedback'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.feedback">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.feedback">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- 允许应用程序访问网络连接 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@ -6,6 +6,7 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.oaid'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.oaid">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-sdk tools:overrideLibrary="com.bun.miitmdid" />
|
||||
|
||||
@ -9,6 +9,7 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.pkg'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.pkg">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
</application>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.pkg">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- 允许应用程序访问网络连接 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@ -5,6 +5,8 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.qqgame'
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.qqgame">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application>
|
||||
|
||||
@ -3,6 +3,7 @@ apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.sentry'
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.sentry">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
|
||||
</manifest>
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.wechat.pay">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
#Wed Jul 19 10:16:09 CST 2017
|
||||
org.gradle.jvmargs=-Xmx4096m -XX\:MaxPermSize\=2048m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
|
||||
org.gradle.jvmargs=-Xmx4096m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
|
||||
#开启gradle并行编译
|
||||
org.gradle.parallel=true
|
||||
#开启守护进程
|
||||
@ -96,4 +96,8 @@ android.injected.testOnly = false
|
||||
|
||||
# 动态配置插件
|
||||
isRelease = true
|
||||
pluginBasePath=vasdk/
|
||||
pluginBasePath=vasdk/
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.enableBuildConfigAsBytecode=true
|
||||
android.nonFinalResIds=false
|
||||
android.defaults.buildfeatures.renderscript=true
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Mon Oct 30 17:29:06 CST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.2-bin.zip
|
||||
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
65
init.gradle
65
init.gradle
@ -1,7 +1,6 @@
|
||||
allprojects { project ->
|
||||
buildscript {
|
||||
ext.booster_version = '4.9.0'
|
||||
ext.plugin_version = "0.3.0"
|
||||
ext.booster_version = '5.0.0'
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@ -9,16 +8,10 @@ allprojects { project ->
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/public' }
|
||||
maven { url "https://artifact.bytedance.com/repository/byteX/" }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// byteX
|
||||
classpath "com.bytedance.android.byteX:base-plugin:${plugin_version}"
|
||||
classpath "com.bytedance.android.byteX:const-inline-plugin:${plugin_version}"
|
||||
classpath "com.bytedance.android.byteX:method-call-opt-plugin:${plugin_version}"
|
||||
classpath "com.bytedance.android.byteX:field-assign-opt-plugin:${plugin_version}"
|
||||
|
||||
// booster
|
||||
classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version"
|
||||
@ -45,62 +38,6 @@ allprojects { project ->
|
||||
project.apply plugin: 'com.didiglobal.booster'
|
||||
|
||||
project.apply plugin: "com.gh.gamecenter.plugin"
|
||||
|
||||
project.apply plugin: 'bytex'
|
||||
project.apply plugin: 'bytex.method_call_opt' // 移除 log https://github.com/bytedance/ByteX/blob/master/method-call-opt-plugin/README-zh.md
|
||||
project.apply plugin: 'bytex.field_assign_opt' //去除重复的赋值 https://github.com/bytedance/ByteX/blob/master/field-assign-opt-plugin/README-zh.md
|
||||
//
|
||||
project.method_call_opt {
|
||||
enable true
|
||||
enableInDebug false
|
||||
logLevel "DEBUG"
|
||||
//是否在log中显示删除方法调用指令后的方法指令,一般调试时使用
|
||||
showAfterOptInsLog false
|
||||
//需要删除的方法配置
|
||||
methodList = [
|
||||
//下面的每一项配置必须严格按照数据配置,一个地方不对这一项不生效。
|
||||
//class#method#desc
|
||||
"android/util/Log#v#(Ljava/lang/String;Ljava/lang/String;)I",
|
||||
"android/util/Log#v#(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I",
|
||||
"android/util/Log#d#(Ljava/lang/String;Ljava/lang/String;)I",
|
||||
"android/util/Log#d#(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I",
|
||||
"android/util/Log#i#(Ljava/lang/String;Ljava/lang/String;)I",
|
||||
"android/util/Log#i#(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I",
|
||||
"android/util/Log#w#(Ljava/lang/String;Ljava/lang/String;)I",
|
||||
"android/util/Log#w#(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I",
|
||||
"android/util/Log#e#(Ljava/lang/String;Ljava/lang/String;)I",
|
||||
"android/util/Log#e#(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I",
|
||||
"android/util/Log#println#(ILjava/lang/String;Ljava/lang/String;)I",
|
||||
|
||||
"java/lang/Throwable#printStackTrace#()V",
|
||||
"com/google/devtools/build/android/desugar/runtime/ThrowableExtension#printStackTrace#(Ljava/lang/Throwable;)V",
|
||||
|
||||
//项目中的方法
|
||||
"com/lightgame/utils/Utils#log#(Ljava/lang/String;)V",
|
||||
"com/lightgame/utils/Utils#log#(Ljava/lang/int;Ljava/lang/String;Ljava/lang/String;)V",
|
||||
"com/lightgame/utils/Utils#log#(Ljava/lang/String;Ljava/lang/String;)V",
|
||||
"com/lightgame/utils/Utils#log#(Ljava/lang/String;Ljava/lang/Object;)V",
|
||||
"com/lightgame/utils/Utils#log#(Ljava/lang/Object;)V",
|
||||
"com/gh/gamecenter/common/util/MtaHelper#onEvent#(Ljava/lang/Object;Ljava/lang/String;)V",
|
||||
"com/gh/gamecenter/common/util/MtaHelper#onEventWithTime#(Ljava/lang/String;I[Ljava/lang/String;)V",
|
||||
"com/gh/gamecenter/common/util/MtaHelper#onEventWithBasicDeviceInfo#(Ljava/lang/String;[Ljava/lang/String;)V"
|
||||
]
|
||||
onlyCheckList = []
|
||||
whiteList = [
|
||||
"com/tencent/qqmini/minigame/opensdk/share/OpenSdkShareHelper*",
|
||||
]
|
||||
}
|
||||
|
||||
project.field_assign_opt {
|
||||
enable false
|
||||
enableInDebug false
|
||||
logLevel "INFO"
|
||||
removeLineNumber true // 同时移除赋值对应的行号信息(如果有的话),默认true。
|
||||
whiteList = [
|
||||
//白名单,ClassName.FieldName 。不支持模式匹配
|
||||
//"android.support.constraint.solver.ArrayRow.isSimpleDefinition"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
allprojects { project ->
|
||||
buildscript {
|
||||
ext.booster_version = '4.9.0'
|
||||
ext.plugin_version = "0.3.0"
|
||||
ext.booster_version = '5.0.0'
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@ -14,12 +13,6 @@ allprojects { project ->
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// byteX
|
||||
classpath "com.bytedance.android.byteX:base-plugin:${plugin_version}"
|
||||
classpath "com.bytedance.android.byteX:const-inline-plugin:${plugin_version}"
|
||||
classpath "com.bytedance.android.byteX:method-call-opt-plugin:${plugin_version}"
|
||||
classpath "com.bytedance.android.byteX:field-assign-opt-plugin:${plugin_version}"
|
||||
|
||||
// booster
|
||||
classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version"
|
||||
// classpath "com.didiglobal.booster:booster-transform-shared-preferences:$booster_version"
|
||||
@ -35,7 +28,6 @@ allprojects { project ->
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/public' }
|
||||
maven { url "https://artifact.bytedance.com/repository/byteX/" }
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
}
|
||||
|
||||
@ -45,20 +37,6 @@ allprojects { project ->
|
||||
project.apply plugin: 'com.didiglobal.booster'
|
||||
|
||||
project.apply plugin: "com.gh.gamecenter.plugin"
|
||||
|
||||
project.apply plugin: 'bytex'
|
||||
project.apply plugin: 'bytex.field_assign_opt' //去除重复的赋值 https://github.com/bytedance/ByteX/blob/master/field-assign-opt-plugin/README-zh.md
|
||||
|
||||
project.field_assign_opt {
|
||||
enable false
|
||||
enableInDebug false
|
||||
logLevel "INFO"
|
||||
removeLineNumber true // 同时移除赋值对应的行号信息(如果有的话),默认true。
|
||||
whiteList = [
|
||||
//白名单,ClassName.FieldName 。不支持模式匹配
|
||||
//"android.support.constraint.solver.ArrayRow.isSimpleDefinition"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Submodule libraries/LGLibrary updated: 8375fcb09d...480388c379
@ -20,6 +20,8 @@ android {
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
namespace "com.zhihu.matisse"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@ -13,8 +13,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
|
||||
package = "com.zhihu.matisse" >
|
||||
<manifest xmlns:android = "http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
@ -9,4 +9,6 @@ android {
|
||||
defaultConfig {
|
||||
consumerProguardFiles 'proguard-library.txt'
|
||||
}
|
||||
|
||||
namespace "com.gh.qqshare"
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
|
||||
package = "com.gh.qqshare" >
|
||||
<manifest xmlns:android = "http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name = "android.permission.INTERNET" />
|
||||
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
@ -7,6 +7,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.common'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.common">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 允许应用程序获取网络信息状态 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
||||
@ -6,6 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.core'
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.core">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
|
||||
</manifest>
|
||||
@ -7,6 +7,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.feature'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.feature">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
@ -4,6 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.vspace.installexternalgames'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.vspace.installexternalgames">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
|
||||
@ -7,6 +7,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.login'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.login">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<queries>
|
||||
<package android:name="com.tencent.mm" />
|
||||
|
||||
@ -9,6 +9,7 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.message'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.message">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.message">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- 允许应用程序访问网络连接 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@ -2,11 +2,11 @@ plugins {
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'kotlin-kapt'
|
||||
id 'com.sensorsdata.analytics.android'
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.sensorsdata'
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
@ -87,14 +87,6 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
sensorsAnalytics {
|
||||
sdk {
|
||||
disableIMEI = true
|
||||
disableCarrier = true
|
||||
disableMacAddress = true
|
||||
}
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.sensorsdata">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
|
||||
@ -9,6 +9,7 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.setting'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.setting">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.setting">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- 允许应用程序访问网络连接 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@ -5,6 +5,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.gh.gamecenter.va.api'
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.va.api">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
@ -22,6 +22,8 @@ android {
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
namespace 'com.gh.gamecenter.va.impl'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.gh.gamecenter.va.impl">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.lg.vspace" />
|
||||
|
||||
|
||||
Submodule ndownload updated: edeefdffa6...ba58a25b8b
@ -81,6 +81,7 @@ def optionalVaModules = [
|
||||
'va-feature-realname': 'vasdk/feature/realname-window',
|
||||
'va-library-commons' : 'vasdk/commons',
|
||||
'va-library-network' : 'vasdk/library/network',
|
||||
'va-feature-translate' : 'vasdk/feature/translate',
|
||||
'va-archive' : 'vasdk/archive',
|
||||
'va-plugin-constant' : 'vasdk/plugin/constant',
|
||||
'va-plugin-host' : 'vasdk/host',
|
||||
|
||||
2
vasdk
2
vasdk
Submodule vasdk updated: 932474708c...51793ea93c
Submodule vspace-bridge updated: 11bbe92dfe...bad072f7f6
Reference in New Issue
Block a user