feat: Add option to disable bundle injection (#2)
This commit is contained in:
commit
3ba04c366c
3 changed files with 118 additions and 14 deletions
|
|
@ -2,6 +2,7 @@ package io.github.revenge.xposed.modules
|
|||
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.XModuleResources
|
||||
import android.content.Context
|
||||
import de.robv.android.xposed.IXposedHookZygoteInit
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
|
|
@ -28,6 +29,9 @@ object HookScriptLoaderModule : Module() {
|
|||
private lateinit var preloadsDir: File
|
||||
private lateinit var mainScript: File
|
||||
|
||||
// Directory to read loader config from when checking disable flag
|
||||
private var moduleFilesDir: File? = null
|
||||
|
||||
private lateinit var modulePath: String
|
||||
private lateinit var resources: XModuleResources
|
||||
|
||||
|
|
@ -37,6 +41,16 @@ object HookScriptLoaderModule : Module() {
|
|||
this@HookScriptLoaderModule.modulePath = startupParam.modulePath
|
||||
}
|
||||
|
||||
override fun onContext(context: android.content.Context) {
|
||||
// Keep a reference to the module's files directory so we can check the loader config
|
||||
try {
|
||||
val dir = File(context.dataDir, Constants.FILES_DIR).apply { asDir() }
|
||||
moduleFilesDir = dir
|
||||
} catch (_: Exception) {
|
||||
// best-effort only
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) {
|
||||
val cacheDir = File(appInfo.dataDir, Constants.CACHE_DIR).apply { asDir() }
|
||||
val filesDir = File(appInfo.dataDir, Constants.FILES_DIR).apply { asDir() }
|
||||
|
|
@ -102,17 +116,32 @@ object HookScriptLoaderModule : Module() {
|
|||
try {
|
||||
preloadsDir.walk().filter { it.isFile }.forEach(runScriptFile)
|
||||
|
||||
if (mainScript.exists()) runScriptFile(mainScript)
|
||||
else {
|
||||
Log.i("Main script does not exist, falling back")
|
||||
// Check whether bundle injection execution is disabled via loader config.
|
||||
// UpdaterModule.isInjectionDisabled reads the in-memory config (loaded in its onLoad),
|
||||
// and falls back to disk if necessary. We call it without a Context because by the time
|
||||
// this runs the UpdaterModule config should already be available.
|
||||
val injectionDisabled = try {
|
||||
io.github.revenge.xposed.modules.UpdaterModule.isInjectionDisabled()
|
||||
} catch (e: Throwable) {
|
||||
Log.e("Failed to check injection disabled flag: ${e.message}")
|
||||
false
|
||||
}
|
||||
|
||||
if (!::resources.isInitialized) resources = XModuleResources.createInstance(modulePath, null)
|
||||
if (injectionDisabled) {
|
||||
Log.i("Bundle injection disabled by loader config - skipping main bundle execution")
|
||||
} else {
|
||||
if (mainScript.exists()) runScriptFile(mainScript)
|
||||
else {
|
||||
Log.i("Main script does not exist, falling back")
|
||||
|
||||
XposedBridge.invokeOriginalMethod(
|
||||
loadScriptFromAssets,
|
||||
thisObject,
|
||||
arrayOf(resources.assets, "assets://revenge.bundle", loadSynchronously)
|
||||
)
|
||||
if (!::resources.isInitialized) resources = XModuleResources.createInstance(modulePath, null)
|
||||
|
||||
XposedBridge.invokeOriginalMethod(
|
||||
loadScriptFromAssets,
|
||||
thisObject,
|
||||
arrayOf(resources.assets, "assets://revenge.bundle", loadSynchronously)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e("Unable to run scripts:", e)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ object LogBoxModule : Module() {
|
|||
|
||||
try {
|
||||
val dcdReactNativeHostClass = classLoader.loadClass("com.discord.bridge.DCDReactNativeHost")
|
||||
val getUseDeveloperSupportMethod = dcdReactNativeHostClass.methods.first { it.name == "getUseDeveloperSupport" }
|
||||
val getUseDeveloperSupportMethod =
|
||||
dcdReactNativeHostClass.methods.first { it.name == "getUseDeveloperSupport" }
|
||||
|
||||
getUseDeveloperSupportMethod.hook {
|
||||
before {
|
||||
|
|
@ -354,6 +355,25 @@ object LogBoxModule : Module() {
|
|||
) { refetchBundle(context) }
|
||||
})
|
||||
|
||||
// toggle bundle injection
|
||||
val bundleInjectionText = if (io.github.revenge.xposed.modules.UpdaterModule.isInjectionDisabled(context)) {
|
||||
"Enable Bundle Injection"
|
||||
} else {
|
||||
"Disable Bundle Injection"
|
||||
}
|
||||
container.addView(createButton(context, bundleInjectionText, colors) {
|
||||
dialog.dismiss()
|
||||
showConfirmAction(
|
||||
context,
|
||||
if (io.github.revenge.xposed.modules.UpdaterModule.isInjectionDisabled(context)) "Enable Bundle Injection" else "Disable Bundle Injection",
|
||||
"This will ${if (io.github.revenge.xposed.modules.UpdaterModule.isInjectionDisabled(context)) "enable" else "disable"} executing the cached/downloaded bundle. The app will reload to apply the change."
|
||||
) {
|
||||
val currentlyDisabled = io.github.revenge.xposed.modules.UpdaterModule.isInjectionDisabled(context)
|
||||
io.github.revenge.xposed.modules.UpdaterModule.setDisableInjection(context, !currentlyDisabled)
|
||||
reloadApp()
|
||||
}
|
||||
})
|
||||
|
||||
// reload App Button
|
||||
container.addView(createButton(context, "Reload App", colors) {
|
||||
dialog.dismiss()
|
||||
|
|
@ -552,7 +572,11 @@ object LogBoxModule : Module() {
|
|||
val thumbCornerRadius = thumbSize / 2f
|
||||
|
||||
val switchThumb = View(context).apply {
|
||||
background = createM3Background(context, if (isEnabled) colors.onPrimary else colors.onSurfaceVariant, thumbCornerRadius)
|
||||
background = createM3Background(
|
||||
context,
|
||||
if (isEnabled) colors.onPrimary else colors.onSurfaceVariant,
|
||||
thumbCornerRadius
|
||||
)
|
||||
val params = FrameLayout.LayoutParams(thumbSize, thumbSize)
|
||||
|
||||
val thumbLeft = if (isEnabled) {
|
||||
|
|
@ -848,11 +872,15 @@ object LogBoxModule : Module() {
|
|||
private fun constrainContainerWidth(context: Context, container: LinearLayout) {
|
||||
try {
|
||||
val maxW = (context.resources.displayMetrics.widthPixels * 0.88).toInt()
|
||||
val lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
container.layoutParams = lp
|
||||
try {
|
||||
container.minimumWidth = maxW
|
||||
} catch (_: Exception) {}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ data class CustomLoadUrl(
|
|||
|
||||
@Serializable
|
||||
data class LoaderConfig(
|
||||
val customLoadUrl: CustomLoadUrl = CustomLoadUrl()
|
||||
val customLoadUrl: CustomLoadUrl = CustomLoadUrl(),
|
||||
val disableInjection: Boolean = false
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -152,4 +153,50 @@ object UpdaterModule : Module() {
|
|||
val config = File(filesDir, CONFIG_FILE)
|
||||
if (config.exists()) config.delete()
|
||||
}
|
||||
|
||||
fun setDisableInjection(context: Context, disabled: Boolean) {
|
||||
try {
|
||||
val filesDir = File(context.dataDir, Constants.FILES_DIR)
|
||||
filesDir.mkdirs()
|
||||
val configFile = File(filesDir, CONFIG_FILE)
|
||||
|
||||
val current = runCatching {
|
||||
if (configFile.exists()) JSON.decodeFromString<LoaderConfig>(configFile.readText()) else LoaderConfig()
|
||||
}.getOrDefault(LoaderConfig())
|
||||
|
||||
val newCfg = current.copy(disableInjection = disabled)
|
||||
configFile.writeText(JSON.encodeToString(newCfg))
|
||||
Toast.makeText(context, "Bundle injection ${if (disabled) "disabled" else "enabled"}", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} catch (e: Exception) {
|
||||
Log.e("Failed to update loader config: ${e.message}", e)
|
||||
Toast.makeText(context, "Failed to update loader config", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to check whether injection is disabled (reads current in-memory config first,
|
||||
* falls back to disk if needed).
|
||||
*/
|
||||
fun isInjectionDisabled(context: Context? = null): Boolean {
|
||||
// Prefer in-memory config if loaded
|
||||
try {
|
||||
if (::config.isInitialized) {
|
||||
return config.disableInjection
|
||||
}
|
||||
|
||||
// If a Context is provided, attempt to read on-disk config
|
||||
if (context != null) {
|
||||
val filesDir = File(context.dataDir, Constants.FILES_DIR)
|
||||
val configFile = File(filesDir, CONFIG_FILE)
|
||||
if (configFile.exists()) {
|
||||
val disk = JSON.decodeFromString<LoaderConfig>(configFile.readText())
|
||||
return disk.disableInjection
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("Error checking disableInjection flag: ${e.message}", e)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue