From d54e3dd25ad44c508504a33e0bc753ac84e717d1 Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Thu, 4 Sep 2025 21:59:37 +0700 Subject: [PATCH] feat(modules): add `PluginsModule` Only adding saving states for now. --- .../kotlin/io/github/revenge/xposed/Main.kt | 1 + .../revenge/xposed/modules/PluginsModule.kt | 116 ++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 app/src/main/kotlin/io/github/revenge/xposed/modules/PluginsModule.kt 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 e6018fb..b7afc7f 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/Main.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt @@ -20,6 +20,7 @@ class Main : Module(), IXposedHookLoadPackage, IXposedHookZygoteInit { HookScriptLoaderModule(), BridgeModule(), AdditionalBridgeMethodsModule(), + PluginsModule(), UpdaterModule(), FixResourcesModule(), LogBoxModule(), diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/PluginsModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/PluginsModule.kt new file mode 100644 index 0000000..dd8f3f3 --- /dev/null +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/PluginsModule.kt @@ -0,0 +1,116 @@ +package io.github.revenge.xposed.modules + +import android.app.Activity +import android.util.AtomicFile +import io.github.revenge.xposed.Module +import io.github.revenge.xposed.Utils.Log +import io.github.revenge.xposed.modules.bridge.BridgeModule +import java.io.* + +class PluginsModule : Module() { + private val DATA_DIR = "revenge/plugins" + private val STATES_FILE = "states" + + private lateinit var states: PluginStates + + override fun onActivity(activity: Activity) = with(activity) { + val dataDir = File(filesDir, DATA_DIR) + + val statesFile = File( + dataDir, + STATES_FILE + ).apply { asFile() } + + BridgeModule.registerMethod("revenge.plugins.states.read") { + if (::states.isInitialized) states.toMap() else + PluginStates.loadFromFileOrNull(statesFile)?.let { + states = it + it.toMap() + } + } + + BridgeModule.registerMethod("revenge.plugins.states.write") { + val (flags) = it + @Suppress("UNCHECKED_CAST") + states = + PluginStates( + flags as Map + ) + .apply { saveToFile(statesFile) } + Log.i("Plugin states saved: ${statesFile.absolutePath}") + } + + return@with + } +} + +private class UnsupportedPluginStatesVersionException(version: Int) : + Throwable("Unsupported plugin states version: $version") + +data class PluginStates( + val flags: Map +) { + fun saveToFile(file: File) { + val atomic = AtomicFile(file) + var fos: FileOutputStream? = null + try { + fos = atomic.startWrite() + val out = DataOutputStream(BufferedOutputStream(fos)) + + out.writeInt(CURRENT_VERSION) + + // Write flags + out.writeInt(flags.size) + for ((id, flags) in flags) { + out.writeUTF(id) + out.writeInt(flags.toInt()) + } + + out.flush() + atomic.finishWrite(fos) + } catch (t: Throwable) { + if (fos != null) atomic.failWrite(fos) + throw t + } + } + + fun toMap(): Map = mapOf("flags" to flags) + + companion object { + const val CURRENT_VERSION = 1 + + fun loadFromFileOrNull(file: File): PluginStates? { + if (!file.exists() || file.length() <= 0L) return null + val atomic = AtomicFile(file) + + try { + DataInputStream(BufferedInputStream(atomic.openRead())).use { input -> + when (val version = input.readInt()) { + 1 -> return loadV1FromFileOrNull(input) + else -> throw UnsupportedPluginStatesVersionException(version) + } + } + } catch (e: UnsupportedPluginStatesVersionException) { + Log.i(e.message!!) + } catch (e: EOFException) { + Log.e("Plugin states corrupt: ${e.message}") + } catch (e: IOException) { + Log.e("Failed to read plugin states: ${e.message}") + } + + runCatching { file.delete() } + return null + } + + private fun loadV1FromFileOrNull(input: DataInputStream): PluginStates { + val flagsSize = input.readInt() + val flags = mutableMapOf() + + repeat(flagsSize) { + flags[input.readUTF()] = input.readInt().toDouble() + } + + return PluginStates(flags) + } + } +} \ No newline at end of file