parent
b8f9e12b07
commit
c0ab2323b8
@ -1,6 +1,6 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="CompilerConfiguration"> |
||||
<bytecodeTargetLevel target="1.8" /> |
||||
<bytecodeTargetLevel target="11" /> |
||||
</component> |
||||
</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 ':DialogX' |
||||
include ':dynamic' |
||||
|
Loading…
Reference in new issue