mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-28 12:30:12 +02:00
feat(scripting): ipc global channels
This commit is contained in:
parent
98b3f2bfc9
commit
19a0ab8398
@ -1,19 +0,0 @@
|
||||
package me.rhunk.snapenhance.scripting
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class IRemoteIPC : IPCInterface() {
|
||||
private val listeners = ConcurrentHashMap<String, MutableSet<Listener>>()
|
||||
|
||||
fun removeListener(eventName: String, listener: Listener) {
|
||||
listeners[eventName]?.remove(listener)
|
||||
}
|
||||
|
||||
override fun on(eventName: String, listener: Listener) {
|
||||
listeners.getOrPut(eventName) { mutableSetOf() }.add(listener)
|
||||
}
|
||||
|
||||
override fun emit(eventName: String, args: Array<out String?>) {
|
||||
listeners[eventName]?.toList()?.forEach { it(args) }
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package me.rhunk.snapenhance.scripting
|
||||
|
||||
import android.os.DeadObjectException
|
||||
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||
import me.rhunk.snapenhance.core.logger.AbstractLogger
|
||||
import me.rhunk.snapenhance.scripting.type.ModuleInfo
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
typealias IPCListeners = ConcurrentHashMap<String, MutableMap<String, MutableSet<IPCListener>>> // channel, eventName -> listeners
|
||||
|
||||
class RemoteManagerIPC(
|
||||
private val moduleInfo: ModuleInfo,
|
||||
private val logger: AbstractLogger,
|
||||
private val ipcListeners: IPCListeners = ConcurrentHashMap(),
|
||||
) : IPCInterface() {
|
||||
companion object {
|
||||
private const val TAG = "RemoteManagerIPC"
|
||||
}
|
||||
|
||||
override fun on(eventName: String, listener: Listener) {
|
||||
onBroadcast(moduleInfo.name, eventName, listener)
|
||||
}
|
||||
|
||||
override fun emit(eventName: String, vararg args: String?) {
|
||||
emit(moduleInfo.name, eventName, *args)
|
||||
}
|
||||
|
||||
override fun onBroadcast(channel: String, eventName: String, listener: Listener) {
|
||||
ipcListeners.getOrPut(channel) { mutableMapOf() }.getOrPut(eventName) { mutableSetOf() }.add(object: IPCListener.Stub() {
|
||||
override fun onMessage(args: Array<out String?>) {
|
||||
try {
|
||||
listener(args)
|
||||
} catch (doe: DeadObjectException) {
|
||||
ipcListeners[channel]?.get(eventName)?.remove(this)
|
||||
} catch (t: Throwable) {
|
||||
logger.error("Failed to receive message for channel: $channel, event: $eventName", t, TAG)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun broadcast(channel: String, eventName: String, vararg args: String?) {
|
||||
ipcListeners[channel]?.get(eventName)?.toList()?.forEach {
|
||||
try {
|
||||
it.onMessage(args)
|
||||
} catch (doe: DeadObjectException) {
|
||||
ipcListeners[channel]?.get(eventName)?.remove(it)
|
||||
} catch (t: Throwable) {
|
||||
logger.error("Failed to send message for channel: $channel, event: $eventName", t, TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package me.rhunk.snapenhance.scripting
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.DeadObjectException
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import me.rhunk.snapenhance.RemoteSideContext
|
||||
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||
@ -14,14 +13,10 @@ class RemoteScriptManager(
|
||||
private val context: RemoteSideContext,
|
||||
) : IScripting.Stub() {
|
||||
val runtime = ScriptRuntime(context.log)
|
||||
val remoteIpc = IRemoteIPC()
|
||||
|
||||
private fun getScriptFolder()
|
||||
= DocumentFile.fromTreeUri(context.androidContext, Uri.parse(context.config.root.scripting.moduleFolder.get()))
|
||||
//private fun hasHotReload() = context.config.root.scripting.hotReload.get()
|
||||
private val reloadListeners = mutableListOf<ReloadListener>()
|
||||
|
||||
private val cachedModuleInfo = mutableMapOf<String, ModuleInfo>()
|
||||
private val ipcListeners = IPCListeners()
|
||||
|
||||
fun sync() {
|
||||
getScriptFileNames().forEach { name ->
|
||||
@ -40,8 +35,8 @@ class RemoteScriptManager(
|
||||
}
|
||||
|
||||
fun init() {
|
||||
runtime.buildModuleObject = {
|
||||
putConst("ipc", this, remoteIpc)
|
||||
runtime.buildModuleObject = { module ->
|
||||
putConst("ipc", this, RemoteManagerIPC(module.moduleInfo, context.log, ipcListeners))
|
||||
}
|
||||
|
||||
sync()
|
||||
@ -56,6 +51,8 @@ class RemoteScriptManager(
|
||||
return context.androidContext.contentResolver.openInputStream(file.uri)?.use(callback) ?: callback(null)
|
||||
}
|
||||
|
||||
private fun getScriptFolder() = DocumentFile.fromTreeUri(context.androidContext, Uri.parse(context.config.root.scripting.moduleFolder.get()))
|
||||
|
||||
private fun getScriptFileNames(): List<String> {
|
||||
return (getScriptFolder() ?: return emptyList()).listFiles().filter { it.name?.endsWith(".js") ?: false }.map { it.name!! }
|
||||
}
|
||||
@ -78,23 +75,15 @@ class RemoteScriptManager(
|
||||
reloadListeners.add(listener)
|
||||
}
|
||||
|
||||
override fun registerIPCListener(eventName: String, listener: IPCListener) {
|
||||
remoteIpc.on(eventName, object: Listener {
|
||||
override fun invoke(args: Array<out String?>) {
|
||||
try {
|
||||
listener.onMessage(args)
|
||||
} catch (e: DeadObjectException) {
|
||||
remoteIpc.removeListener(eventName, this)
|
||||
} catch (t: Throwable) {
|
||||
context.log.error("Failed to invoke $eventName", t)
|
||||
}
|
||||
}
|
||||
})
|
||||
override fun registerIPCListener(channel: String, eventName: String, listener: IPCListener) {
|
||||
ipcListeners.getOrPut(channel) { mutableMapOf() }.getOrPut(eventName) { mutableSetOf() }.add(listener)
|
||||
}
|
||||
|
||||
override fun sendIPCMessage(eventName: String, args: Array<out String>) {
|
||||
override fun sendIPCMessage(channel: String, eventName: String, args: Array<out String>) {
|
||||
runCatching {
|
||||
remoteIpc.emit(eventName, args)
|
||||
ipcListeners[channel]?.get(eventName)?.toList()?.forEach {
|
||||
it.onMessage(args)
|
||||
}
|
||||
}.onFailure {
|
||||
context.log.error("Failed to send message for $eventName", it)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ interface IScripting {
|
||||
|
||||
void registerReloadListener(ReloadListener listener);
|
||||
|
||||
void registerIPCListener(String eventName, IPCListener listener);
|
||||
void registerIPCListener(String channel, String eventName, IPCListener listener);
|
||||
|
||||
void sendIPCMessage(String eventName, in String[] args);
|
||||
void sendIPCMessage(String channel, String eventName, in String[] args);
|
||||
}
|
@ -5,8 +5,13 @@ typealias Listener = (Array<out String?>) -> Unit
|
||||
abstract class IPCInterface {
|
||||
abstract fun on(eventName: String, listener: Listener)
|
||||
|
||||
abstract fun onBroadcast(channel: String, eventName: String, listener: Listener)
|
||||
|
||||
abstract fun emit(eventName: String, vararg args: String?)
|
||||
abstract fun broadcast(channel: String, eventName: String, vararg args: String?)
|
||||
|
||||
@Suppress("unused")
|
||||
fun emit(eventName: String) = emit(eventName, *emptyArray())
|
||||
@Suppress("unused")
|
||||
fun emit(channel: String, eventName: String) = broadcast(channel, eventName, *emptyArray())
|
||||
}
|
@ -18,7 +18,6 @@ class CoreScriptRuntime(
|
||||
|
||||
private val scriptHookers = mutableListOf<ScriptHooker>()
|
||||
|
||||
|
||||
fun connect(scriptingInterface: IScripting) {
|
||||
scriptingInterface.apply {
|
||||
registerReloadListener(object: ReloadListener.Stub() {
|
||||
@ -27,31 +26,39 @@ class CoreScriptRuntime(
|
||||
}
|
||||
})
|
||||
|
||||
ipcInterface = object: IPCInterface() {
|
||||
override fun on(eventName: String, listener: Listener) {
|
||||
registerIPCListener(eventName, object: IPCListener.Stub() {
|
||||
override fun onMessage(args: Array<out String?>) {
|
||||
listener(args)
|
||||
}
|
||||
})
|
||||
}
|
||||
buildModuleObject = { module ->
|
||||
putConst("ipc", this, object: IPCInterface() {
|
||||
override fun onBroadcast(channel: String, eventName: String, listener: Listener) {
|
||||
registerIPCListener(channel, eventName, object: IPCListener.Stub() {
|
||||
override fun onMessage(args: Array<out String?>) {
|
||||
listener(args)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun emit(eventName: String, args: Array<out String?>) {
|
||||
sendIPCMessage(eventName, 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)
|
||||
}
|
||||
})
|
||||
putFunction("findClass") {
|
||||
val className = it?.get(0).toString()
|
||||
classLoader.loadClass(className)
|
||||
}
|
||||
putConst("hooker", this, ScriptHooker(module.moduleInfo, logger, classLoader).also {
|
||||
scriptHookers.add(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
buildModuleObject = { module ->
|
||||
putConst("ipc", this, ipcInterface)
|
||||
putFunction("findClass") {
|
||||
val className = it?.get(0).toString()
|
||||
classLoader.loadClass(className)
|
||||
}
|
||||
putConst("hooker", this, ScriptHooker(module.moduleInfo, logger, classLoader).also {
|
||||
scriptHookers.add(it)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
scriptingInterface.enabledScripts.forEach { path ->
|
||||
runCatching {
|
||||
|
Loading…
x
Reference in New Issue
Block a user