mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 13:47:47 +02:00
feat(scripting): config interface
- change onLaunched to onDispose - refactor JSModule extras
This commit is contained in:
@ -14,6 +14,8 @@ import coil.ImageLoader
|
|||||||
import coil.decode.VideoFrameDecoder
|
import coil.decode.VideoFrameDecoder
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
import coil.memory.MemoryCache
|
import coil.memory.MemoryCache
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import me.rhunk.snapenhance.bridge.BridgeService
|
import me.rhunk.snapenhance.bridge.BridgeService
|
||||||
@ -81,6 +83,8 @@ class RemoteSideContext(
|
|||||||
.components { add(VideoFrameDecoder.Factory()) }.build()
|
.components { add(VideoFrameDecoder.Factory()) }.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val gson: Gson by lazy { GsonBuilder().setPrettyPrinting().create() }
|
||||||
|
|
||||||
fun reload() {
|
fun reload() {
|
||||||
log.verbose("Loading RemoteSideContext")
|
log.verbose("Loading RemoteSideContext")
|
||||||
runCatching {
|
runCatching {
|
||||||
|
@ -6,18 +6,20 @@ import me.rhunk.snapenhance.RemoteSideContext
|
|||||||
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||||
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
||||||
import me.rhunk.snapenhance.common.scripting.ScriptRuntime
|
import me.rhunk.snapenhance.common.scripting.ScriptRuntime
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.ConfigInterface
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.ConfigTransactionType
|
||||||
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
import me.rhunk.snapenhance.scripting.impl.IPCListeners
|
import me.rhunk.snapenhance.scripting.impl.IPCListeners
|
||||||
import me.rhunk.snapenhance.scripting.impl.RemoteManagerIPC
|
import me.rhunk.snapenhance.scripting.impl.RemoteManagerIPC
|
||||||
import me.rhunk.snapenhance.scripting.impl.ui.InterfaceBuilder
|
import me.rhunk.snapenhance.scripting.impl.RemoteScriptConfig
|
||||||
import me.rhunk.snapenhance.scripting.impl.ui.InterfaceManager
|
import me.rhunk.snapenhance.scripting.impl.ui.InterfaceManager
|
||||||
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
class RemoteScriptManager(
|
class RemoteScriptManager(
|
||||||
private val context: RemoteSideContext,
|
val context: RemoteSideContext,
|
||||||
) : IScripting.Stub() {
|
) : IScripting.Stub() {
|
||||||
val runtime = ScriptRuntime(context.androidContext, context.log)
|
val runtime = ScriptRuntime(context.androidContext, context.log)
|
||||||
private val userInterfaces = mutableMapOf<String, MutableMap<String, InterfaceBuilder>>()
|
|
||||||
|
|
||||||
private val cachedModuleInfo = mutableMapOf<String, ModuleInfo>()
|
private val cachedModuleInfo = mutableMapOf<String, ModuleInfo>()
|
||||||
private val ipcListeners = IPCListeners()
|
private val ipcListeners = IPCListeners()
|
||||||
@ -40,19 +42,15 @@ class RemoteScriptManager(
|
|||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
runtime.buildModuleObject = { module ->
|
runtime.buildModuleObject = { module ->
|
||||||
putConst("ipc", this, RemoteManagerIPC(module.moduleInfo, context.log, ipcListeners))
|
module.extras["ipc"] = RemoteManagerIPC(module.moduleInfo, context.log, ipcListeners)
|
||||||
putConst("im", this, InterfaceManager(module.moduleInfo, context.log) { name, interfaceBuilder ->
|
module.extras["im"] = InterfaceManager(module.moduleInfo, context.log)
|
||||||
userInterfaces.getOrPut(module.moduleInfo.name) {
|
module.extras["config"] = RemoteScriptConfig(this@RemoteScriptManager, module.moduleInfo, context.log).also {
|
||||||
mutableMapOf()
|
it.load()
|
||||||
}[name] = interfaceBuilder
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sync()
|
sync()
|
||||||
enabledScripts.forEach { name ->
|
enabledScripts.forEach { name ->
|
||||||
if (getModuleDataFolder(name) == null) {
|
|
||||||
context.log.warn("Module data folder not found for $name")
|
|
||||||
}
|
|
||||||
loadScript(name)
|
loadScript(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,19 +60,17 @@ class RemoteScriptManager(
|
|||||||
runtime.load(name, content)
|
runtime.load(name, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getScriptInterface(scriptName: String, interfaceName: String)
|
|
||||||
= userInterfaces[scriptName]?.get(interfaceName)
|
|
||||||
|
|
||||||
|
|
||||||
private fun <R> getScriptInputStream(name: String, callback: (InputStream?) -> R): R {
|
private fun <R> getScriptInputStream(name: String, callback: (InputStream?) -> R): R {
|
||||||
val file = getScriptsFolder()?.findFile(name) ?: return callback(null)
|
val file = getScriptsFolder()?.findFile(name) ?: return callback(null)
|
||||||
return context.androidContext.contentResolver.openInputStream(file.uri)?.use(callback) ?: callback(null)
|
return context.androidContext.contentResolver.openInputStream(file.uri)?.use(callback) ?: callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getModuleDataFolder(moduleFileName: String): DocumentFile? {
|
fun getModuleDataFolder(moduleFileName: String): File {
|
||||||
val folderName = moduleFileName.substringBeforeLast(".js")
|
return context.androidContext.filesDir.resolve("modules").resolve(moduleFileName).also {
|
||||||
val folder = getScriptsFolder() ?: return null
|
if (!it.exists()) {
|
||||||
return folder.findFile(folderName) ?: folder.createDirectory(folderName)
|
it.mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getScriptsFolder() = runCatching {
|
private fun getScriptsFolder() = runCatching {
|
||||||
@ -114,4 +110,35 @@ class RemoteScriptManager(
|
|||||||
context.log.error("Failed to send message for $eventName", it)
|
context.log.error("Failed to send message for $eventName", it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun configTransaction(
|
||||||
|
module: String?,
|
||||||
|
action: String,
|
||||||
|
key: String?,
|
||||||
|
value: String?,
|
||||||
|
save: Boolean
|
||||||
|
): String? {
|
||||||
|
val scriptConfig = runtime.getModuleByName(module ?: return null)?.extras?.get("config") as? ConfigInterface ?: return null.also {
|
||||||
|
context.log.warn("Failed to get config interface for $module")
|
||||||
|
}
|
||||||
|
val transactionType = ConfigTransactionType.fromKey(action)
|
||||||
|
|
||||||
|
return runCatching {
|
||||||
|
scriptConfig.run {
|
||||||
|
if (transactionType == ConfigTransactionType.GET) {
|
||||||
|
return get(key ?: return@runCatching null, value)
|
||||||
|
}
|
||||||
|
when (transactionType) {
|
||||||
|
ConfigTransactionType.SET -> set(key ?: return@runCatching null, value, save)
|
||||||
|
ConfigTransactionType.SAVE -> save()
|
||||||
|
ConfigTransactionType.LOAD -> load()
|
||||||
|
ConfigTransactionType.DELETE -> delete()
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.onFailure {
|
||||||
|
context.log.error("Failed to perform config transaction", it)
|
||||||
|
}.getOrDefault("")
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,8 +3,8 @@ package me.rhunk.snapenhance.scripting.impl
|
|||||||
import android.os.DeadObjectException
|
import android.os.DeadObjectException
|
||||||
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||||
import me.rhunk.snapenhance.common.scripting.IPCInterface
|
import me.rhunk.snapenhance.common.scripting.impl.IPCInterface
|
||||||
import me.rhunk.snapenhance.common.scripting.Listener
|
import me.rhunk.snapenhance.common.scripting.impl.Listener
|
||||||
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package me.rhunk.snapenhance.scripting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.ConfigInterface
|
||||||
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
|
import me.rhunk.snapenhance.scripting.RemoteScriptManager
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class RemoteScriptConfig(
|
||||||
|
private val remoteScriptManager: RemoteScriptManager,
|
||||||
|
moduleInfo: ModuleInfo,
|
||||||
|
private val logger: AbstractLogger,
|
||||||
|
) : ConfigInterface() {
|
||||||
|
private val configFile = File(remoteScriptManager.getModuleDataFolder(moduleInfo.name), "config.json")
|
||||||
|
private var config = JsonObject()
|
||||||
|
|
||||||
|
override fun get(key: String, defaultValue: Any?): String? {
|
||||||
|
return config[key]?.asString ?: defaultValue?.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(key: String, value: Any?, save: Boolean) {
|
||||||
|
when (value) {
|
||||||
|
is Int -> config.addProperty(key, value)
|
||||||
|
is Double -> config.addProperty(key, value)
|
||||||
|
is Boolean -> config.addProperty(key, value)
|
||||||
|
is Long -> config.addProperty(key, value)
|
||||||
|
is Float -> config.addProperty(key, value)
|
||||||
|
is Byte -> config.addProperty(key, value)
|
||||||
|
is Short -> config.addProperty(key, value)
|
||||||
|
else -> config.addProperty(key, value?.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save) save()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
configFile.writeText(config.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load() {
|
||||||
|
runCatching {
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
save()
|
||||||
|
return@runCatching
|
||||||
|
}
|
||||||
|
config = remoteScriptManager.context.gson.fromJson(configFile.readText(), JsonObject::class.java)
|
||||||
|
}.onFailure {
|
||||||
|
logger.error("Failed to load config file", it)
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
configFile.delete()
|
||||||
|
}
|
||||||
|
}
|
@ -12,15 +12,15 @@ import org.mozilla.javascript.annotations.JSFunction
|
|||||||
|
|
||||||
class InterfaceBuilder {
|
class InterfaceBuilder {
|
||||||
val nodes = mutableListOf<Node>()
|
val nodes = mutableListOf<Node>()
|
||||||
var onLaunchedCallback: (() -> Unit)? = null
|
var onDisposeCallback: (() -> Unit)? = null
|
||||||
|
|
||||||
|
|
||||||
private fun createNode(type: NodeType, block: Node.() -> Unit): Node {
|
private fun createNode(type: NodeType, block: Node.() -> Unit): Node {
|
||||||
return Node(type).apply(block).also { nodes.add(it) }
|
return Node(type).apply(block).also { nodes.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onLaunched(block: () -> Unit) {
|
fun onDispose(block: () -> Unit) {
|
||||||
onLaunchedCallback = block
|
onDisposeCallback = block
|
||||||
}
|
}
|
||||||
|
|
||||||
fun row(block: (InterfaceBuilder) -> Unit) = RowColumnNode(NodeType.ROW).apply {
|
fun row(block: (InterfaceBuilder) -> Unit) = RowColumnNode(NodeType.ROW).apply {
|
||||||
@ -66,14 +66,25 @@ class InterfaceBuilder {
|
|||||||
|
|
||||||
class InterfaceManager(
|
class InterfaceManager(
|
||||||
private val moduleInfo: ModuleInfo,
|
private val moduleInfo: ModuleInfo,
|
||||||
private val logger: AbstractLogger,
|
private val logger: AbstractLogger
|
||||||
private val registerInterface: (String, InterfaceBuilder) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
@JSFunction
|
private val interfaces = mutableMapOf<String, () -> InterfaceBuilder?>()
|
||||||
fun create(name: String, callback: Function) {
|
|
||||||
logger.info("Creating interface $name for ${moduleInfo.name}")
|
fun buildInterface(name: String): InterfaceBuilder? {
|
||||||
val interfaceBuilder = InterfaceBuilder()
|
return interfaces[name]?.invoke()
|
||||||
callback.call(Context.getCurrentContext(), callback, callback, arrayOf(interfaceBuilder))
|
}
|
||||||
registerInterface(name, interfaceBuilder)
|
|
||||||
|
@JSFunction fun create(name: String, callback: Function) {
|
||||||
|
interfaces[name] = {
|
||||||
|
val interfaceBuilder = InterfaceBuilder()
|
||||||
|
runCatching {
|
||||||
|
Context.enter()
|
||||||
|
callback.call(Context.getCurrentContext(), callback, callback, arrayOf(interfaceBuilder))
|
||||||
|
Context.exit()
|
||||||
|
interfaceBuilder
|
||||||
|
}.onFailure {
|
||||||
|
logger.error("Failed to create interface $name for ${moduleInfo.name}", it)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -147,8 +147,14 @@ fun ScriptInterface(interfaceBuilder: InterfaceBuilder) {
|
|||||||
DrawNode(node)
|
DrawNode(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(interfaceBuilder) {
|
DisposableEffect(Unit) {
|
||||||
interfaceBuilder.onLaunchedCallback?.invoke()
|
onDispose {
|
||||||
|
runCatching {
|
||||||
|
interfaceBuilder.onDisposeCallback?.invoke()
|
||||||
|
}.onFailure {
|
||||||
|
AbstractLogger.directError("Error running onDisposed callback", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
|
import me.rhunk.snapenhance.scripting.impl.ui.InterfaceManager
|
||||||
import me.rhunk.snapenhance.ui.manager.Section
|
import me.rhunk.snapenhance.ui.manager.Section
|
||||||
import me.rhunk.snapenhance.ui.util.pullrefresh.PullRefreshIndicator
|
import me.rhunk.snapenhance.ui.util.pullrefresh.PullRefreshIndicator
|
||||||
import me.rhunk.snapenhance.ui.util.pullrefresh.pullRefresh
|
import me.rhunk.snapenhance.ui.util.pullrefresh.pullRefresh
|
||||||
@ -88,7 +89,8 @@ class ScriptsSection : Section() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun ScriptSettings(script: ModuleInfo) {
|
fun ScriptSettings(script: ModuleInfo) {
|
||||||
val settingsInterface = remember {
|
val settingsInterface = remember {
|
||||||
context.scriptManager.getScriptInterface(script.name, "settings")
|
val module = context.scriptManager.runtime.getModuleByName(script.name) ?: return@remember null
|
||||||
|
(module.extras["im"] as? InterfaceManager)?.buildInterface("settings")
|
||||||
} ?: run {
|
} ?: run {
|
||||||
Text(
|
Text(
|
||||||
text = "This module does not have any settings",
|
text = "This module does not have any settings",
|
||||||
@ -101,7 +103,6 @@ class ScriptsSection : Section() {
|
|||||||
ScriptInterface(interfaceBuilder = settingsInterface)
|
ScriptInterface(interfaceBuilder = settingsInterface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
var scriptModules by remember {
|
var scriptModules by remember {
|
||||||
|
@ -10,4 +10,6 @@ interface IScripting {
|
|||||||
void registerIPCListener(String channel, String eventName, IPCListener listener);
|
void registerIPCListener(String channel, String eventName, IPCListener listener);
|
||||||
|
|
||||||
void sendIPCMessage(String channel, String eventName, in String[] args);
|
void sendIPCMessage(String channel, String eventName, in String[] args);
|
||||||
|
|
||||||
|
@nullable String configTransaction(String module, String action, @nullable String key, @nullable String value, boolean save);
|
||||||
}
|
}
|
@ -18,6 +18,7 @@ class JSModule(
|
|||||||
val moduleInfo: ModuleInfo,
|
val moduleInfo: ModuleInfo,
|
||||||
val content: String,
|
val content: String,
|
||||||
) {
|
) {
|
||||||
|
val extras = mutableMapOf<String, Any>()
|
||||||
private lateinit var moduleObject: ScriptableObject
|
private lateinit var moduleObject: ScriptableObject
|
||||||
|
|
||||||
fun load(block: ScriptableObject.() -> Unit) {
|
fun load(block: ScriptableObject.() -> Unit) {
|
||||||
@ -115,8 +116,10 @@ class JSModule(
|
|||||||
Undefined.instance
|
Undefined.instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block(moduleObject)
|
block(moduleObject)
|
||||||
|
extras.forEach { (key, value) ->
|
||||||
|
moduleObject.putConst(key, moduleObject, value)
|
||||||
|
}
|
||||||
evaluateString(moduleObject, content, moduleInfo.name, 1, null)
|
evaluateString(moduleObject, content, moduleInfo.name, 1, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,10 @@ open class ScriptRuntime(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getModuleByName(name: String): JSModule? {
|
||||||
|
return modules.values.find { it.moduleInfo.name == name }
|
||||||
|
}
|
||||||
|
|
||||||
private fun readModuleInfo(reader: BufferedReader): ModuleInfo {
|
private fun readModuleInfo(reader: BufferedReader): ModuleInfo {
|
||||||
val header = reader.readLine()
|
val header = reader.readLine()
|
||||||
if (!header.startsWith("// ==SE_module==")) {
|
if (!header.startsWith("// ==SE_module==")) {
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package me.rhunk.snapenhance.common.scripting.impl
|
||||||
|
|
||||||
|
import org.mozilla.javascript.annotations.JSFunction
|
||||||
|
|
||||||
|
|
||||||
|
enum class ConfigTransactionType(
|
||||||
|
val key: String
|
||||||
|
) {
|
||||||
|
GET("get"),
|
||||||
|
SET("set"),
|
||||||
|
SAVE("save"),
|
||||||
|
LOAD("load"),
|
||||||
|
DELETE("delete");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromKey(key: String) = entries.find { it.key == key }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ConfigInterface {
|
||||||
|
@JSFunction fun get(key: String): String? = get(key, null)
|
||||||
|
@JSFunction abstract fun get(key: String, defaultValue: Any?): String?
|
||||||
|
|
||||||
|
@JSFunction fun getInteger(key: String): Int? = getInteger(key, null)
|
||||||
|
@JSFunction fun getInteger(key: String, defaultValue: Int?): Int? = get(key, defaultValue.toString())?.toIntOrNull() ?: defaultValue
|
||||||
|
|
||||||
|
@JSFunction fun getDouble(key: String): Double? = getDouble(key, null)
|
||||||
|
@JSFunction fun getDouble(key: String, defaultValue: Double?): Double? = get(key, defaultValue.toString())?.toDoubleOrNull() ?: defaultValue
|
||||||
|
|
||||||
|
@JSFunction fun getBoolean(key: String): Boolean? = getBoolean(key, null)
|
||||||
|
@JSFunction fun getBoolean(key: String, defaultValue: Boolean?): Boolean? = get(key, defaultValue.toString())?.toBoolean() ?: defaultValue
|
||||||
|
|
||||||
|
@JSFunction fun getLong(key: String): Long? = getLong(key, null)
|
||||||
|
@JSFunction fun getLong(key: String, defaultValue: Long?): Long? = get(key, defaultValue.toString())?.toLongOrNull() ?: defaultValue
|
||||||
|
|
||||||
|
@JSFunction fun getFloat(key: String): Float? = getFloat(key, null)
|
||||||
|
@JSFunction fun getFloat(key: String, defaultValue: Float?): Float? = get(key, defaultValue.toString())?.toFloatOrNull() ?: defaultValue
|
||||||
|
|
||||||
|
@JSFunction fun getByte(key: String): Byte? = getByte(key, null)
|
||||||
|
@JSFunction fun getByte(key: String, defaultValue: Byte?): Byte? = get(key, defaultValue.toString())?.toByteOrNull() ?: defaultValue
|
||||||
|
|
||||||
|
@JSFunction fun getShort(key: String): Short? = getShort(key, null)
|
||||||
|
@JSFunction fun getShort(key: String, defaultValue: Short?): Short? = get(key, defaultValue.toString())?.toShortOrNull() ?: defaultValue
|
||||||
|
|
||||||
|
|
||||||
|
@JSFunction fun set(key: String, value: Any?) = set(key, value, false)
|
||||||
|
@JSFunction abstract fun set(key: String, value: Any?, save: Boolean)
|
||||||
|
|
||||||
|
@JSFunction fun setInteger(key: String, value: Int?) = setInteger(key, value, false)
|
||||||
|
@JSFunction fun setInteger(key: String, value: Int?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction fun setDouble(key: String, value: Double?) = setDouble(key, value, false)
|
||||||
|
@JSFunction fun setDouble(key: String, value: Double?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction fun setBoolean(key: String, value: Boolean?) = setBoolean(key, value, false)
|
||||||
|
@JSFunction fun setBoolean(key: String, value: Boolean?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction fun setLong(key: String, value: Long?) = setLong(key, value, false)
|
||||||
|
@JSFunction fun setLong(key: String, value: Long?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction fun setFloat(key: String, value: Float?) = setFloat(key, value, false)
|
||||||
|
@JSFunction fun setFloat(key: String, value: Float?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction fun setByte(key: String, value: Byte?) = setByte(key, value, false)
|
||||||
|
@JSFunction fun setByte(key: String, value: Byte?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction fun setShort(key: String, value: Short?) = setShort(key, value, false)
|
||||||
|
@JSFunction fun setShort(key: String, value: Short?, save: Boolean) = set(key, value, save)
|
||||||
|
|
||||||
|
@JSFunction abstract fun save()
|
||||||
|
@JSFunction abstract fun load()
|
||||||
|
@JSFunction abstract fun delete()
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package me.rhunk.snapenhance.common.scripting
|
package me.rhunk.snapenhance.common.scripting.impl
|
||||||
|
|
||||||
typealias Listener = (Array<out String?>) -> Unit
|
typealias Listener = (Array<out String?>) -> Unit
|
||||||
|
|
@ -1,12 +1,11 @@
|
|||||||
package me.rhunk.snapenhance.core.scripting
|
package me.rhunk.snapenhance.core.scripting
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
|
||||||
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
||||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||||
import me.rhunk.snapenhance.common.scripting.IPCInterface
|
|
||||||
import me.rhunk.snapenhance.common.scripting.Listener
|
|
||||||
import me.rhunk.snapenhance.common.scripting.ScriptRuntime
|
import me.rhunk.snapenhance.common.scripting.ScriptRuntime
|
||||||
|
import me.rhunk.snapenhance.core.scripting.impl.CoreIPC
|
||||||
|
import me.rhunk.snapenhance.core.scripting.impl.CoreScriptConfig
|
||||||
import me.rhunk.snapenhance.core.scripting.impl.ScriptHooker
|
import me.rhunk.snapenhance.core.scripting.impl.ScriptHooker
|
||||||
|
|
||||||
class CoreScriptRuntime(
|
class CoreScriptRuntime(
|
||||||
@ -18,38 +17,19 @@ class CoreScriptRuntime(
|
|||||||
fun connect(scriptingInterface: IScripting) {
|
fun connect(scriptingInterface: IScripting) {
|
||||||
scriptingInterface.apply {
|
scriptingInterface.apply {
|
||||||
buildModuleObject = { module ->
|
buildModuleObject = { module ->
|
||||||
putConst("ipc", this, object: IPCInterface() {
|
module.extras["ipc"] = CoreIPC(this@apply, module.moduleInfo)
|
||||||
override fun onBroadcast(channel: String, eventName: String, listener: Listener) {
|
module.extras["hooker"] = ScriptHooker(module.moduleInfo, logger, androidContext.classLoader).also {
|
||||||
registerIPCListener(channel, eventName, object: IPCListener.Stub() {
|
|
||||||
override fun onMessage(args: Array<out String?>) {
|
|
||||||
listener(args)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun on(eventName: String, listener: Listener) {
|
|
||||||
onBroadcast(module.moduleInfo.name, eventName, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun emit(eventName: String, vararg args: String?) {
|
|
||||||
broadcast(module.moduleInfo.name, eventName, *args)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun broadcast(channel: String, eventName: String, vararg args: String?) {
|
|
||||||
sendIPCMessage(channel, eventName, args)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
putConst("hooker", this, ScriptHooker(module.moduleInfo, logger, androidContext.classLoader).also {
|
|
||||||
scriptHookers.add(it)
|
scriptHookers.add(it)
|
||||||
})
|
}
|
||||||
|
module.extras["config"] = CoreScriptConfig(this@apply, module.moduleInfo)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scriptingInterface.enabledScripts.forEach { path ->
|
enabledScripts.forEach { path ->
|
||||||
runCatching {
|
runCatching {
|
||||||
load(path, scriptingInterface.getScriptContent(path))
|
load(path, scriptingInterface.getScriptContent(path))
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
logger.error("Failed to load script $path", it)
|
logger.error("Failed to load script $path", it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package me.rhunk.snapenhance.core.scripting.impl
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||||
|
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.IPCInterface
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.Listener
|
||||||
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
|
|
||||||
|
class CoreIPC(
|
||||||
|
private val scripting: IScripting,
|
||||||
|
private val moduleInfo: ModuleInfo
|
||||||
|
) : IPCInterface() {
|
||||||
|
override fun onBroadcast(channel: String, eventName: String, listener: Listener) {
|
||||||
|
scripting.registerIPCListener(channel, eventName, object: IPCListener.Stub() {
|
||||||
|
override fun onMessage(args: Array<out String?>) {
|
||||||
|
listener(args)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun on(eventName: String, listener: Listener) {
|
||||||
|
onBroadcast(moduleInfo.name, eventName, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun emit(eventName: String, vararg args: String?) {
|
||||||
|
broadcast(moduleInfo.name, eventName, *args)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun broadcast(channel: String, eventName: String, vararg args: String?) {
|
||||||
|
scripting.sendIPCMessage(channel, eventName, args)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package me.rhunk.snapenhance.core.scripting.impl
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.bridge.scripting.IScripting
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.ConfigInterface
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.ConfigTransactionType
|
||||||
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
|
|
||||||
|
class CoreScriptConfig(
|
||||||
|
private val scripting: IScripting,
|
||||||
|
private val moduleInfo: ModuleInfo
|
||||||
|
): ConfigInterface() {
|
||||||
|
override fun get(key: String, defaultValue: Any?): String? {
|
||||||
|
return scripting.configTransaction(moduleInfo.name, ConfigTransactionType.GET.key, key, defaultValue.toString(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(key: String, value: Any?, save: Boolean) {
|
||||||
|
scripting.configTransaction(moduleInfo.name, ConfigTransactionType.SET.key, key, value.toString(), save)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
scripting.configTransaction(moduleInfo.name, ConfigTransactionType.SAVE.key, null, null, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load() {
|
||||||
|
scripting.configTransaction(moduleInfo.name, ConfigTransactionType.LOAD.key, null, null, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
scripting.configTransaction(moduleInfo.name, ConfigTransactionType.DELETE.key, null, null, false)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user