fix(modules): register all native methods in onContext

This commit is contained in:
PalmDevs 2025-09-15 23:19:04 +07:00
parent f015246e94
commit c0045b2e1e
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
2 changed files with 82 additions and 54 deletions

View file

@ -1,68 +1,93 @@
package io.github.revenge.xposed.modules
import android.app.Activity
import android.content.Context
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.*
/**
* A module for caching versioned modules finds and blacklist, and assets mappings.
*
* See [ModulesCache] and [AssetsCache] for the cache structure.
*
* ## Methods
*
* ### Modules cache
*
* - `revenge.caches.modules.read() : { blacklist: number[], finds: { [filter: string]: { [id: string]: number } | null }, version: number } | null`
* - Reads the modules cache. Returns `null` if the cache file does not exist or is invalid.
*
* - `revenge.caches.modules.write(blacklist: number[], finds: { [filter: string]: { [id: string]: number } | null }) : void`
* - Writes the modules cache.
*
* ### Assets cache
*
* - `revenge.caches.assets.read() : { data: { [name: string]: { [type: string]: number } }, version: number } | null`
* - Reads the assets cache. Returns `null` if the cache file does not exist or is invalid.
*
* - `revenge.caches.assets.write(data: { [name: string]: { [type: string]: number } }) : void`
* - Writes the assets cache.
*
* The caches are versioned and tied to the app version code. When the app is updated,
* the caches are invalidated and new cache files are created.
*/
class CacheModule : Module() {
private val CACHE_DIR = "revenge"
private companion object {
const val CACHE_DIR = "revenge"
private val MODULES_CACHE_PREFIX = "modules"
private val ASSETS_CACHE_PREFIX = "assets"
const val MODULES_CACHE_PREFIX = "modules"
const val ASSETS_CACHE_PREFIX = "assets"
}
private lateinit var modulesCache: ModulesCache
private lateinit var assetsCache: AssetsCache
override fun onActivity(activity: Activity) = with(activity) {
override fun onContext(context: Context) = with(context) {
val revengeCacheDir = File(cacheDir, CACHE_DIR).apply { asDir() }
val (_, _, _, versionCode) = getAppInfo()
val modulesCacheFile =
File(
revengeCacheDir,
"$MODULES_CACHE_PREFIX.$versionCode"
).apply { asFile() }
val assetsCacheFile =
File(
revengeCacheDir,
"$ASSETS_CACHE_PREFIX.$versionCode"
).apply { asFile() }
val modulesCacheFile = File(
revengeCacheDir, "$MODULES_CACHE_PREFIX.$versionCode"
).apply { asFile() }
val assetsCacheFile = File(
revengeCacheDir, "$ASSETS_CACHE_PREFIX.$versionCode"
).apply { asFile() }
BridgeModule.registerMethod("revenge.caches.modules.read") {
if (::modulesCache.isInitialized) modulesCache.toMap() else null
if (::modulesCache.isInitialized) modulesCache.toMap() else ModulesCache.loadFromFileOrNull(modulesCacheFile)
?.let {
modulesCache = it
it.toMap()
}
}
BridgeModule.registerMethod("revenge.caches.modules.write") {
val (blacklist, finds) = it
@Suppress("UNCHECKED_CAST")
modulesCache =
ModulesCache(
blacklist as ArrayList<Double>,
finds as HashMap<String, HashMap<String, Double>?>
)
.apply { saveToFile(modulesCacheFile) }
Log.i("Modules cache saved: ${modulesCacheFile.absolutePath}")
modulesCache = ModulesCache(
blacklist as ArrayList<Double>, finds as HashMap<String, HashMap<String, Double>?>
).apply { saveToFile(modulesCacheFile) }
Log.i("Modules cache saved: ${modulesCacheFile.absolutePath} (blacklisted: ${blacklist.size}, finds: ${finds.size})")
}
BridgeModule.registerMethod("revenge.caches.assets.read") {
if (::assetsCache.isInitialized) assetsCache.toMap() else null
if (::assetsCache.isInitialized) assetsCache.toMap() else AssetsCache.loadFromFileOrNull(assetsCacheFile)
?.let {
assetsCache = it
it.toMap()
}
}
BridgeModule.registerMethod("revenge.caches.assets.write") {
val (data) = it
@Suppress("UNCHECKED_CAST")
assetsCache =
AssetsCache(data as HashMap<String, HashMap<String, Double>>)
.apply { saveToFile(assetsCacheFile) }
Log.i("Assets cache saved: ${assetsCacheFile.absolutePath}")
AssetsCache(data as HashMap<String, HashMap<String, Double>>).apply { saveToFile(assetsCacheFile) }
Log.i("Assets cache saved: ${assetsCacheFile.absolutePath} (count: ${data.size})")
}
return@with
}
}
@ -70,8 +95,7 @@ private class CacheVersionMismatchException(expected: Int, actual: Int) :
Throwable("Expected cache version: $expected, but got version: $actual")
data class ModulesCache(
val blacklist: List<Double>,
val finds: Map<String, Map<String, Double>?>
val blacklist: List<Double>, val finds: Map<String, Map<String, Double>?>
) {
fun saveToFile(file: File) {
val atomic = AtomicFile(file)
@ -89,7 +113,7 @@ data class ModulesCache(
// Write finds
out.writeInt(finds.size)
for ((filter, matches) in finds) {
out.writeUTF(filter) // replaces writeUTF
out.writeUTF(filter)
if (matches == null) {
out.writeBoolean(false) // null marker
} else {
@ -111,8 +135,7 @@ data class ModulesCache(
}
fun toMap(): Map<String, Any> {
val findsMap =
finds.mapValues { (_, matches) -> matches?.map { (k, v) -> k to v }?.toMap() }
val findsMap = finds.mapValues { (_, matches) -> matches?.map { (k, v) -> k to v }?.toMap() }
return mapOf("blacklist" to blacklist, "finds" to findsMap, "version" to VERSION)
}
@ -126,8 +149,7 @@ data class ModulesCache(
try {
DataInputStream(BufferedInputStream(atomic.openRead())).use { input ->
val version = input.readInt()
if (version != VERSION)
throw CacheVersionMismatchException(VERSION, version)
if (version != VERSION) throw CacheVersionMismatchException(VERSION, version)
val blacklistSize = input.readInt()
val blacklist = MutableList(blacklistSize) { input.readInt().toDouble() }
@ -200,8 +222,7 @@ data class AssetsCache(
return mapOf(
"data" to data.mapValues { (_, mappings) ->
mappings.map { (type, id) -> type to id }.toMap()
},
"version" to VERSION
}, "version" to VERSION
)
}
@ -214,8 +235,7 @@ data class AssetsCache(
try {
DataInputStream(BufferedInputStream(atomic.openRead())).use { input ->
val version = input.readInt()
if (version != VERSION)
throw CacheVersionMismatchException(VERSION, version)
if (version != VERSION) throw CacheVersionMismatchException(VERSION, version)
val dataSize = input.readInt()
val data = mutableMapOf<String, Map<String, Double>>()

View file

@ -1,24 +1,36 @@
package io.github.revenge.xposed.modules
import android.app.Activity
import android.content.Context
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.*
/**
* Manages the states of plugins, allowing reading and writing of plugin states to a file.
*
* ## Methods
*
* - `revenge.plugins.states.read(): { flags: { [pluginId: string]: number } }`
* - Reads the current plugin states from the file and returns them as a map.
*
* - `revenge.plugins.states.write(flags: { [pluginId: string]: number }): void`
* - Writes the provided plugin states to the file.
*/
class PluginsModule : Module() {
private val DATA_DIR = "revenge/plugins"
private val STATES_FILE = "states"
private companion object {
const val DATA_DIR = "revenge/plugins"
const val STATES_FILE = "states"
}
private lateinit var states: PluginStates
override fun onActivity(activity: Activity) = with(activity) {
override fun onContext(context: Context) = with(context) {
val dataDir = File(filesDir, DATA_DIR)
val statesFile = File(
dataDir,
STATES_FILE
dataDir, STATES_FILE
).apply { asFile() }
BridgeModule.registerMethod("revenge.plugins.states.read") {
@ -32,15 +44,11 @@ class PluginsModule : Module() {
BridgeModule.registerMethod("revenge.plugins.states.write") {
val (flags) = it
@Suppress("UNCHECKED_CAST")
states =
PluginStates(
flags as Map<String, Double>
)
.apply { saveToFile(statesFile) }
states = PluginStates(
flags as Map<String, Double>
).apply { saveToFile(statesFile) }
Log.i("Plugin states saved: ${statesFile.absolutePath}")
}
return@with
}
}