From 9f1941e9743835bf9f17a9111f67260ed9a64df0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 18 Nov 2025 13:02:47 +0000 Subject: [PATCH 01/12] chore: bump version to 1.4.6 (1406) --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 20777b0..62b89fe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "io.github.revenge.xposed" minSdk = 24 targetSdk = 36 - versionCode = 1405 - versionName = "1.4.5" + versionCode = 1406 + versionName = "1.4.6" } buildTypes { From 06d6778c4596ce472bfbd5bce633c05e0a0c6dbb Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Fri, 21 Nov 2025 13:01:55 +0700 Subject: [PATCH 02/12] fix(modules/no_track/BlockCrashReportingModule): also hook isDisabled to true Prevents Discord from crashing when trying to set a Sentry tag. --- .../no_track/BlockCrashReportingModule.kt | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt index b6468fd..529b7ca 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt @@ -12,12 +12,23 @@ import io.github.revenge.xposed.Utils.Log object BlockCrashReportingModule : Module() { override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) { val crashReportingClass = classLoader.safeLoadClass("com.discord.crash_reporting.CrashReporting") - crashReportingClass?.hookMethod( - "init", Context::class.java, String::class.java - ) { - before { - Log.i("Blocked CrashReporting initialization") - result = null + crashReportingClass?.apply { + // Hooking this will result in a crash after a few seconds, since Discord expects initialization to complete when setting a Sentry tag. + // So we also hook isDisabled to make sure those calls are no-ops. + hookMethod( + "init", Context::class.java, String::class.java + ) { + before { + Log.i("Blocked CrashReporting initialization") + result = null + } + } + + hookMethod("isDisabled") { + before { + Log.i("Forced CrashReporting.isDisabled() to true") + result = true + } } } From f49ea13da528f27748f22028bc61fa5d45aaa3f1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 21 Nov 2025 06:17:17 +0000 Subject: [PATCH 03/12] chore: bump version to 1.4.7 (1407) --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 62b89fe..c69992d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "io.github.revenge.xposed" minSdk = 24 targetSdk = 36 - versionCode = 1406 - versionName = "1.4.6" + versionCode = 1407 + versionName = "1.4.7" } buildTypes { From 4a8df8954e433269198b3af7688822a779554664 Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Fri, 21 Nov 2025 16:36:14 +0700 Subject: [PATCH 04/12] fix(modules/no_track/BlockCrashReportingModule): make backwards compatible --- .../modules/no_track/BlockCrashReportingModule.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt index 529b7ca..986662b 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt @@ -24,10 +24,13 @@ object BlockCrashReportingModule : Module() { } } - hookMethod("isDisabled") { - before { - Log.i("Forced CrashReporting.isDisabled() to true") - result = true + // This only exists on 30720x and above + runCatching { + hookMethod("isDisabled") { + before { + Log.i("Forced CrashReporting.isDisabled() to true") + result = true + } } } } From c20cf01f6bb9612e3aede1c5544f90d4c4a43ba1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 21 Nov 2025 09:44:30 +0000 Subject: [PATCH 05/12] chore: bump version to 1.4.8 (1408) --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c69992d..f95c4b2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "io.github.revenge.xposed" minSdk = 24 targetSdk = 36 - versionCode = 1407 - versionName = "1.4.7" + versionCode = 1408 + versionName = "1.4.8" } buildTypes { From 38c708276041da7d9e56101e9795ad48b16370eb Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Sat, 22 Nov 2025 14:49:17 +0700 Subject: [PATCH 06/12] fix(modules/no_track/BlockCrashReportingModule): support 307204+ Discord pushed an update that changed some logic for the setTag method. Previously it only asserted initialization if isDisabled returns false, now it asserts initialization regardless. The init method sets the initialization latch and returns if crash reporting is disabled, so we don't need to hook that anymore. --- .../no_track/BlockCrashReportingModule.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt index 986662b..c387a91 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/no_track/BlockCrashReportingModule.kt @@ -13,16 +13,6 @@ object BlockCrashReportingModule : Module() { override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) = with(packageParam) { val crashReportingClass = classLoader.safeLoadClass("com.discord.crash_reporting.CrashReporting") crashReportingClass?.apply { - // Hooking this will result in a crash after a few seconds, since Discord expects initialization to complete when setting a Sentry tag. - // So we also hook isDisabled to make sure those calls are no-ops. - hookMethod( - "init", Context::class.java, String::class.java - ) { - before { - Log.i("Blocked CrashReporting initialization") - result = null - } - } // This only exists on 30720x and above runCatching { @@ -32,6 +22,18 @@ object BlockCrashReportingModule : Module() { result = true } } + }.onFailure { + // In older versions, this hook works fine. + // Hooking this on 30720x will result in a crash after a few seconds, + // since Discord asserts initialization when setting a Sentry tag. + hookMethod( + "init", Context::class.java, String::class.java + ) { + before { + Log.i("Blocked CrashReporting initialization") + result = null + } + } } } From 5c29b1072bd78358893848d3224dab2df8812cdd Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 23 Nov 2025 22:03:50 +0000 Subject: [PATCH 07/12] chore: bump version to 1.4.9 (1409) --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f95c4b2..a9d327d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "io.github.revenge.xposed" minSdk = 24 targetSdk = 36 - versionCode = 1408 - versionName = "1.4.8" + versionCode = 1409 + versionName = "1.4.9" } buildTypes { From e0c75e30afb6323ac447e5628d3414faf3186a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Purple=5F=CE=9Eye=E2=84=A2?= <168314373+Purple-EyeZ@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:25:16 +0100 Subject: [PATCH 08/12] perf(modules/HookScriptLoaderModule): don't block startup on bundle update if cached --- .../revenge/xposed/modules/HookScriptLoaderModule.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt index 1e0d282..536a6de 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt @@ -83,9 +83,17 @@ object HookScriptLoaderModule : Module() { runBlocking { val ready = async { HookStateHolder.readyDeferred.join() } - val download = async { UpdaterModule.downloadScript().join() } - awaitAll(ready, download) + if (mainScript.exists()) { + Log.i("Main script exists, updating in background...") + UpdaterModule.downloadScript() + + ready.await() + } else { + Log.i("Main script does not exist, downloading before load...") + val download = async { UpdaterModule.downloadScript().join() } + awaitAll(ready, download) + } } val loadSynchronously = args[2] From da337dfd984e0c7c1be77bbd973e003225737180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Purple=5F=CE=9Eye=E2=84=A2?= <168314373+Purple-EyeZ@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:57:01 +0100 Subject: [PATCH 09/12] feat(modules/UpdaterModule): add methods to force bundle update --- .../io/github/revenge/xposed/modules/UpdaterModule.kt | 7 +++++++ .../xposed/modules/bridge/AdditionalBridgeMethodsModule.kt | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt index 44e90eb..8a31e64 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt @@ -13,6 +13,7 @@ import io.github.revenge.xposed.Utils import io.github.revenge.xposed.Utils.Companion.JSON import io.github.revenge.xposed.Utils.Companion.reloadApp import io.github.revenge.xposed.Utils.Log +import io.github.revenge.xposed.modules.bridge.BridgeModule import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.cio.* @@ -71,6 +72,12 @@ object UpdaterModule : Module() { JSON.decodeFromString(configFile.readText()) } else LoaderConfig() }.getOrDefault(LoaderConfig()) + + BridgeModule.registerMethod("revenge.updater.clear") { + if (bundle.exists()) bundle.delete() + if (etag.exists()) etag.delete() + null + } } fun downloadScript(activity: Activity? = null): Job = scope.launch { diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/bridge/AdditionalBridgeMethodsModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/bridge/AdditionalBridgeMethodsModule.kt index 1637fb7..b2d9498 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/bridge/AdditionalBridgeMethodsModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/bridge/AdditionalBridgeMethodsModule.kt @@ -9,6 +9,7 @@ import android.os.Build import android.widget.Toast import io.github.revenge.xposed.Module import io.github.revenge.xposed.Utils +import io.github.revenge.xposed.Utils.Companion.reloadApp import java.io.File object AdditionalBridgeMethodsModule : Module() { @@ -47,6 +48,11 @@ object AdditionalBridgeMethodsModule : Module() { file.writeText(contents as String) } + + BridgeModule.registerMethod("revenge.app.reload") { + reloadApp() + null + } } override fun onActivity(activity: Activity) = with(activity) { From 87d4c78c67a690c369769f64c138186ee87bf579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Purple=5F=CE=9Eye=E2=84=A2?= <168314373+Purple-EyeZ@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:58:59 +0100 Subject: [PATCH 10/12] feat(modules/UpdaterModule): show update dialog when bundle is updated in background --- .../xposed/modules/HookScriptLoaderModule.kt | 5 ++--- .../revenge/xposed/modules/UpdaterModule.kt | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt index 536a6de..4e239f9 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt @@ -86,12 +86,11 @@ object HookScriptLoaderModule : Module() { if (mainScript.exists()) { Log.i("Main script exists, updating in background...") - UpdaterModule.downloadScript() - + UpdaterModule.downloadScript(showUpdateDialog = true) ready.await() } else { Log.i("Main script does not exist, downloading before load...") - val download = async { UpdaterModule.downloadScript().join() } + val download = async { UpdaterModule.downloadScript(showUpdateDialog = false).join() } awaitAll(ready, download) } } diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt index 8a31e64..d5d439a 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt @@ -80,7 +80,7 @@ object UpdaterModule : Module() { } } - fun downloadScript(activity: Activity? = null): Job = scope.launch { + fun downloadScript(activity: Activity? = null, showUpdateDialog: Boolean = true): Job = scope.launch { try { HttpClient(CIO) { expectSuccess = false @@ -115,15 +115,22 @@ object UpdaterModule : Module() { Log.i("Bundle updated (${bytes.size} bytes)") - // This is a retry, so we show a dialog - if (activity != null) { - withContext(Dispatchers.Main) { - AlertDialog.Builder(activity).setTitle("Revenge Update Successful") + if (showUpdateDialog) { + val activity = activity ?: lastActivity?.get() + + activity?.runOnUiThread { + AlertDialog.Builder(activity) + .setTitle("Revenge Update Downloaded") .setMessage("A reload is required for changes to take effect.") .setPositiveButton("Reload") { dialog, _ -> reloadApp() dialog.dismiss() - }.setCancelable(false).show() + } + .setNegativeButton("Later") { dialog, _ -> + dialog.dismiss() + } + .setCancelable(false) + .show() } } } From d9603ffdb5fb382016621b177f54a066b22ef776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Purple=5F=CE=9Eye=E2=84=A2?= <168314373+Purple-EyeZ@users.noreply.github.com> Date: Fri, 30 Jan 2026 05:09:47 +0100 Subject: [PATCH 11/12] fix(modules/HookScriptLoaderModule): don't use cache when custom bundle URL is enabled --- .../xposed/modules/HookScriptLoaderModule.kt | 13 ++++++++----- .../github/revenge/xposed/modules/UpdaterModule.kt | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt index 4e239f9..930f2c8 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt @@ -83,15 +83,18 @@ object HookScriptLoaderModule : Module() { runBlocking { val ready = async { HookStateHolder.readyDeferred.join() } + val isCustomUrl = UpdaterModule.isCustomUrlEnabled - if (mainScript.exists()) { + if (!mainScript.exists() || isCustomUrl) { + val reason = if (isCustomUrl) "Custom URL enabled" else "Main script does not exist" + Log.i("$reason, downloading before load...") + val download = + async { UpdaterModule.downloadScript(showUpdateDialog = false).join() } + awaitAll(ready, download) + } else { Log.i("Main script exists, updating in background...") UpdaterModule.downloadScript(showUpdateDialog = true) ready.await() - } else { - Log.i("Main script does not exist, downloading before load...") - val download = async { UpdaterModule.downloadScript(showUpdateDialog = false).join() } - awaitAll(ready, download) } } diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt index d5d439a..498fc8d 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/UpdaterModule.kt @@ -43,6 +43,8 @@ data class LoaderConfig( */ object UpdaterModule : Module() { private lateinit var config: LoaderConfig + val isCustomUrlEnabled: Boolean + get() = config.customLoadUrl.enabled private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private var lastActivity: WeakReference? = null From c6667c2307a250c3c725e6c745666eb3353d33f0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 1 Feb 2026 11:11:17 +0000 Subject: [PATCH 12/12] chore: bump version to 1.4.10 (1410) --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a9d327d..ee103e0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "io.github.revenge.xposed" minSdk = 24 targetSdk = 36 - versionCode = 1409 - versionName = "1.4.9" + versionCode = 1410 + versionName = "1.4.10" } buildTypes {