feat(Module): allow modules to attach own Activity.onCreate callbacks
This commit is contained in:
parent
f8f77e88a1
commit
b12b6ab996
6 changed files with 44 additions and 34 deletions
|
|
@ -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<Unit>
|
||||
|
||||
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<LoaderConfig>(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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<FontDefinition>(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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@ typealias BridgeMethodCallback = (args: BridgeMethodArgs) -> Any?
|
|||
typealias BridgeMethodArgs = ArrayList<Any>
|
||||
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue