Update Module.kt

This commit is contained in:
OTKUSteyler 2026-09-03 18:29:43 +02:00 • committed by GitHub
parent 13bdcdad03
commit 765c546c20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,182 +2,75 @@ package GoonXposed.xposed
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.os.Build import android.hardware.Sensor
import de.robv.android.xposed.IXposedHookZygoteInit import android.hardware.SensorEvent
import de.robv.android.xposed.XC_MethodHook import android.hardware.SensorEventListener
import android.hardware.SensorManager
import de.robv.android.xposed.IXposedHookLoadPackage
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 kotlinx.serialization.json.JsonObjectBuilder import GoonXposed.xposed.modules.appearance.FontsModule
import java.io.File
import java.lang.reflect.Method
data class AppInfo( class MainModule : IXposedHookLoadPackage {
val name: String, private var lastShakeTime: Long = 0
val packageName: String, private val SHAKE_THRESHOLD = 15f
val version: String, private val SHAKE_INTERVAL_MS = 500L
val versionCode: Long,
)
abstract class Module { override fun handleLoadPackage(lparam: XC_LoadPackage.LoadPackageParam) {
/** // Load the fonts module
* Builds a JSON payload to be injected into the JavaScript context. FontsModule.onLoad(lparam)
*/
@Deprecated("This will be removed in future versions. Payloads can be replaced via synchronous bridge methods.")
open fun buildPayload(builder: JsonObjectBuilder) {
}
/** XposedBridge.log("GoonXposed: FontsModule loaded for ${lparam.packageName}")
* Called during Zygote initialization.
*/
open fun onInit(startupParam: IXposedHookZygoteInit.StartupParam) {}
/** // Hook into the main activity to detect shakes
* Called when a package is loaded. try {
* val activityClass = Class.forName("android.app.Activity")
* Hooks should typically be set up here. val method = activityClass.getDeclaredMethod("onCreate", Bundle::class.java)
*
* Bridge methods that do not require [Context] can also be registered here.
*/
open fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {}
/** XposedBridge.hookMethod(method, object : XC_MethodHook() {
* Called after [Context] has been attached to a [android.content.ContextWrapper] override fun afterHookedMethod(param: MethodHookParam) {
* via [android.content.ContextWrapper.attachBaseContext]. val activity = param.thisObject as Activity
* setupShakeDetection(activity)
* 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)
} }
} })
} catch (e: Throwable) {
override fun afterHookedMethod(param: MethodHookParam) { XposedBridge.log("GoonXposed: Failed to hook activity: ${e.message}")
val a = afterBlock
if (a != null) {
val scope = HookScope(
param = param, proceed = { p ->
super.afterHookedMethod(p)
})
scope.a()
} else {
super.afterHookedMethod(param)
}
}
} }
} }
/** private fun setupShakeDetection(activity: Activity) {
* Scope object passed to before/after hook blocks. try {
* val sensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager
* Provides: val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
* - 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 if (accelerometer != null) {
sensorManager.registerListener(object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
val args: Array<Any?> get() = param.args val acceleration = Math.sqrt((x * x + y * y + z * z).toDouble()).toFloat()
val currentTime = System.currentTimeMillis()
var result: Any? if (acceleration > SHAKE_THRESHOLD &&
get() = param.result currentTime - lastShakeTime > SHAKE_INTERVAL_MS) {
set(value) { lastShakeTime = currentTime
param.result = value onShakeDetected(activity)
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}, accelerometer, SensorManager.SENSOR_DELAY_UI)
} }
} catch (e: Throwable) {
XposedBridge.log("GoonXposed: Failed to setup shake detection: ${e.message}")
}
}
var throwable: Throwable? private fun onShakeDetected(activity: Activity) {
get() = param.throwable XposedBridge.log("GoonXposed: Shake detected!")
set(value) { // You can show a toast or dialog here
param.throwable = value // Or launch your UI
}
} }
} }