diff --git a/app/src/main/kotlin/io/github/revenge/xposed/Main.kt b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt index 9363f85..47e3108 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/Main.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt @@ -42,9 +42,13 @@ data class LoaderConfig( class Main : Module(), IXposedHookLoadPackage { private lateinit var preloadsDir: File private lateinit var bundle: File - private lateinit var onActivityCreate: ((Activity) -> Unit) -> Unit private lateinit var httpJob: Deferred + private lateinit var activity: Activity + private val onActivityCreate: ((Activity) -> Unit) -> Unit = + { if (::activity.isInitialized) it(activity) else onActivityCreateCallbacks.add(it) } + private val onActivityCreateCallbacks = mutableSetOf<(Activity) -> Unit>() + val PRELOADS_DIR = "preloads" val ETAG_FILE = "etag.txt" @@ -77,35 +81,33 @@ class Main : Module(), IXposedHookLoadPackage { val reactActivity = classLoader.safeLoadClass(TARGET_ACTIVITY) ?: return - var activity: Activity? = null - val onActivityCreateCallbacks = mutableSetOf<(Activity) -> Unit>() - reactActivity.hookMethod("onCreate", Bundle::class.java) { before { activity = thisObject as Activity - for (cb in onActivityCreateCallbacks) cb(activity) - onActivityCreateCallbacks.clear() + this@Main.onCreate(activity) } } - onActivityCreate = { callback -> - activity?.let(callback) ?: onActivityCreateCallbacks.add(callback) - } - init(param) } + override fun onCreate(activity: Activity) { + for (module in modules) module.onCreate(activity) + for (cb in onActivityCreateCallbacks) cb(activity) + onActivityCreateCallbacks.clear() + } + private fun init( param: XC_LoadPackage.LoadPackageParam, ) = with(param) { - val cacheDir = File(appInfo.dataDir, Constants.CACHE_DIR).apply { mkdirs() } - val filesDir = File(appInfo.dataDir, Constants.FILES_DIR).apply { mkdirs() } + val cacheDir = File(appInfo.dataDir, Constants.CACHE_DIR).apply { asDir() } + val filesDir = File(appInfo.dataDir, Constants.FILES_DIR).apply { asDir() } - preloadsDir = File(filesDir, PRELOADS_DIR).apply { mkdirs() } - bundle = File(cacheDir, Constants.BUNDLE_FILE) + preloadsDir = File(filesDir, PRELOADS_DIR).apply { asDir() } + bundle = File(cacheDir, Constants.BUNDLE_FILE).apply { asFile() } - val etag = File(cacheDir, ETAG_FILE) - val configFile = File(filesDir, CONFIG_FILE) + val etag = File(cacheDir, ETAG_FILE).apply { asFile() } + val configFile = File(filesDir, CONFIG_FILE).apply { asFile() } val config = runCatching { JSON.decodeFromString(configFile.readText()) @@ -163,7 +165,7 @@ class Main : Module(), IXposedHookLoadPackage { ).mapNotNull { classLoader.safeLoadClass(it) } .forEach { hookLoadScript(it) } - for (module in modules) module.onInit(param) + for (module in modules) module.onLoad(param) // Fix resource package name mismatch if (packageName != TARGET_PACKAGE) { diff --git a/app/src/main/kotlin/io/github/revenge/xposed/Module.kt b/app/src/main/kotlin/io/github/revenge/xposed/Module.kt index 2103d25..c951f8b 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/Module.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/Module.kt @@ -1,15 +1,28 @@ package io.github.revenge.xposed +import android.app.Activity import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_LoadPackage import java.lang.reflect.Method import kotlinx.serialization.json.JsonObjectBuilder +import java.io.File abstract class Module { open fun buildPayload(builder: JsonObjectBuilder) {} - open fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) {} + open fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {} + + open fun onCreate(activity: Activity) {} + + protected fun File.asDir() { + if (!this.isDirectory()) this.delete() + this.mkdirs() + } + + protected fun File.asFile() { + if (!this.isFile()) this.deleteRecursively() + } protected fun Class<*>.method( name: String, diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/FontsModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/FontsModule.kt index 686ec31..76b42cf 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/FontsModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/FontsModule.kt @@ -50,7 +50,7 @@ class FontsModule: Module() { } } - override fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) = with (packageParam) { + override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with (packageParam) { XposedHelpers.findAndHookMethod( "com.facebook.react.views.text.ReactFontManager\$Companion", classLoader, @@ -67,15 +67,15 @@ class FontsModule: Module() { } }) - val fontDefFile = File(appInfo.dataDir, "${Constants.FILES_DIR}/fonts.json") + val fontDefFile = File(appInfo.dataDir, "${Constants.FILES_DIR}/fonts.json").apply { asFile() } if (!fontDefFile.exists()) return@with val fontDef = try { JSON.decodeFromString(fontDefFile.readText()) } catch (_: Throwable) { return@with } - fontsDownloadsDir = File(appInfo.dataDir, "${Constants.FILES_DIR}/downloads/fonts").apply { mkdirs() } - fontsDir = File(fontsDownloadsDir, fontDef.name!!).apply { mkdirs() } + fontsDownloadsDir = File(appInfo.dataDir, "${Constants.FILES_DIR}/downloads/fonts").apply { asDir() } + fontsDir = File(fontsDownloadsDir, fontDef.name!!).apply { asDir() } fontsAbsPath = fontsDir.absolutePath + "/" fontsDir.listFiles()?.forEach { file -> @@ -96,7 +96,7 @@ class FontsModule: Module() { val url = fontDef.main.getValue(name) try { Log.i("Downloading $name from $url") - val file = File(fontsDir, "$name${FILE_EXTENSIONS.first { url.endsWith(it) }}") + val file = File(fontsDir, "$name${FILE_EXTENSIONS.first { url.endsWith(it) }}").apply { asFile() } if (file.exists()) return@async val client = HttpClient(CIO) { @@ -132,7 +132,7 @@ class FontsModule: Module() { try { for (fileExtension in FILE_EXTENSIONS) { val (customName, refName) = fontFamilyName.split(":") - val file = File(fontsDownloadsDir, "$customName/$refName.$fileExtension") + val file = File(fontsDownloadsDir, "$customName/$refName.$fileExtension").apply { asFile() } val font = Font.Builder(file).build() val family = FontFamily.Builder(font).build() fontFamilies.add(family) @@ -204,7 +204,7 @@ class FontsModule: Module() { try { for (fileExtension in FILE_EXTENSIONS) { val (customName, refName) = fontFamilyName.split(":") - val file = File(fontsDownloadsDir, "$customName/$refName.$fileExtension") + val file = File(fontsDownloadsDir, "$customName/$refName.$fileExtension").apply { asFile() } if (!file.exists()) throw Exception() return Typeface.createFromFile(file.absolutePath) } diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/LogBoxModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/LogBoxModule.kt index e58e90a..4c7f553 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/LogBoxModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/LogBoxModule.kt @@ -17,7 +17,7 @@ import kotlin.system.exitProcess class LogBoxModule: Module() { lateinit var packageParam: XC_LoadPackage.LoadPackageParam - override fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) = with (packageParam) { + override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with (packageParam) { this@LogBoxModule.packageParam = packageParam val dcdReactNativeHostClass = classLoader.loadClass("com.discord.bridge.DCDReactNativeHost") diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/NativeBridgeModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/NativeBridgeModule.kt index 0568f08..6e4b186 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/NativeBridgeModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/NativeBridgeModule.kt @@ -15,9 +15,6 @@ typealias BridgeMethodCallback = (args: BridgeMethodArgs) -> Any? typealias BridgeMethodArgs = ArrayList class NativeBridgeModule : Module() { - - private lateinit var packageParam: XC_LoadPackage.LoadPackageParam - private lateinit var readableMapGetStringMethod: Method private lateinit var readableMapToHashMapMethod: Method private lateinit var argumentsMakeNativeObject: Method @@ -55,9 +52,7 @@ class NativeBridgeModule : Module() { else -> this }) - override fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) { - this@NativeBridgeModule.packageParam = packageParam - + override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) { val argumentsClass = classLoader.loadClass("com.facebook.react.bridge.Arguments") val readableMapClass = classLoader.loadClass("com.facebook.react.bridge.ReadableMap") val promiseClass = classLoader.loadClass("com.facebook.react.bridge.Promise") diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/ThemeModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/ThemeModule.kt index 505f9d9..4e64a5a 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/ThemeModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/ThemeModule.kt @@ -56,7 +56,7 @@ class ThemeModule : Module() { private fun String.fromScreamingSnakeToCamelCase() = this.split("_").joinToString("") { it -> it.lowercase().replaceFirstChar { it.uppercase() } } - override fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) { + override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) { param = packageParam theme = getTheme() hookTheme() @@ -70,7 +70,7 @@ class ThemeModule : Module() { } private fun getTheme(): Theme? { - val themeFile = File(param.appInfo.dataDir, "${Constants.FILES_DIR}/${THEME_FILE}") + val themeFile = File(param.appInfo.dataDir, "${Constants.FILES_DIR}/${THEME_FILE}").apply { asFile() } if (!themeFile.isValidish()) return null return try {