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