pull/21/head
fengyuecanzhu 3 years ago
parent b8f9e12b07
commit c0ab2323b8
  1. 2
      .idea/compiler.xml
  2. 2
      .idea/gradle.xml
  3. 2
      .idea/misc.xml
  4. 1
      .idea/modules.xml
  5. 3
      app/build.gradle
  6. BIN
      app/libs/dynamic-release.aar
  7. 1
      app/src/main/AndroidManifest.xml
  8. 4
      app/src/main/assets/updatelog.fy
  9. 7
      app/src/main/java/xyz/fycz/myreader/application/App.java
  10. 4
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  11. 2
      app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
  12. 13
      app/src/main/java/xyz/fycz/myreader/entity/PluginConfig.kt
  13. 92
      app/src/main/java/xyz/fycz/myreader/util/utils/PluginUtils.kt
  14. 2
      build.gradle
  15. 1
      dynamic/.gitignore
  16. 38
      dynamic/build.gradle
  17. 0
      dynamic/consumer-rules.pro
  18. 21
      dynamic/proguard-rules.pro
  19. 24
      dynamic/src/androidTest/java/xyz/fycz/dynamic/ExampleInstrumentedTest.kt
  20. 5
      dynamic/src/main/AndroidManifest.xml
  21. 38
      dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt
  22. 18
      dynamic/src/main/java/xyz/fycz/dynamic/AppParam.java
  23. 9
      dynamic/src/main/java/xyz/fycz/dynamic/IAppLoader.java
  24. 17
      dynamic/src/test/java/xyz/fycz/dynamic/ExampleUnitTest.kt
  25. 1
      settings.gradle

@ -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>

@ -7,11 +7,13 @@
<option name="testRunner" value="GRADLE" /> <option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="11 (2)" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/DialogX" /> <option value="$PROJECT_DIR$/DialogX" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/dynamic" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />

@ -18,7 +18,7 @@
</map> </map>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

@ -5,6 +5,7 @@
<module fileurl="file://$PROJECT_DIR$/.idea/modules/FYReader.iml" filepath="$PROJECT_DIR$/.idea/modules/FYReader.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/FYReader.iml" filepath="$PROJECT_DIR$/.idea/modules/FYReader.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/DialogX/FYReader.DialogX.iml" filepath="$PROJECT_DIR$/.idea/modules/DialogX/FYReader.DialogX.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/DialogX/FYReader.DialogX.iml" filepath="$PROJECT_DIR$/.idea/modules/DialogX/FYReader.DialogX.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/FYReader.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/FYReader.app.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/app/FYReader.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/FYReader.app.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.iml" filepath="$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.iml" />
</modules> </modules>
</component> </component>
</project> </project>

@ -274,6 +274,9 @@ dependencies {
exclude module: 'support-v4' exclude module: 'support-v4'
exclude group: 'com.android.support' exclude group: 'com.android.support'
} }
//maple
implementation("me.fycz.maple:maple:1.6")
} }
greendao { greendao {

Binary file not shown.

@ -43,6 +43,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-sdk tools:overrideLibrary="me.fycz.maple"/>
<application <application
android:name=".application.App" android:name=".application.App"
android:allowBackup="true" android:allowBackup="true"

@ -1,3 +1,7 @@
风月读书v2.4.3
更新内容:
1、修复存在同名章节时后面章节内容加载前面章节的问题
2022.03.25 2022.03.25
风月读书v2.4.2 风月读书v2.4.2
更新内容: 更新内容:

@ -43,6 +43,8 @@ import kotlin.Unit;
import kotlin.coroutines.Continuation; import kotlin.coroutines.Continuation;
import kotlin.jvm.functions.Function3; import kotlin.jvm.functions.Function3;
import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.CoroutineScope;
import xyz.fycz.dynamic.AppParam;
import xyz.fycz.dynamic.IAppLoader;
import xyz.fycz.myreader.R; import xyz.fycz.myreader.R;
import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST; import xyz.fycz.myreader.common.URLCONST;
@ -60,6 +62,7 @@ import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.utils.AdUtils; import xyz.fycz.myreader.util.utils.AdUtils;
import xyz.fycz.myreader.util.utils.NetworkUtils; import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.OkHttpUtils; import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.PluginUtils;
import xyz.fycz.myreader.webapi.LanZouApi; import xyz.fycz.myreader.webapi.LanZouApi;
@ -98,6 +101,7 @@ public class App extends Application {
// LLog.init(APPCONST.LOG_DIR); // LLog.init(APPCONST.LOG_DIR);
initDialogX(); initDialogX();
//AdUtils.initAd(); //AdUtils.initAd();
PluginUtils.INSTANCE.init();
} }
@ -301,7 +305,8 @@ public class App extends Application {
String domain = contents[9].substring(contents[9].indexOf(":") + 1); String domain = contents[9].substring(contents[9].indexOf(":") + 1);
SharedPreUtils.getInstance().putString("domain", domain); SharedPreUtils.getInstance().putString("domain", domain);
String pluginConfigUrl = contents[10].substring(contents[10].indexOf(":") + 1);
SharedPreUtils.getInstance().putString("pluginConfigUrl", pluginConfigUrl);
int versionCode = getVersionCode(); int versionCode = getVersionCode();
isForceUpdate = isForceUpdate && forceUpdateVersion > versionCode; isForceUpdate = isForceUpdate && forceUpdateVersion > versionCode;

@ -44,6 +44,8 @@ public class APPCONST {
+ "book_cache" + File.separator; + "book_cache" + File.separator;
public static String HTML_CACHE_PATH = FileUtils.getCachePath() + File.separator public static String HTML_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "html_cache" + File.separator; + "html_cache" + File.separator;
public static String PLUGIN_DIR_PATH = App.getmContext().getFilesDir().getParent()
+ File.separator + "plugin" + File.separator;
public static long exitTime; public static long exitTime;
public static final int exitConfirmTime = 2000; public static final int exitConfirmTime = 2000;
@ -143,7 +145,7 @@ public class APPCONST {
public static final String androidId = getAndroidId(); public static final String androidId = getAndroidId();
public static String getAndroidId(){ public static String getAndroidId() {
return Settings.System.getString(App.getmContext().getContentResolver(), Settings.Secure.ANDROID_ID); return Settings.System.getString(App.getmContext().getContentResolver(), Settings.Secure.ANDROID_ID);
} }
} }

@ -38,6 +38,8 @@ public class URLCONST {
public static final String QUOTATION = "https://v1.hitokoto.cn/?encode=json&charset=utf-8"; public static final String QUOTATION = "https://v1.hitokoto.cn/?encode=json&charset=utf-8";
public static final String DEFAULT_PLUGIN_CONFIG_URL = "https://fyreader.coding.net/p/img/d/Plugin/git/raw/master/release/config_FYReader.json";
public static String getDefaultDomain() { public static String getDefaultDomain() {
return SharedPreUtils.getInstance().getString("domain", "fycz.me"); return SharedPreUtils.getInstance().getString("domain", "fycz.me");
} }

@ -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 + "文件不存在")
}
}
}
}

@ -14,7 +14,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.3' classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

@ -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…
Cancel
Save