Compare commits
No commits in common. "master" and "V1.3.8" have entirely different histories.
22 changed files with 255 additions and 302 deletions
|
|
@ -68,6 +68,5 @@ dependencies {
|
||||||
implementation("io.ktor:ktor-client-core:3.3.1")
|
implementation("io.ktor:ktor-client-core:3.3.1")
|
||||||
implementation("io.ktor:ktor-client-cio:3.3.1")
|
implementation("io.ktor:ktor-client-cio:3.3.1")
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
implementation("dev.rikka.shizuku:api:13.1.5")
|
implementation("dev.rushii:libunbound:1.0.0")
|
||||||
implementation("dev.rikka.shizuku:provider:13.1.5")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import GoonXposed.xposed.modules.bridge.AdditionalBridgeMethodsModule
|
||||||
import GoonXposed.xposed.modules.bridge.BridgeModule
|
import GoonXposed.xposed.modules.bridge.BridgeModule
|
||||||
import GoonXposed.xposed.modules.no_track.BlockCrashReportingModule
|
import GoonXposed.xposed.modules.no_track.BlockCrashReportingModule
|
||||||
import GoonXposed.xposed.modules.no_track.BlockDeepLinksTrackingModule
|
import GoonXposed.xposed.modules.no_track.BlockDeepLinksTrackingModule
|
||||||
|
import GoonXposed.xposed.modules.LogBox.LogBoxModule
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
|
||||||
object HookStateHolder {
|
object HookStateHolder {
|
||||||
|
|
|
||||||
|
|
@ -2,160 +2,182 @@ package GoonXposed.xposed
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.os.Build
|
||||||
import de.robv.android.xposed.IXposedHookZygoteInit
|
import de.robv.android.xposed.IXposedHookZygoteInit
|
||||||
import de.robv.android.xposed.XC_MethodHook
|
import de.robv.android.xposed.XC_MethodHook
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
import de.robv.android.xposed.XposedHelpers
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import kotlinx.serialization.json.JsonObjectBuilder
|
import kotlinx.serialization.json.JsonObjectBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for every Xposed side module.
|
|
||||||
*
|
|
||||||
* The [Main] loader forwards the various [XC_LoadPackage] lifecycle callbacks to every
|
|
||||||
* registered [Module] instance. Modules may override any subset of the hooks below.
|
|
||||||
*/
|
|
||||||
open class Module {
|
|
||||||
open fun onInit(startupParam: IXposedHookZygoteInit.StartupParam) {}
|
|
||||||
|
|
||||||
open fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {}
|
|
||||||
|
|
||||||
open fun onContext(context: Context) {}
|
|
||||||
|
|
||||||
open fun onActivity(activity: Activity) {}
|
|
||||||
|
|
||||||
@Deprecated("This method is deprecated in the parent class")
|
|
||||||
open fun buildPayload(builder: JsonObjectBuilder) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime wrapper around an [XC_MethodHook.MethodHookParam] used as the receiver of every
|
|
||||||
* `before`/`after` hook block, exposing [thisObject], [args], [result] and [param] directly.
|
|
||||||
*/
|
|
||||||
class HookScope(val param: XC_MethodHook.MethodHookParam) {
|
|
||||||
val thisObject: Any?
|
|
||||||
get() = param.thisObject
|
|
||||||
|
|
||||||
val args: Array<Any?>
|
|
||||||
get() = param.args
|
|
||||||
|
|
||||||
var result: Any?
|
|
||||||
get() = param.result
|
|
||||||
set(value) {
|
|
||||||
param.result = value
|
|
||||||
}
|
|
||||||
|
|
||||||
var throwable: Throwable?
|
|
||||||
get() = param.throwable
|
|
||||||
set(value) {
|
|
||||||
param.throwable = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DSL builder for lazily constructed [XC_MethodHook] instances.
|
|
||||||
*
|
|
||||||
* ```kotlin
|
|
||||||
* val hook = MethodHookBuilder().run {
|
|
||||||
* before { result = null }
|
|
||||||
* after { Log.i("done") }
|
|
||||||
* build()
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
class MethodHookBuilder {
|
|
||||||
private val beforeCallbacks = mutableListOf<HookScope.() -> Unit>()
|
|
||||||
private val afterCallbacks = mutableListOf<HookScope.() -> Unit>()
|
|
||||||
|
|
||||||
fun before(callback: HookScope.() -> Unit) {
|
|
||||||
beforeCallbacks += callback
|
|
||||||
}
|
|
||||||
|
|
||||||
fun after(callback: HookScope.() -> Unit) {
|
|
||||||
afterCallbacks += callback
|
|
||||||
}
|
|
||||||
|
|
||||||
fun build(): XC_MethodHook = object : XC_MethodHook() {
|
|
||||||
override fun beforeHookedMethod(param: MethodHookParam) {
|
|
||||||
val scope = HookScope(param)
|
|
||||||
beforeCallbacks.forEach { scope.run(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun afterHookedMethod(param: MethodHookParam) {
|
|
||||||
val scope = HookScope(param)
|
|
||||||
afterCallbacks.forEach { scope.run(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun from(block: MethodHookBuilder.() -> Unit): XC_MethodHook = MethodHookBuilder().run {
|
|
||||||
block()
|
|
||||||
build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ClassLoader.safeLoadClass(name: String): Class<*>? = try {
|
|
||||||
loadClass(name)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Class<*>.method(name: String, vararg parameterTypes: Class<*>): Method =
|
|
||||||
XposedHelpers.findMethodExact(this, name, *parameterTypes)
|
|
||||||
|
|
||||||
fun Method.hook(block: MethodHookBuilder.() -> Unit) {
|
|
||||||
val builder = MethodHookBuilder()
|
|
||||||
builder.block()
|
|
||||||
XposedBridge.hookMethod(this, builder.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Method.hook(hook: XC_MethodHook) {
|
|
||||||
XposedBridge.hookMethod(this, hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Class<*>.hookMethod(
|
|
||||||
name: String,
|
|
||||||
vararg parameterTypes: Class<*>,
|
|
||||||
block: MethodHookBuilder.() -> Unit
|
|
||||||
): Method {
|
|
||||||
val method = method(name, *parameterTypes)
|
|
||||||
method.hook(block)
|
|
||||||
return method
|
|
||||||
}
|
|
||||||
|
|
||||||
fun File.asDir(): File {
|
|
||||||
if (!exists() || !isDirectory) mkdirs()
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun File.asFile(): File {
|
|
||||||
parentFile?.mkdirs()
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
data class AppInfo(
|
data class AppInfo(
|
||||||
val name: String,
|
val name: String,
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val version: String,
|
val version: String,
|
||||||
val versionCode: Int
|
val versionCode: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Context.getAppInfo(): AppInfo {
|
abstract class Module {
|
||||||
val label = applicationInfo.loadLabel(packageManager).toString()
|
/**
|
||||||
val packageInfo = try {
|
* Builds a JSON payload to be injected into the JavaScript context.
|
||||||
packageManager.getPackageInfo(packageName, 0)
|
*/
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
@Deprecated("This will be removed in future versions. Payloads can be replaced via synchronous bridge methods.")
|
||||||
null
|
open fun buildPayload(builder: JsonObjectBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppInfo(
|
/**
|
||||||
name = label,
|
* Called during Zygote initialization.
|
||||||
packageName = packageName,
|
*/
|
||||||
version = packageInfo?.versionName ?: "",
|
open fun onInit(startupParam: IXposedHookZygoteInit.StartupParam) {}
|
||||||
versionCode = packageInfo?.versionCode ?: 0
|
|
||||||
)
|
/**
|
||||||
|
* Called when a package is loaded.
|
||||||
|
*
|
||||||
|
* Hooks should typically be set up here.
|
||||||
|
*
|
||||||
|
* Bridge methods that do not require [Context] can also be registered here.
|
||||||
|
*/
|
||||||
|
open fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after [Context] has been attached to a [android.content.ContextWrapper]
|
||||||
|
* via [android.content.ContextWrapper.attachBaseContext].
|
||||||
|
*
|
||||||
|
* This may be called multiple times for different [Context]s, e.g. when the app is killed by the system.
|
||||||
|
*
|
||||||
|
* For bridge methods that require access to [Context], register them here.
|
||||||
|
*/
|
||||||
|
open fun onContext(context: Context) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after an [Activity.onCreate] method is executed.
|
||||||
|
*
|
||||||
|
* UI code that requires an [Activity] context can be placed here.
|
||||||
|
*
|
||||||
|
* Bridge methods must not be registered here, as it can be too late and cause crashes.
|
||||||
|
*
|
||||||
|
* If you need to register bridge methods that require an [Activity], do it in [onContext] and make it async.
|
||||||
|
*
|
||||||
|
* Once an [Activity] is available, execute the queued calls. Then from JS, call the method asynchronously.
|
||||||
|
*/
|
||||||
|
open fun onActivity(activity: Activity) {}
|
||||||
|
|
||||||
|
protected fun Context.getAppInfo(): AppInfo {
|
||||||
|
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
|
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) pInfo.longVersionCode
|
||||||
|
else @Suppress("DEPRECATION") pInfo.versionCode.toLong()
|
||||||
|
|
||||||
|
return AppInfo(
|
||||||
|
packageManager.getApplicationLabel(applicationInfo).toString(),
|
||||||
|
packageName,
|
||||||
|
pInfo.versionName ?: versionCode.toString(),
|
||||||
|
versionCode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, vararg params: Class<*>?
|
||||||
|
): Method = getDeclaredMethod(name, *params).apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun ClassLoader.safeLoadClass(name: String): Class<*>? = runCatching { loadClass(name) }.getOrNull()
|
||||||
|
|
||||||
|
protected fun Method.hook(hook: XC_MethodHook): XC_MethodHook.Unhook = XposedBridge.hookMethod(this, hook)
|
||||||
|
|
||||||
|
protected fun Method.hook(block: MethodHookBuilder.() -> Unit): XC_MethodHook.Unhook =
|
||||||
|
hook(MethodHookBuilder().apply(block).build())
|
||||||
|
|
||||||
|
protected fun Class<*>.hookMethod(
|
||||||
|
name: String, vararg params: Class<*>?, block: MethodHookBuilder.() -> Unit
|
||||||
|
): XC_MethodHook.Unhook = method(name, *params).hook(MethodHookBuilder().apply(block).build())
|
||||||
|
|
||||||
|
protected class MethodHookBuilder {
|
||||||
|
private var beforeBlock: (HookScope.() -> Unit)? = null
|
||||||
|
private var afterBlock: (HookScope.() -> Unit)? = null
|
||||||
|
|
||||||
|
fun before(block: HookScope.() -> Unit) {
|
||||||
|
beforeBlock = block
|
||||||
|
}
|
||||||
|
|
||||||
|
fun after(block: HookScope.() -> Unit) {
|
||||||
|
afterBlock = block
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): XC_MethodHook = object : XC_MethodHook() {
|
||||||
|
override fun beforeHookedMethod(param: MethodHookParam) {
|
||||||
|
val b = beforeBlock
|
||||||
|
if (b != null) {
|
||||||
|
val scope = HookScope(
|
||||||
|
param = param, proceed = { p ->
|
||||||
|
super.beforeHookedMethod(p)
|
||||||
|
})
|
||||||
|
scope.b()
|
||||||
|
} else {
|
||||||
|
super.beforeHookedMethod(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterHookedMethod(param: MethodHookParam) {
|
||||||
|
val a = afterBlock
|
||||||
|
if (a != null) {
|
||||||
|
val scope = HookScope(
|
||||||
|
param = param, proceed = { p ->
|
||||||
|
super.afterHookedMethod(p)
|
||||||
|
})
|
||||||
|
scope.a()
|
||||||
|
} else {
|
||||||
|
super.afterHookedMethod(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope object passed to before/after hook blocks.
|
||||||
|
*
|
||||||
|
* Provides:
|
||||||
|
* - Access to the [param] object
|
||||||
|
* - [proceed] to call the original XC_MethodHook super method
|
||||||
|
* - Accessors for `thisObject`, `args`, `result`, and `throwable`
|
||||||
|
*
|
||||||
|
* @property param The [XC_MethodHook.MethodHookParam] for the current hook.
|
||||||
|
* @property proceed Function that calls the super method.
|
||||||
|
*/
|
||||||
|
protected class HookScope internal constructor(
|
||||||
|
val param: XC_MethodHook.MethodHookParam, private val proceed: (XC_MethodHook.MethodHookParam) -> Unit
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Continues with the default XC_MethodHook super behavior.
|
||||||
|
* Equivalent to calling `super.beforeHookedMethod(param)` or
|
||||||
|
* `super.afterHookedMethod(param)` depending on the phase.
|
||||||
|
*/
|
||||||
|
fun proceed() = proceed(param)
|
||||||
|
|
||||||
|
val thisObject: Any? get() = param.thisObject
|
||||||
|
|
||||||
|
val args: Array<Any?> get() = param.args
|
||||||
|
|
||||||
|
var result: Any?
|
||||||
|
get() = param.result
|
||||||
|
set(value) {
|
||||||
|
param.result = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var throwable: Throwable?
|
||||||
|
get() = param.throwable
|
||||||
|
set(value) {
|
||||||
|
param.throwable = value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
package GoonXposed.xposed
|
package GoonXposed.xposed
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.Application
|
import android.app.AndroidAppHelper
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import de.robv.android.xposed.XposedHelpers
|
|
||||||
import GoonXposed.xposed.modules.UpdaterModule
|
import GoonXposed.xposed.modules.UpdaterModule
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -15,9 +14,7 @@ class Utils {
|
||||||
val JSON = Json { ignoreUnknownKeys = true }
|
val JSON = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
fun reloadApp() {
|
fun reloadApp() {
|
||||||
val activityThread = XposedHelpers.findClass("android.app.ActivityThread", null)
|
val application = AndroidAppHelper.currentApplication()
|
||||||
val application =
|
|
||||||
XposedHelpers.callStaticMethod(activityThread, "currentApplication") as? Application ?: return
|
|
||||||
val intent = application.packageManager.getLaunchIntentForPackage(application.packageName)
|
val intent = application.packageManager.getLaunchIntentForPackage(application.packageName)
|
||||||
application.startActivity(Intent.makeRestartActivityTask(intent!!.component))
|
application.startActivity(Intent.makeRestartActivityTask(intent!!.component))
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ import android.content.Context
|
||||||
import android.util.AtomicFile
|
import android.util.AtomicFile
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.asDir
|
|
||||||
import GoonXposed.xposed.asFile
|
|
||||||
import GoonXposed.xposed.getAppInfo
|
|
||||||
import GoonXposed.xposed.modules.bridge.BridgeModule
|
import GoonXposed.xposed.modules.bridge.BridgeModule
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
package GoonXposed.xposed.modules
|
package GoonXposed.xposed.modules
|
||||||
|
|
||||||
import GoonXposed.xposed.hookMethod
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.Constants.Companion.TARGET_PACKAGE
|
import GoonXposed.xposed.Constants.Companion.TARGET_PACKAGE
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.hookMethod
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks [Resources.getIdentifier] to fix resource package name mismatch.
|
* Hooks [Resources.getIdentifier] to fix resource package name mismatch.
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,9 @@ import de.robv.android.xposed.IXposedHookZygoteInit
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.Constants
|
import GoonXposed.xposed.Constants
|
||||||
import GoonXposed.xposed.HookScope
|
|
||||||
import GoonXposed.xposed.HookStateHolder
|
import GoonXposed.xposed.HookStateHolder
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.asDir
|
|
||||||
import GoonXposed.xposed.asFile
|
|
||||||
import GoonXposed.xposed.hook
|
|
||||||
import GoonXposed.xposed.method
|
|
||||||
import GoonXposed.xposed.safeLoadClass
|
|
||||||
import GoonXposed.xposed.modules.HookScriptLoaderModule.PRELOADS_DIR
|
import GoonXposed.xposed.modules.HookScriptLoaderModule.PRELOADS_DIR
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
|
@ -60,11 +54,11 @@ object HookScriptLoaderModule : Module() {
|
||||||
|
|
||||||
private fun hook(instance: Class<*>) = runCatching {
|
private fun hook(instance: Class<*>) = runCatching {
|
||||||
val loadScriptFromAssets = instance.method(
|
val loadScriptFromAssets = instance.method(
|
||||||
"loadScriptFromAssets", AssetManager::class.java, String::class.java, Boolean::class.javaPrimitiveType!!
|
"loadScriptFromAssets", AssetManager::class.java, String::class.java, Boolean::class.javaPrimitiveType
|
||||||
)
|
)
|
||||||
|
|
||||||
val loadScriptFromFile = instance.method(
|
val loadScriptFromFile = instance.method(
|
||||||
"loadScriptFromFile", String::class.java, String::class.java, Boolean::class.javaPrimitiveType!!
|
"loadScriptFromFile", String::class.java, String::class.java, Boolean::class.javaPrimitiveType
|
||||||
)
|
)
|
||||||
|
|
||||||
loadScriptFromAssets.hook {
|
loadScriptFromAssets.hook {
|
||||||
|
|
@ -113,10 +107,14 @@ object HookScriptLoaderModule : Module() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// If a disabled marker exists next to the cached bundle, treat this as a global
|
||||||
|
// disable for the entire script-loading step. In that case, we skip preloads,
|
||||||
|
// cached main script and the assets fallback so nothing is injected.
|
||||||
val disabledMarker = File(mainScript.parentFile, "${Constants.MAIN_SCRIPT_FILE}.disabled")
|
val disabledMarker = File(mainScript.parentFile, "${Constants.MAIN_SCRIPT_FILE}.disabled")
|
||||||
if (disabledMarker.exists()) {
|
if (disabledMarker.exists()) {
|
||||||
Log.i("Script loading disabled by marker; skipping preloads, cached bundle and fallback")
|
Log.i("Script loading disabled by marker; skipping preloads, cached bundle and fallback")
|
||||||
} else {
|
} else {
|
||||||
|
// Normal behaviour: run preloads then cached main script or fallback to bundled asset
|
||||||
preloadsDir.walk().filter { it.isFile }.forEach(runScriptFile)
|
preloadsDir.walk().filter { it.isFile }.forEach(runScriptFile)
|
||||||
|
|
||||||
if (mainScript.exists()) {
|
if (mainScript.exists()) {
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ object LogBoxActions {
|
||||||
val config = JSONObject(configFile.readText())
|
val config = JSONObject(configFile.readText())
|
||||||
val customLoadUrl = config.optJSONObject("customLoadUrl") ?: JSONObject()
|
val customLoadUrl = config.optJSONObject("customLoadUrl") ?: JSONObject()
|
||||||
customLoadUrl.put("enabled", false)
|
customLoadUrl.put("enabled", false)
|
||||||
customLoadUrl.put("url", "http://localhost:4040/gooncord.js")
|
customLoadUrl.put("url", "http://localhost:4040/shiggycord.js")
|
||||||
config.put("customLoadUrl", customLoadUrl)
|
config.put("customLoadUrl", customLoadUrl)
|
||||||
configFile.writeText(config.toString())
|
configFile.writeText(config.toString())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.Utils.Companion.reloadApp
|
import GoonXposed.xposed.Utils.Companion.reloadApp
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import de.robv.android.xposed.XC_MethodHook
|
|
||||||
import de.robv.android.xposed.XC_MethodReplacement
|
import de.robv.android.xposed.XC_MethodReplacement
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
import de.robv.android.xposed.XposedHelpers
|
import de.robv.android.xposed.XposedHelpers
|
||||||
|
|
@ -24,15 +23,11 @@ object LogBoxModule : Module() {
|
||||||
val getUseDeveloperSupportMethod =
|
val getUseDeveloperSupportMethod =
|
||||||
dcdReactNativeHostClass.methods.first { it.name == "getUseDeveloperSupport" }
|
dcdReactNativeHostClass.methods.first { it.name == "getUseDeveloperSupport" }
|
||||||
|
|
||||||
// NOTE: This used to call a custom `.hook { before { result = true } }` DSL that
|
getUseDeveloperSupportMethod.hook {
|
||||||
// relied on a shared extensions file which broke (unresolved references).
|
before {
|
||||||
// Rewritten using the standard XposedBridge/XC_MethodHook API, which is already
|
result = true
|
||||||
// used elsewhere in this file, to force this method to always return true.
|
|
||||||
XposedBridge.hookMethod(getUseDeveloperSupportMethod, object : XC_MethodHook() {
|
|
||||||
override fun beforeHookedMethod(param: MethodHookParam) {
|
|
||||||
param.result = true
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
Log.e("Successfully hooked DCDReactNativeHost")
|
Log.e("Successfully hooked DCDReactNativeHost")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("Failed to hook DCDReactNativeHost: ${e.message}")
|
Log.e("Failed to hook DCDReactNativeHost: ${e.message}")
|
||||||
|
|
|
||||||
|
|
@ -19,29 +19,40 @@ object OptionsMenu {
|
||||||
container.addView(LogBoxComponents.createM3Button(context, injectionText, colors) {
|
container.addView(LogBoxComponents.createM3Button(context, injectionText, colors) {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
LogBoxActions.toggleBundleInjection(context)
|
LogBoxActions.toggleBundleInjection(context)
|
||||||
GoonXposed.xposed.Utils.Companion.reloadApp()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
container.addView(LogBoxComponents.createM3Button(context, "Refetch Bundle", colors) {
|
container.addView(LogBoxComponents.createM3Button(context, "Refetch Bundle", colors) {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
RecoveryMenu.showConfirmAction(
|
RecoveryMenu::class.java.getDeclaredMethod(
|
||||||
|
"showConfirmAction",
|
||||||
|
Context::class.java,
|
||||||
|
String::class.java,
|
||||||
|
String::class.java,
|
||||||
|
Runnable::class.java
|
||||||
|
).apply { isAccessible = true }.invoke(
|
||||||
|
null,
|
||||||
context,
|
context,
|
||||||
"Refetch Bundle",
|
"Refetch Bundle",
|
||||||
"This will download the latest bundle from Github."
|
"This will download the latest bundle from Github.",
|
||||||
) {
|
Runnable { LogBoxActions.refetchBundle(context) }
|
||||||
LogBoxActions.refetchBundle(context)
|
)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
container.addView(LogBoxComponents.createM3Button(context, "Clear Cache & Reset", colors) {
|
container.addView(LogBoxComponents.createM3Button(context, "Clear Cache & Reset", colors) {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
RecoveryMenu.showConfirmAction(
|
RecoveryMenu::class.java.getDeclaredMethod(
|
||||||
|
"showConfirmAction",
|
||||||
|
Context::class.java,
|
||||||
|
String::class.java,
|
||||||
|
String::class.java,
|
||||||
|
Runnable::class.java
|
||||||
|
).apply { isAccessible = true }.invoke(
|
||||||
|
null,
|
||||||
context,
|
context,
|
||||||
"Clear Cache & Reset",
|
"Clear Cache & Reset",
|
||||||
"This will clear all cached bundles and reset to default settings."
|
"This will clear all cached bundles and reset to default settings.",
|
||||||
) {
|
Runnable { LogBoxActions.clearCacheAndReset(context) }
|
||||||
LogBoxActions.clearCacheAndReset(context)
|
)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
container.addView(LogBoxComponents.createM3Button(context, "Back", colors) {
|
container.addView(LogBoxComponents.createM3Button(context, "Back", colors) {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ object RecoveryMenu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showConfirmAction(context: Context, title: String, message: String, action: () -> Unit) {
|
private fun showConfirmAction(context: Context, title: String, message: String, action: () -> Unit) {
|
||||||
val colors = LogBoxTheme.getM3Colors(context)
|
val colors = LogBoxTheme.getM3Colors(context)
|
||||||
val container = LogBoxComponents.createMenuContainer(context, colors)
|
val container = LogBoxComponents.createMenuContainer(context, colors)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.setPadding
|
import androidx.core.view.setPadding
|
||||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
|
||||||
import de.robv.android.xposed.XC_MethodReplacement
|
import de.robv.android.xposed.XC_MethodReplacement
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
import de.robv.android.xposed.XposedHelpers
|
import de.robv.android.xposed.XposedHelpers
|
||||||
|
|
@ -27,7 +26,6 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Companion.reloadApp
|
import GoonXposed.xposed.Utils.Companion.reloadApp
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.hook
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,8 @@ import de.robv.android.xposed.XposedBridge
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.BuildConfig
|
import GoonXposed.xposed.BuildConfig
|
||||||
import GoonXposed.xposed.Constants
|
import GoonXposed.xposed.Constants
|
||||||
import GoonXposed.xposed.MethodHookBuilder
|
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Companion.JSON
|
import GoonXposed.xposed.Utils.Companion.JSON
|
||||||
import GoonXposed.xposed.hook
|
|
||||||
import GoonXposed.xposed.method
|
|
||||||
import GoonXposed.xposed.safeLoadClass
|
|
||||||
import GoonXposed.xposed.modules.HookScriptLoaderModule.PRELOADS_DIR
|
import GoonXposed.xposed.modules.HookScriptLoaderModule.PRELOADS_DIR
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
|
|
@ -52,7 +48,7 @@ class PayloadGlobalModule(private val modules: List<Module>) : Module() {
|
||||||
"loadScriptFromFile",
|
"loadScriptFromFile",
|
||||||
String::class.java,
|
String::class.java,
|
||||||
String::class.java,
|
String::class.java,
|
||||||
Boolean::class.javaPrimitiveType!!
|
Boolean::class.javaPrimitiveType
|
||||||
), param.thisObject, arrayOf(absolutePath, absolutePath, param.args[2])
|
), param.thisObject, arrayOf(absolutePath, absolutePath, param.args[2])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -74,11 +70,11 @@ class PayloadGlobalModule(private val modules: List<Module>) : Module() {
|
||||||
|
|
||||||
private fun hook(instance: Class<*>, hook: XC_MethodHook) {
|
private fun hook(instance: Class<*>, hook: XC_MethodHook) {
|
||||||
instance.method(
|
instance.method(
|
||||||
"loadScriptFromAssets", AssetManager::class.java, String::class.java, Boolean::class.javaPrimitiveType!!,
|
"loadScriptFromAssets", AssetManager::class.java, String::class.java, Boolean::class.javaPrimitiveType,
|
||||||
).hook(hook)
|
).hook(hook)
|
||||||
|
|
||||||
instance.method(
|
instance.method(
|
||||||
"loadScriptFromFile", String::class.java, String::class.java, Boolean::class.javaPrimitiveType!!
|
"loadScriptFromFile", String::class.java, String::class.java, Boolean::class.javaPrimitiveType
|
||||||
).hook(hook)
|
).hook(hook)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
||||||
import android.util.AtomicFile
|
import android.util.AtomicFile
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.asFile
|
|
||||||
import GoonXposed.xposed.modules.bridge.BridgeModule
|
import GoonXposed.xposed.modules.bridge.BridgeModule
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ object UpdaterModule : Module() {
|
||||||
private const val CONFIG_FILE = "loader.json"
|
private const val CONFIG_FILE = "loader.json"
|
||||||
|
|
||||||
private const val DEFAULT_BUNDLE_URL =
|
private const val DEFAULT_BUNDLE_URL =
|
||||||
"https://github.com/OTKUSteyler/GoonCord/releases/latest/download/gooncord.js"
|
"https://github.com/OTKUSteyler/GoonCord/releases/download/latest/gooncord.js"
|
||||||
|
|
||||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
||||||
// store app data dir for later checks (LogBox settings live under files/logbox)
|
// store app data dir for later checks (LogBox settings live under files/logbox)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
@file:Suppress("DEPRECATION")
|
|
||||||
|
|
||||||
package GoonXposed.xposed.modules.appearance
|
package GoonXposed.xposed.modules.appearance
|
||||||
|
|
||||||
import android.content.res.AssetManager
|
import android.content.res.AssetManager
|
||||||
|
|
@ -29,14 +27,6 @@ import kotlinx.serialization.json.put
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
// NOTE: These extensions used to be resolved from a shared Utils/Extensions file,
|
|
||||||
// but that reference broke (renamed/removed/relocated), causing "Unresolved reference"
|
|
||||||
// build failures. Defining them locally here removes the dependency on that file.
|
|
||||||
// asFile(): ensures the parent directory of a file exists.
|
|
||||||
// asDir(): ensures the directory itself exists.
|
|
||||||
private fun File.asFile(): File = apply { parentFile?.mkdirs() }
|
|
||||||
private fun File.asDir(): File = apply { mkdirs() }
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FontDefinition(
|
data class FontDefinition(
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
|
|
@ -103,40 +93,28 @@ object FontsModule : Module() {
|
||||||
|
|
||||||
// These files should be downloaded by the JS side, but oh well
|
// These files should be downloaded by the JS side, but oh well
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val client = HttpClient(CIO) {
|
fontDef.main.keys.map { name ->
|
||||||
install(UserAgent) { agent = Constants.USER_AGENT }
|
async {
|
||||||
install(HttpTimeout) {
|
val url = fontDef.main.getValue(name)
|
||||||
requestTimeoutMillis = 15_000
|
try {
|
||||||
connectTimeoutMillis = 10_000
|
Log.i("Downloading $name from $url")
|
||||||
socketTimeoutMillis = 15_000
|
val file =
|
||||||
}
|
File(fontsDir, "$name${FILE_EXTENSIONS.first { url.endsWith(it) }}").apply { asFile() }
|
||||||
}
|
if (file.exists()) return@async
|
||||||
|
|
||||||
try {
|
val client = HttpClient(CIO) {
|
||||||
fontDef.main.keys.map { name ->
|
install(UserAgent) { agent = Constants.USER_AGENT }
|
||||||
async {
|
|
||||||
val url = fontDef.main.getValue(name)
|
|
||||||
try {
|
|
||||||
val extension = FILE_EXTENSIONS.firstOrNull { url.substringBefore('?').endsWith(it) }
|
|
||||||
?: FILE_EXTENSIONS[0]
|
|
||||||
val file = File(fontsDir, "$name$extension").apply { asFile() }
|
|
||||||
if (file.exists()) return@async
|
|
||||||
|
|
||||||
Log.i("Downloading $name from $url")
|
|
||||||
val response: HttpResponse = client.get(url)
|
|
||||||
if (response.status == HttpStatusCode.OK) {
|
|
||||||
file.writeBytes(response.body())
|
|
||||||
} else {
|
|
||||||
Log.e("Failed to download fonts ($name from $url): HTTP ${response.status}")
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Log.e("Failed to download fonts ($name from $url)", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val response: HttpResponse = client.get(url)
|
||||||
|
if (response.status == HttpStatusCode.OK) file.writeBytes(response.body())
|
||||||
|
|
||||||
|
return@async
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e("Failed to download fonts ($name from $url)", e)
|
||||||
}
|
}
|
||||||
}.awaitAll()
|
}
|
||||||
} finally {
|
}.awaitAll()
|
||||||
client.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package GoonXposed.xposed.modules.appearance
|
package GoonXposed.xposed.modules.appearance
|
||||||
|
|
||||||
import android.R.color
|
import android.R.color
|
||||||
|
import android.app.AndroidAppHelper
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import de.robv.android.xposed.XposedHelpers
|
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
@ -15,11 +15,7 @@ object SysColorsModule : Module() {
|
||||||
|
|
||||||
@Deprecated("This method is deprecated in the parent class")
|
@Deprecated("This method is deprecated in the parent class")
|
||||||
override fun buildPayload(builder: JsonObjectBuilder) {
|
override fun buildPayload(builder: JsonObjectBuilder) {
|
||||||
context = WeakReference(runCatching {
|
context = WeakReference(AndroidAppHelper.currentApplication())
|
||||||
XposedHelpers.callStaticMethod(
|
|
||||||
XposedHelpers.findClass("android.app.ActivityThread", null), "currentApplication"
|
|
||||||
) as Context
|
|
||||||
}.getOrNull())
|
|
||||||
val accents = arrayOf("accent1", "accent2", "accent3", "neutral1", "neutral2")
|
val accents = arrayOf("accent1", "accent2", "accent3", "neutral1", "neutral2")
|
||||||
val shades = arrayOf(0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
|
val shades = arrayOf(0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
|
||||||
|
|
||||||
|
|
@ -42,8 +38,7 @@ object SysColorsModule : Module() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertToColor(id: Int): String {
|
private fun convertToColor(id: Int): String {
|
||||||
val ctx = context.get()
|
val clr = if (isSupported()) ContextCompat.getColor(context.get()!!, id) else 0
|
||||||
val clr = if (isSupported() && ctx != null) ContextCompat.getColor(ctx, id) else 0
|
|
||||||
return String.format("#%06X", 0xFFFFFF and clr)
|
return String.format("#%06X", 0xFFFFFF and clr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,8 @@ import android.content.res.Resources
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.Constants
|
import GoonXposed.xposed.Constants
|
||||||
import GoonXposed.xposed.MethodHookBuilder
|
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Companion.JSON
|
import GoonXposed.xposed.Utils.Companion.JSON
|
||||||
import GoonXposed.xposed.asFile
|
|
||||||
import GoonXposed.xposed.hook
|
|
||||||
import GoonXposed.xposed.method
|
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.JsonObjectBuilder
|
import kotlinx.serialization.json.JsonObjectBuilder
|
||||||
|
|
@ -46,6 +42,7 @@ object ThemesModule : Module() {
|
||||||
|
|
||||||
private const val THEME_FILE = "current-theme.json"
|
private const val THEME_FILE = "current-theme.json"
|
||||||
|
|
||||||
|
|
||||||
@Deprecated("This method is deprecated in the parent class")
|
@Deprecated("This method is deprecated in the parent class")
|
||||||
@ExperimentalSerializationApi
|
@ExperimentalSerializationApi
|
||||||
override fun buildPayload(builder: JsonObjectBuilder) {
|
override fun buildPayload(builder: JsonObjectBuilder) {
|
||||||
|
|
@ -85,20 +82,10 @@ object ThemesModule : Module() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hookTheme() {
|
fun hookTheme() {
|
||||||
try {
|
val themeManager = param.classLoader.loadClass("com.discord.theme.utils.ColorUtilsKt")
|
||||||
val themeManager = param.classLoader.loadClass("com.discord.theme.utils.ColorUtilsKt")
|
val darkTheme = param.classLoader.loadClass("com.discord.theme.DarkerTheme")
|
||||||
val darkTheme = param.classLoader.loadClass("com.discord.theme.DarkerTheme")
|
val lightTheme = param.classLoader.loadClass("com.discord.theme.LightTheme")
|
||||||
val lightTheme = param.classLoader.loadClass("com.discord.theme.LightTheme")
|
|
||||||
|
|
||||||
hookThemeInternal(themeManager, darkTheme, lightTheme)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
GoonXposed.xposed.Utils.Log.e("Failed to hook theme: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hookThemeInternal(
|
|
||||||
themeManager: Class<*>, darkTheme: Class<*>, lightTheme: Class<*>
|
|
||||||
) {
|
|
||||||
val theme = this.theme ?: return
|
val theme = this.theme ?: return
|
||||||
|
|
||||||
// Apply rawColors
|
// Apply rawColors
|
||||||
|
|
@ -120,53 +107,42 @@ object ThemesModule : Module() {
|
||||||
|
|
||||||
// If there's any rawColors value, hook the color getter
|
// If there's any rawColors value, hook the color getter
|
||||||
if (!theme.data.rawColors.isNullOrEmpty()) {
|
if (!theme.data.rawColors.isNullOrEmpty()) {
|
||||||
try {
|
val getColorCompat = themeManager.getDeclaredMethod(
|
||||||
val getColorCompat = themeManager.getDeclaredMethod(
|
"getColorCompat",
|
||||||
"getColorCompat",
|
Resources::class.java,
|
||||||
Resources::class.java,
|
Int::class.javaPrimitiveType,
|
||||||
Int::class.javaPrimitiveType,
|
Resources.Theme::class.java,
|
||||||
Resources.Theme::class.java,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
val getColorCompatLegacy = themeManager.getDeclaredMethod(
|
val getColorCompatLegacy = themeManager.getDeclaredMethod(
|
||||||
"getColorCompat", Context::class.java, Int::class.javaPrimitiveType
|
"getColorCompat", Context::class.java, Int::class.javaPrimitiveType
|
||||||
)
|
)
|
||||||
|
|
||||||
val patch = MethodHookBuilder().run {
|
val patch = MethodHookBuilder().run {
|
||||||
before {
|
before {
|
||||||
try {
|
val arg1 = args[0]
|
||||||
val arg1 = args[0]
|
val resources = if (arg1 is Context) arg1.resources else (arg1 as Resources)
|
||||||
val resources = if (arg1 is Context) arg1.resources else (arg1 as Resources)
|
val name = resources.getResourceEntryName(args[1] as Int)
|
||||||
val name = resources.getResourceEntryName(args[1] as Int)
|
|
||||||
|
|
||||||
if (rawColorMap[name] != null) result = rawColorMap[name]
|
if (rawColorMap[name] != null) result = rawColorMap[name]
|
||||||
} catch (_: Throwable) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getColorCompat.hook(patch)
|
build()
|
||||||
getColorCompatLegacy.hook(patch)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
GoonXposed.xposed.Utils.Log.e("Failed to hook getColorCompat: ${e.message}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getColorCompat.hook(patch)
|
||||||
|
getColorCompatLegacy.hook(patch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse HEX colour string to INT. Takes "#RRGGBBAA" or "#RRGGBB"
|
// Parse HEX colour string to INT. Takes "#RRGGBBAA" or "#RRGGBB"
|
||||||
private fun hexStringToColorInt(hexString: String): Int {
|
private fun hexStringToColorInt(hexString: String): Int {
|
||||||
return try {
|
return if (hexString.length == 9) {
|
||||||
if (hexString.length == 9) {
|
// Rearrange RRGGBBAA -> AARRGGBB so parseColor() is happy
|
||||||
// Rearrange RRGGBBAA -> AARRGGBB so parseColor() is happy
|
val alpha = hexString.substring(7, 9)
|
||||||
val alpha = hexString.substring(7, 9)
|
val rrggbb = hexString.substring(1, 7)
|
||||||
val rrggbb = hexString.substring(1, 7)
|
"#$alpha$rrggbb".toColorInt()
|
||||||
"#$alpha$rrggbb".toColorInt()
|
} else hexString.toColorInt()
|
||||||
} else hexString.toColorInt()
|
|
||||||
} catch (_: Throwable) {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hookThemeMethod(themeClass: Class<*>, methodName: String, themeValue: Int) {
|
private fun hookThemeMethod(themeClass: Class<*>, methodName: String, themeValue: Int) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils
|
import GoonXposed.xposed.Utils
|
||||||
import GoonXposed.xposed.getAppInfo
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object AdditionalBridgeMethodsModule : Module() {
|
object AdditionalBridgeMethodsModule : Module() {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import GoonXposed.xposed.BuildConfig
|
||||||
import GoonXposed.xposed.Constants
|
import GoonXposed.xposed.Constants
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.hookMethod
|
|
||||||
import GoonXposed.xposed.method
|
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import android.content.Context
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.hookMethod
|
|
||||||
import GoonXposed.xposed.safeLoadClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks Discord's crash reporting to disable Sentry initialization.
|
* Hooks Discord's crash reporting to disable Sentry initialization.
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import android.content.Context
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||||
import GoonXposed.xposed.Module
|
import GoonXposed.xposed.Module
|
||||||
import GoonXposed.xposed.Utils.Log
|
import GoonXposed.xposed.Utils.Log
|
||||||
import GoonXposed.xposed.hookMethod
|
|
||||||
import GoonXposed.xposed.safeLoadClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks Discord's deep links tracking to disable AppsFlyer initialization.
|
* Hooks Discord's deep links tracking to disable AppsFlyer initialization.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue