fix: prevent module load failures from hiding fonts and themes pages
This commit is contained in:
parent
7d15a63b4c
commit
0b39a29e8e
4 changed files with 78 additions and 37 deletions
|
|
@ -58,7 +58,7 @@ class Main : Module(), IXposedHookLoadPackage, IXposedHookZygoteInit {
|
|||
}
|
||||
|
||||
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
|
||||
for (module in modules) module.onInit(startupParam)
|
||||
dispatch("onInit") { module -> module.onInit(startupParam) }
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(param: XC_LoadPackage.LoadPackageParam) = with(param) {
|
||||
|
|
@ -96,14 +96,25 @@ class Main : Module(), IXposedHookLoadPackage, IXposedHookZygoteInit {
|
|||
}
|
||||
|
||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {
|
||||
for (module in modules) module.onLoad(packageParam)
|
||||
dispatch("onLoad") { module -> module.onLoad(packageParam) }
|
||||
}
|
||||
|
||||
override fun onContext(context: Context) {
|
||||
for (module in modules) module.onContext(context)
|
||||
dispatch("onContext") { module -> module.onContext(context) }
|
||||
}
|
||||
|
||||
override fun onActivity(activity: Activity) {
|
||||
for (module in modules) module.onActivity(activity)
|
||||
dispatch("onActivity") { module -> module.onActivity(activity) }
|
||||
}
|
||||
|
||||
private fun dispatch(stage: String, block: (Module) -> Unit) {
|
||||
for (module in modules) {
|
||||
val name = module.javaClass.simpleName.ifEmpty { module.javaClass.name }
|
||||
try {
|
||||
block(module)
|
||||
} catch (e: Throwable) {
|
||||
Log.e("Module $name failed during $stage", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import ShiggyXposed.xposed.BuildConfig
|
|||
import ShiggyXposed.xposed.Constants
|
||||
import ShiggyXposed.xposed.Module
|
||||
import ShiggyXposed.xposed.Utils.Companion.JSON
|
||||
import ShiggyXposed.xposed.Utils.Log
|
||||
import ShiggyXposed.xposed.modules.HookScriptLoaderModule.PRELOADS_DIR
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
|
|
@ -23,7 +24,11 @@ class PayloadGlobalModule(private val modules: List<Module>) : Module() {
|
|||
buildJsonObject {
|
||||
put("loaderName", Constants.LOADER_NAME)
|
||||
put("loaderVersion", BuildConfig.VERSION_NAME)
|
||||
@Suppress("DEPRECATION") for (module in modules) module.buildPayload(this)
|
||||
@Suppress("DEPRECATION") for (module in modules) runCatching {
|
||||
module.buildPayload(this)
|
||||
}.onFailure {
|
||||
Log.e("Module ${module.javaClass.simpleName} failed while building payload", it)
|
||||
}
|
||||
})
|
||||
|
||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ object FontsModule : Module() {
|
|||
private val FILE_EXTENSIONS = arrayOf(".ttf", ".otf")
|
||||
private const val FONTS_ASSET_PATH = "fonts/"
|
||||
|
||||
private val FONT_MANAGER_CLASSES = arrayOf(
|
||||
"com.facebook.react.views.text.ReactFontManager\$Companion",
|
||||
"com.facebook.react.common.assets.ReactFontManager\$Companion",
|
||||
)
|
||||
|
||||
private lateinit var fontsDir: File
|
||||
private lateinit var fontsDownloadsDir: File
|
||||
private var fontsAbsPath: String? = null
|
||||
|
|
@ -51,21 +56,33 @@ object FontsModule : Module() {
|
|||
}
|
||||
|
||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.facebook.react.common.assets.ReactFontManager\$Companion",
|
||||
classLoader,
|
||||
"createAssetTypeface",
|
||||
String::class.java,
|
||||
Int::class.java,
|
||||
"android.content.res.AssetManager",
|
||||
object : XC_MethodReplacement() {
|
||||
val replacement = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Typeface? {
|
||||
val fontFamilyName: String = param.args[0].toString()
|
||||
val style: Int = param.args[1] as Int
|
||||
val assetManager: AssetManager = param.args[2] as AssetManager
|
||||
return createAssetTypeface(fontFamilyName, style, assetManager)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// React Native moved ReactFontManager between releases, so try every known package.
|
||||
// Never let this throw: an uncaught error here aborts the whole module chain.
|
||||
val hooked = FONT_MANAGER_CLASSES.any { className ->
|
||||
runCatching {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
className,
|
||||
classLoader,
|
||||
"createAssetTypeface",
|
||||
String::class.java,
|
||||
Int::class.java,
|
||||
"android.content.res.AssetManager",
|
||||
replacement
|
||||
)
|
||||
}.onFailure { Log.e("Failed to hook createAssetTypeface on $className", it) }
|
||||
.isSuccess
|
||||
}
|
||||
|
||||
if (!hooked) Log.e("Failed to hook createAssetTypeface on all known ReactFontManager classes")
|
||||
|
||||
val fontDefFile = File(appInfo.dataDir, "${Constants.FILES_DIR}/fonts.json").apply { asFile() }
|
||||
if (!fontDefFile.exists()) return@with
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage
|
|||
import ShiggyXposed.xposed.Constants
|
||||
import ShiggyXposed.xposed.Module
|
||||
import ShiggyXposed.xposed.Utils.Companion.JSON
|
||||
import ShiggyXposed.xposed.Utils.Log
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
|
|
@ -81,12 +82,17 @@ object ThemesModule : Module() {
|
|||
}
|
||||
|
||||
fun hookTheme() {
|
||||
val themeManager = param.classLoader.loadClass("com.discord.theme.utils.ColorUtilsKt")
|
||||
val darkTheme = param.classLoader.loadClass("com.discord.theme.DarkerTheme")
|
||||
val lightTheme = param.classLoader.loadClass("com.discord.theme.LightTheme")
|
||||
|
||||
val theme = this.theme ?: return
|
||||
|
||||
val themeManager = param.classLoader.safeLoadClass("com.discord.theme.utils.ColorUtilsKt")
|
||||
val darkTheme = param.classLoader.safeLoadClass("com.discord.theme.DarkerTheme")
|
||||
val lightTheme = param.classLoader.safeLoadClass("com.discord.theme.LightTheme")
|
||||
|
||||
if (themeManager == null || darkTheme == null || lightTheme == null) {
|
||||
Log.e("Failed to load Discord theme classes, skipping theme hooks")
|
||||
return
|
||||
}
|
||||
|
||||
// Apply rawColors
|
||||
theme.data.rawColors?.forEach { (key, value) ->
|
||||
rawColorMap[key.lowercase()] = hexStringToColorInt(value)
|
||||
|
|
@ -106,17 +112,6 @@ object ThemesModule : Module() {
|
|||
|
||||
// If there's any rawColors value, hook the color getter
|
||||
if (!theme.data.rawColors.isNullOrEmpty()) {
|
||||
val getColorCompat = themeManager.getDeclaredMethod(
|
||||
"getColorCompat",
|
||||
Resources::class.java,
|
||||
Int::class.javaPrimitiveType,
|
||||
Resources.Theme::class.java,
|
||||
)
|
||||
|
||||
val getColorCompatLegacy = themeManager.getDeclaredMethod(
|
||||
"getColorCompat", Context::class.java, Int::class.javaPrimitiveType
|
||||
)
|
||||
|
||||
val patch = MethodHookBuilder().run {
|
||||
before {
|
||||
val arg1 = args[0]
|
||||
|
|
@ -129,8 +124,21 @@ object ThemesModule : Module() {
|
|||
build()
|
||||
}
|
||||
|
||||
getColorCompat.hook(patch)
|
||||
getColorCompatLegacy.hook(patch)
|
||||
listOf(
|
||||
runCatching {
|
||||
themeManager.getDeclaredMethod(
|
||||
"getColorCompat",
|
||||
Resources::class.java,
|
||||
Int::class.javaPrimitiveType,
|
||||
Resources.Theme::class.java,
|
||||
)
|
||||
}.getOrNull(),
|
||||
runCatching {
|
||||
themeManager.getDeclaredMethod(
|
||||
"getColorCompat", Context::class.java, Int::class.javaPrimitiveType
|
||||
)
|
||||
}.getOrNull()
|
||||
).filterNotNull().forEach { it.hook(patch) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue