diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1fc257c..2c82391 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,29 +25,29 @@ jobs:
- name: "Setup directories"
run: |
- mkdir -p /tmp/pyonbuild/apks
- mkdir -p /tmp/pyonbuild/tools
+ mkdir -p /tmp/build/apks
+ mkdir -p /tmp/build/tools
- name: "Move debug APK"
- run: mv ./app/build/outputs/apk/debug/app-debug.apk /tmp/pyonbuild/apks
+ run: mv ./app/build/outputs/apk/debug/app-debug.apk /tmp/build/apks
- name: "Download uber-apk-signer"
- run: wget -nv "https://github.com/patrickfav/uber-apk-signer/releases/download/v1.2.1/uber-apk-signer-1.2.1.jar" -O /tmp/pyonbuild/tools/uber-apk-signer.jar
+ run: wget -nv "https://github.com/patrickfav/uber-apk-signer/releases/download/v1.2.1/uber-apk-signer-1.2.1.jar" -O /tmp/build/tools/uber-apk-signer.jar
- name: "Sign release APK"
- run: java -jar /tmp/pyonbuild/tools/uber-apk-signer.jar --apks ./app/build/outputs/apk/release/app-release-unsigned.apk --out /tmp/pyonbuild/apks/
+ run: java -jar /tmp/build/tools/uber-apk-signer.jar --apks ./app/build/outputs/apk/release/app-release-unsigned.apk --out /tmp/build/apks/
- name: "Rename release APK"
- run: mv /tmp/pyonbuild/apks/app-release-aligned-debugSigned.apk /tmp/pyonbuild/apks/app-release.apk
+ run: mv /tmp/build/apks/app-release-aligned-debugSigned.apk /tmp/build/apks/app-release.apk
- name: "Upload debug APK"
uses: actions/upload-artifact@v3
with:
name: app-debug
- path: /tmp/pyonbuild/apks/app-debug.apk
+ path: /tmp/build/apks/app-debug.apk
- name: "Upload release APK"
uses: actions/upload-artifact@v3
with:
name: app-release
- path: /tmp/pyonbuild/apks/app-release.apk
\ No newline at end of file
+ path: /tmp/build/apks/app-release.apk
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index e58d3e4..b589d56 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..19ce6a7
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index cbbe561..0897082 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,16 +4,15 @@
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 9924903..0ad17cb 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7..35eb1dd 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 4fabd7c..c941490 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,15 +5,15 @@ plugins {
}
android {
- namespace = "io.github.pyoncord.xposed"
+ namespace = "io.github.revenge.xposed"
compileSdk = 33
defaultConfig {
- applicationId = "io.github.pyoncord.xposed"
+ applicationId = "io.github.revenge.xposed"
minSdk = 24
targetSdk = 33
- versionCode = 203
- versionName = "0.2.3"
+ versionCode = 204
+ versionName = "0.2.4"
}
buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9e52cff..84c00fd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,7 +13,7 @@
android:value="true" />
+ android:value="An Xposed module to inject Revenge, a mod for Discord's mobile apps." />
diff --git a/app/src/main/assets/xposed_init b/app/src/main/assets/xposed_init
index 9afddb0..9ad2900 100644
--- a/app/src/main/assets/xposed_init
+++ b/app/src/main/assets/xposed_init
@@ -1 +1 @@
-io.github.pyoncord.xposed.Main
\ No newline at end of file
+io.github.revenge.xposed.Main
\ No newline at end of file
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/FontsModule.kt
similarity index 93%
rename from app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt
rename to app/src/main/kotlin/io/github/revenge/xposed/FontsModule.kt
index 85e574d..0481a6f 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt
+++ b/app/src/main/kotlin/io/github/revenge/xposed/FontsModule.kt
@@ -1,19 +1,16 @@
// credits to janisslsm from his PR: https://github.com/vendetta-mod/VendettaXposed/pull/17
// hooks are modified function from RN codebase
-package io.github.pyoncord.xposed
+package io.github.revenge.xposed
import android.content.res.AssetManager
import android.os.Build
-import android.graphics.Color
import android.graphics.Typeface
import android.graphics.Typeface.CustomFallbackBuilder
import android.graphics.fonts.Font
import android.graphics.fonts.FontFamily
import android.util.Log
-import android.webkit.URLUtil
import de.robv.android.xposed.XC_MethodReplacement
-import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage
import kotlinx.serialization.Serializable
@@ -21,8 +18,6 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.*
import java.io.IOException
import java.io.File
-import java.net.HttpURLConnection
-import java.net.URL
import kotlinx.coroutines.*
import io.ktor.client.*
@@ -41,7 +36,7 @@ data class FontDefinition(
val main: Map,
)
-class FontsModule: PyonModule() {
+class FontsModule: Module() {
private val EXTENSIONS = arrayOf("", "_bold", "_italic", "_bold_italic")
private val FILE_EXTENSIONS = arrayOf(".ttf", ".otf")
private val FONTS_ASSET_PATH = "fonts/"
@@ -67,7 +62,7 @@ class FontsModule: PyonModule() {
val assetManager: AssetManager = param.args[2] as AssetManager
return createAssetTypeface(fontFamilyName, style, assetManager)
}
- });
+ })
val fontDefFile = File(appInfo.dataDir, "files/pyoncord/fonts.json")
if (!fontDefFile.exists()) return@with
@@ -85,7 +80,7 @@ class FontsModule: PyonModule() {
if (!fileName.startsWith(".")) {
val fontName = fileName.split('.')[0]
if (fontDef.main.keys.none { it == fontName }) {
- Log.i("Bunny", "Deleting font file: $fileName")
+ Log.i("Revenge", "Deleting font file: $fileName")
file.delete()
}
}
@@ -97,12 +92,12 @@ class FontsModule: PyonModule() {
async {
val url = fontDef.main.getValue(name)
try {
- Log.i("Bunny", "Downloading $name from $url")
+ Log.i("Revenge", "Downloading $name from $url")
val file = File(fontsDir, "$name${FILE_EXTENSIONS.first { url.endsWith(it) }}")
if (file.exists()) return@async
val client = HttpClient(CIO) {
- install(UserAgent) { agent = "BunnyXposed" }
+ install(UserAgent) { agent = "RevengeXposed" }
}
val response: HttpResponse = client.get(url)
@@ -113,7 +108,7 @@ class FontsModule: PyonModule() {
return@async
} catch (e: Throwable) {
- Log.e("Bunny", "Failed to download fonts ($name from $url)", e)
+ Log.e("Revenge", "Failed to download fonts ($name from $url)", e)
}
}
}.awaitAll()
@@ -144,7 +139,7 @@ class FontsModule: PyonModule() {
// ignore
}
- for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filter { it != null }) {
+ for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filterNotNull()) {
for (fileExtension in FILE_EXTENSIONS) {
val fileName = java.lang.StringBuilder()
.append(fontRootPath)
@@ -220,7 +215,7 @@ class FontsModule: PyonModule() {
// Lastly, after all those checks above, this is the original RN logic for
// getting the typeface.
- for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filter { it != null }) {
+ for (fontRootPath in arrayOf(fontsAbsPath, FONTS_ASSET_PATH).filterNotNull()) {
for (fileExtension in FILE_EXTENSIONS) {
val fileName = java.lang.StringBuilder()
.append(fontRootPath)
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt
similarity index 73%
rename from app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt
rename to app/src/main/kotlin/io/github/revenge/xposed/Main.kt
index c7718a4..46360af 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt
+++ b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt
@@ -1,8 +1,6 @@
-package io.github.pyoncord.xposed
+package io.github.revenge.xposed
-import android.app.Activity
-import android.app.AndroidAppHelper
-import android.content.Context
+import android.app.Activity
import android.content.res.AssetManager
import android.content.res.Resources
import android.util.Log
@@ -12,7 +10,7 @@ import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage
-import io.github.pyoncord.xposed.BuildConfig
+import io.github.revenge.xposed.BuildConfig
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
@@ -36,7 +34,7 @@ data class LoaderConfig(
)
class Main : IXposedHookLoadPackage {
- private val pyonModules: Array = arrayOf(
+ private val modules: Array = arrayOf(
ThemeModule(),
SysColorsModule(),
FontsModule(),
@@ -47,7 +45,7 @@ class Main : IXposedHookLoadPackage {
put("loaderName", "BunnyXposed")
put("loaderVersion", BuildConfig.VERSION_NAME)
- for (module in pyonModules) {
+ for (module in modules) {
module.buildJson(this)
}
}
@@ -56,24 +54,34 @@ class Main : IXposedHookLoadPackage {
}
override fun handleLoadPackage(param: XC_LoadPackage.LoadPackageParam) = with (param) {
- // val reactActivity = runCatching {
- // lpparam.classLoader.loadClass("com.discord.react_activities.ReactActivity")
- // }.getOrElse { return } // Package is not our the target app, return
+ val reactActivity = runCatching {
+ classLoader.loadClass("com.discord.react_activities.ReactActivity")
+ }.getOrElse { return@with } // Package is not our the target app, return
- // XposedBridge.hookMethod(reactActivity.getDeclaredMethod("onCreate", Bundle::class.java), object : XC_MethodHook() {
- // override fun beforeHookedMethod(param: MethodHookParam) {
- // init(lpparam, param.thisObject as Activity)
- // }
- // })
- // }
+ var activity: Activity? = null;
+ val onActivityCreateCallback = mutableSetOf<(activity: Activity) -> Unit>()
- // fun init(param: XC_LoadPackage.LoadPackageParam, activity: Activity) = with (param) {
- // val catalystInstanceImpl = classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl")
- val catalystInstanceImpl = runCatching {
- classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl")
- }.getOrElse { return@with }
+ XposedBridge.hookMethod(reactActivity.getDeclaredMethod("onCreate", Bundle::class.java), object : XC_MethodHook() {
+ override fun beforeHookedMethod(param: MethodHookParam) {
+ activity = param.thisObject as Activity;
+ onActivityCreateCallback.forEach { cb -> cb(activity!!) }
+ onActivityCreateCallback.clear()
+ }
+ })
+
+ init(param) { cb ->
+ if (activity != null) cb(activity!!)
+ else onActivityCreateCallback.add(cb)
+ }
+ }
+
+ private fun init(
+ param: XC_LoadPackage.LoadPackageParam,
+ onActivityCreate: ((activity: Activity) -> Unit) -> Unit
+ ) = with (param) {
+ val catalystInstanceImpl = classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl")
- for (module in pyonModules) module.onInit(param)
+ for (module in modules) module.onInit(param)
val loadScriptFromAssets = catalystInstanceImpl.getDeclaredMethod(
"loadScriptFromAssets",
@@ -124,14 +132,14 @@ class Main : IXposedHookLoadPackage {
install(HttpTimeout) {
requestTimeoutMillis = if (bundle.exists()) 3000 else 10000
}
- install(UserAgent) { agent = "BunnyXposed" }
+ install(UserAgent) { agent = "RevengeXposed" }
}
val url =
if (config.customLoadUrl.enabled) config.customLoadUrl.url
- else "https://github.com/revenge-mod/Revenge/releases/latest/download/revenge.js"
+ else "https://github.com/revenge-mod/revenge-bundle/releases/latest/download/revenge.min.js"
- Log.e("Bunny", "Fetching JS bundle from $url")
+ Log.e("Revenge", "Fetching JS bundle from $url")
val response: HttpResponse = client.get(url) {
headers {
@@ -153,13 +161,19 @@ class Main : IXposedHookLoadPackage {
return@async
} catch (e: RedirectResponseException) {
if (e.response.status != HttpStatusCode.NotModified) throw e;
- Log.e("Bunny", "Server reponded with status code 304 - no changes to file")
+ Log.e("Revenge", "Server responded with status code 304 - no changes to file")
} catch (e: Throwable) {
- // activity.runOnUiThread {
- // Toast.makeText(activity.applicationContext, "Failed to fetch JS bundle, Bunny may not load!", Toast.LENGTH_SHORT).show()
- // }
+ onActivityCreate { activity ->
+ activity.runOnUiThread {
+ Toast.makeText(
+ activity.applicationContext,
+ "Failed to fetch JS bundle, Revenge may not load!",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
- Log.e("Bunny", "Failed to download bundle", e)
+ Log.e("Revenge", "Failed to download bundle", e)
}
}
@@ -211,4 +225,4 @@ class Main : IXposedHookLoadPackage {
})
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/PyonModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/Module.kt
similarity index 79%
rename from app/src/main/kotlin/io/github/pyoncord/xposed/PyonModule.kt
rename to app/src/main/kotlin/io/github/revenge/xposed/Module.kt
index ebcab56..b0416fd 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/PyonModule.kt
+++ b/app/src/main/kotlin/io/github/revenge/xposed/Module.kt
@@ -1,9 +1,9 @@
-package io.github.pyoncord.xposed
+package io.github.revenge.xposed
import de.robv.android.xposed.callbacks.XC_LoadPackage
import kotlinx.serialization.json.JsonObjectBuilder
-abstract class PyonModule {
+abstract class Module {
open fun buildJson(builder: JsonObjectBuilder) {}
open fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) {}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/SysColorsModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/SysColorsModule.kt
similarity index 88%
rename from app/src/main/kotlin/io/github/pyoncord/xposed/SysColorsModule.kt
rename to app/src/main/kotlin/io/github/revenge/xposed/SysColorsModule.kt
index 140b631..9ea6db3 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/SysColorsModule.kt
+++ b/app/src/main/kotlin/io/github/revenge/xposed/SysColorsModule.kt
@@ -1,4 +1,4 @@
-package io.github.pyoncord.xposed
+package io.github.revenge.xposed
import android.app.AndroidAppHelper
import android.content.Context
@@ -16,9 +16,9 @@ data class SysColors(
val accent3: List
)
-class SysColorsModule : PyonModule() {
+class SysColorsModule : Module() {
private lateinit var context: Context
- fun isSupported() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+ private fun isSupported() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
override fun buildJson(builder: JsonObjectBuilder) {
context = AndroidAppHelper.currentApplication()
@@ -43,7 +43,7 @@ class SysColorsModule : PyonModule() {
}
}
- fun convertToColor(id: Int): String {
+ private fun convertToColor(id: Int): String {
val clr = if (isSupported()) ContextCompat.getColor(context, id) else 0
return String.format("#%06X", 0xFFFFFF and clr)
}
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/ThemeModule.kt
similarity index 91%
rename from app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt
rename to app/src/main/kotlin/io/github/revenge/xposed/ThemeModule.kt
index 7b1cb6c..175993d 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt
+++ b/app/src/main/kotlin/io/github/revenge/xposed/ThemeModule.kt
@@ -1,4 +1,4 @@
-package io.github.pyoncord.xposed
+package io.github.revenge.xposed
import android.content.Context
import android.graphics.Color
@@ -35,7 +35,7 @@ data class Theme(
val data: ThemeData
)
-class ThemeModule : PyonModule() {
+class ThemeModule : Module() {
private lateinit var param: XC_LoadPackage.LoadPackageParam
private var theme: Theme? = null
@@ -58,14 +58,14 @@ class ThemeModule : PyonModule() {
hookTheme()
}
- fun File.isValidish(): Boolean {
+ private fun File.isValidish(): Boolean {
if (!this.exists()) return false
val text = this.readText()
- return !text.isBlank() && text != "{}" && text != "null"
+ return text.isNotBlank() && text != "{}" && text != "null"
}
- fun getTheme(): Theme? {
+ private fun getTheme(): Theme? {
val filesDir = File(param.appInfo.dataDir, "files").apply { mkdirs() }
val pyonDir = File(filesDir, "pyoncord").apply { mkdirs() }
val themeFile = File(pyonDir, "current-theme.json")
@@ -139,12 +139,12 @@ class ThemeModule : PyonModule() {
}
// Convert 0xRRGGBBAA to 0XAARRGGBB
- fun hexStringToColorInt(hexString: String): Int {
+ private fun hexStringToColorInt(hexString: String): Int {
val parsed = Color.parseColor(hexString)
- return parsed.takeIf { hexString.length == 7 } ?: parsed and 0xFFFFFF or (parsed ushr 24)
+ return parsed.takeIf { hexString.length == 7 } ?: (parsed and 0xFFFFFF or (parsed ushr 24))
}
- fun hookThemeMethod(themeClass: Class<*>, methodName: String, themeValue: Int) {
+ private fun hookThemeMethod(themeClass: Class<*>, methodName: String, themeValue: Int) {
try {
themeClass.getDeclaredMethod(methodName).let { method ->
// Log.i("Hooking $methodName -> ${themeValue.toString(16)}")
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a327e5c..401896b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- BunnyXposed
+ RevengeXposed
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 808d13f..754c616 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,2 +1,2 @@
-rootProject.name = "BunnyXposed"
+rootProject.name = "RevengeXposed"
include(":app")