parent
b8f9e12b07
commit
c0ab2323b8
@ -1,6 +1,6 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||||
<project version="4"> |
<project version="4"> |
||||||
<component name="CompilerConfiguration"> |
<component name="CompilerConfiguration"> |
||||||
<bytecodeTargetLevel target="1.8" /> |
<bytecodeTargetLevel target="11" /> |
||||||
</component> |
</component> |
||||||
</project> |
</project> |
Binary file not shown.
@ -0,0 +1,13 @@ |
|||||||
|
package xyz.fycz.myreader.entity |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2022/3/29 14:55 |
||||||
|
*/ |
||||||
|
data class PluginConfig( |
||||||
|
val name: String, |
||||||
|
val versionCode: Int, |
||||||
|
val version: String, |
||||||
|
val url: String, |
||||||
|
val changelog: String |
||||||
|
) |
@ -0,0 +1,92 @@ |
|||||||
|
package xyz.fycz.myreader.util.utils |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.util.Log |
||||||
|
import dalvik.system.DexClassLoader |
||||||
|
import xyz.fycz.dynamic.AppParam |
||||||
|
import xyz.fycz.dynamic.IAppLoader |
||||||
|
import xyz.fycz.myreader.application.App |
||||||
|
import xyz.fycz.myreader.common.APPCONST |
||||||
|
import xyz.fycz.myreader.common.URLCONST.DEFAULT_PLUGIN_CONFIG_URL |
||||||
|
import xyz.fycz.myreader.entity.PluginConfig |
||||||
|
import xyz.fycz.myreader.model.third3.Coroutine |
||||||
|
import xyz.fycz.myreader.model.third3.http.getProxyClient |
||||||
|
import xyz.fycz.myreader.model.third3.http.newCallResponse |
||||||
|
import xyz.fycz.myreader.model.third3.http.newCallResponseBody |
||||||
|
import xyz.fycz.myreader.model.third3.http.text |
||||||
|
import xyz.fycz.myreader.util.SharedPreUtils |
||||||
|
import xyz.fycz.myreader.util.ToastUtils |
||||||
|
import java.io.File |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2022/3/29 12:36 |
||||||
|
*/ |
||||||
|
object PluginUtils { |
||||||
|
|
||||||
|
val TAG = PluginUtils.javaClass.simpleName |
||||||
|
|
||||||
|
fun init() { |
||||||
|
val pluginConfigUrl = |
||||||
|
SharedPreUtils.getInstance().getString("pluginConfigUrl", DEFAULT_PLUGIN_CONFIG_URL) |
||||||
|
var config: PluginConfig? = null |
||||||
|
Coroutine.async { |
||||||
|
val configJson = getProxyClient().newCallResponseBody { |
||||||
|
url(pluginConfigUrl) |
||||||
|
}.text() |
||||||
|
config = GSON.fromJsonObject<PluginConfig>(configJson) |
||||||
|
val oldConfig = GSON.fromJsonObject<PluginConfig>( |
||||||
|
SharedPreUtils.getInstance().getString("pluginConfig") |
||||||
|
) ?: PluginConfig("dynamic.dex", 100, "", "", "") |
||||||
|
if (config != null) { |
||||||
|
if (config!!.versionCode > oldConfig.versionCode) { |
||||||
|
downloadPlugin(config!!) |
||||||
|
SharedPreUtils.getInstance().putString("pluginConfig", configJson) |
||||||
|
} |
||||||
|
} else { |
||||||
|
config = oldConfig |
||||||
|
} |
||||||
|
}.onSuccess { |
||||||
|
loadAppLoader(App.getmContext(), config) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private suspend fun downloadPlugin(config: PluginConfig) { |
||||||
|
val res = getProxyClient().newCallResponseBody { |
||||||
|
url(config.url) |
||||||
|
} |
||||||
|
FileUtils.getFile(APPCONST.PLUGIN_DIR_PATH + config.name) |
||||||
|
.writeBytes(res.byteStream().readBytes()) |
||||||
|
} |
||||||
|
|
||||||
|
private fun loadAppLoader(context: Context, config: PluginConfig?) { |
||||||
|
config?.let { |
||||||
|
val pluginPath = APPCONST.PLUGIN_DIR_PATH + it.name |
||||||
|
val desFile = File(pluginPath) |
||||||
|
if (desFile.exists()) { |
||||||
|
val dexClassLoader = DexClassLoader( |
||||||
|
pluginPath, |
||||||
|
FileUtils.getCachePath(), |
||||||
|
null, |
||||||
|
context.classLoader |
||||||
|
) |
||||||
|
try { |
||||||
|
val libClazz = dexClassLoader.loadClass("xyz.fycz.dynamic.AppLoadImpl") |
||||||
|
val appLoader = libClazz.newInstance() as IAppLoader? |
||||||
|
appLoader?.run { |
||||||
|
val appParam = AppParam() |
||||||
|
appParam.classLoader = context.classLoader |
||||||
|
appParam.packageName = context.packageName |
||||||
|
appParam.appInfo = context.applicationInfo |
||||||
|
onLoad(appParam) |
||||||
|
} |
||||||
|
} catch (e: Exception) { |
||||||
|
e.printStackTrace() |
||||||
|
} |
||||||
|
} else { |
||||||
|
Log.d(TAG, pluginPath + "文件不存在") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
/build |
@ -0,0 +1,38 @@ |
|||||||
|
plugins { |
||||||
|
id 'com.android.library' |
||||||
|
id 'org.jetbrains.kotlin.android' |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion 29 |
||||||
|
|
||||||
|
defaultConfig { |
||||||
|
minSdkVersion 21 |
||||||
|
targetSdkVersion 29 |
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
||||||
|
consumerProguardFiles "consumer-rules.pro" |
||||||
|
} |
||||||
|
|
||||||
|
buildTypes { |
||||||
|
release { |
||||||
|
minifyEnabled false |
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||||
|
} |
||||||
|
} |
||||||
|
compileOptions { |
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8 |
||||||
|
targetCompatibility JavaVersion.VERSION_1_8 |
||||||
|
} |
||||||
|
kotlinOptions { |
||||||
|
jvmTarget = "1.8" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation 'androidx.core:core-ktx:1.6.0' |
||||||
|
testImplementation 'junit:junit:4.13.2' |
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3' |
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' |
||||||
|
compileOnly("me.fycz.maple:maple:1.6") |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
# Add project specific ProGuard rules here. |
||||||
|
# You can control the set of applied configuration files using the |
||||||
|
# proguardFiles setting in build.gradle. |
||||||
|
# |
||||||
|
# For more details, see |
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following |
||||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||||
|
# class: |
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||||
|
# public *; |
||||||
|
#} |
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for |
||||||
|
# debugging stack traces. |
||||||
|
#-keepattributes SourceFile,LineNumberTable |
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to |
||||||
|
# hide the original source file name. |
||||||
|
#-renamesourcefileattribute SourceFile |
@ -0,0 +1,24 @@ |
|||||||
|
package xyz.fycz.dynamic |
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry |
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4 |
||||||
|
|
||||||
|
import org.junit.Test |
||||||
|
import org.junit.runner.RunWith |
||||||
|
|
||||||
|
import org.junit.Assert.* |
||||||
|
|
||||||
|
/** |
||||||
|
* Instrumented test, which will execute on an Android device. |
||||||
|
* |
||||||
|
* See [testing documentation](http://d.android.com/tools/testing). |
||||||
|
*/ |
||||||
|
@RunWith(AndroidJUnit4::class) |
||||||
|
class ExampleInstrumentedTest { |
||||||
|
@Test |
||||||
|
fun useAppContext() { |
||||||
|
// Context of the app under test. |
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext |
||||||
|
assertEquals("xyz.fycz.dynamic.test", appContext.packageName) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="xyz.fycz.dynamic"> |
||||||
|
|
||||||
|
</manifest> |
@ -0,0 +1,38 @@ |
|||||||
|
package xyz.fycz.dynamic |
||||||
|
|
||||||
|
import me.fycz.maple.MapleBridge |
||||||
|
import me.fycz.maple.MapleUtils |
||||||
|
import me.fycz.maple.MethodHook |
||||||
|
import me.fycz.maple.MethodReplacement |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2022/3/29 11:59 |
||||||
|
*/ |
||||||
|
class AppLoadImpl : IAppLoader { |
||||||
|
override fun onLoad(appParam: AppParam) { |
||||||
|
try { |
||||||
|
MapleUtils.findAndHookMethod( |
||||||
|
"xyz.fycz.myreader.util.utils.AdUtils", |
||||||
|
appParam.classLoader, |
||||||
|
"checkHasAd", |
||||||
|
Boolean::class.java, |
||||||
|
Boolean::class.java, |
||||||
|
object : MethodReplacement() { |
||||||
|
override fun replaceHookedMethod(param: MapleBridge.MethodHookParam): Any? { |
||||||
|
val just = MapleUtils.findMethodExact( |
||||||
|
"io.reactivex.Single", |
||||||
|
appParam.classLoader, |
||||||
|
"just", |
||||||
|
Any::class.java |
||||||
|
) |
||||||
|
return just.invoke(null, false) |
||||||
|
} |
||||||
|
} |
||||||
|
) |
||||||
|
} catch (e: Exception) { |
||||||
|
e.printStackTrace() |
||||||
|
MapleUtils.log(e) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package xyz.fycz.dynamic; |
||||||
|
|
||||||
|
import android.content.pm.ApplicationInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2022/3/29 11:31 |
||||||
|
*/ |
||||||
|
public class AppParam { |
||||||
|
/** The name of the package being loaded. */ |
||||||
|
public String packageName; |
||||||
|
|
||||||
|
/** The ClassLoader used for this package. */ |
||||||
|
public ClassLoader classLoader; |
||||||
|
|
||||||
|
/** More information about the application being loaded. */ |
||||||
|
public ApplicationInfo appInfo; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package xyz.fycz.dynamic; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2022/3/29 11:27 |
||||||
|
*/ |
||||||
|
public interface IAppLoader { |
||||||
|
void onLoad(AppParam appParam); |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package xyz.fycz.dynamic |
||||||
|
|
||||||
|
import org.junit.Test |
||||||
|
|
||||||
|
import org.junit.Assert.* |
||||||
|
|
||||||
|
/** |
||||||
|
* Example local unit test, which will execute on the development machine (host). |
||||||
|
* |
||||||
|
* See [testing documentation](http://d.android.com/tools/testing). |
||||||
|
*/ |
||||||
|
class ExampleUnitTest { |
||||||
|
@Test |
||||||
|
fun addition_isCorrect() { |
||||||
|
assertEquals(4, 2 + 2) |
||||||
|
} |
||||||
|
} |
@ -1,2 +1,3 @@ |
|||||||
include ':app' |
include ':app' |
||||||
include ':DialogX' |
include ':DialogX' |
||||||
|
include ':dynamic' |
||||||
|
Loading…
Reference in new issue