diff --git a/.idea/.name b/.idea/.name
index e7c85bc..bb21b8f 100644
--- a/.idea/.name
+++ b/.idea/.name
@@ -1 +1 @@
-PyoncordXposed
\ No newline at end of file
+BunnyXposed
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c36fae6..0f90194 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -33,6 +33,9 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
+ buildFeatures {
+ buildConfig = true
+ }
}
dependencies {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ca939d8..9e52cff 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 Bunny, a mod for Discord's mobile apps." />
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt b/app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt
index 64903a9..564c627 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt
+++ b/app/src/main/kotlin/io/github/pyoncord/xposed/FontsModule.kt
@@ -1,5 +1,5 @@
// credits to janisslsm from his PR: https://github.com/vendetta-mod/VendettaXposed/pull/17
-// the functions hooked are based on the RN codebase, just modified to add fonts
+// hooks are modified function from RN codebase
package io.github.pyoncord.xposed
@@ -10,32 +10,97 @@ 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
+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.*
+import io.ktor.client.call.*
+import io.ktor.client.request.*
+import io.ktor.client.engine.cio.*
+import io.ktor.client.statement.*
+import io.ktor.client.plugins.*
+import io.ktor.http.*
+
+@Serializable
+data class FontDefinition(
+ val name: String,
+ val description: String,
+ val spec: Int,
+ val hash: String,
+ val main: Map,
+)
class FontsModule: PyonModule() {
private val EXTENSIONS = arrayOf("", "_bold", "_italic", "_bold_italic")
private val FILE_EXTENSIONS = arrayOf(".ttf", ".otf")
private val FONTS_ASSET_PATH = "fonts/"
+
private lateinit var fontsDir: File
private lateinit var fontsAbsPath: String
override fun buildJson(builder: JsonObjectBuilder) {
builder.apply {
- put("supportFonts", true)
+ put("fontPatch", 1)
}
}
override fun onInit(packageParam: XC_LoadPackage.LoadPackageParam) = with (packageParam) {
- fontsDir = File(appInfo.dataDir, "files/pyoncord/fonts").also { it.mkdirs() }
+ val fontDefFile = File(appInfo.dataDir, "files/pyoncord/fonts.json")
+ if (!fontDefFile.exists()) return@with
+
+ val fontDef = try {
+ Json { ignoreUnknownKeys = true }.decodeFromString(fontDefFile.readText())
+ } catch (_: Throwable) { return@with }
+
+ fontsDir = File(appInfo.dataDir, "files/pyoncord/downloads/fonts").apply { mkdirs() }
fontsAbsPath = fontsDir.absolutePath + "/"
+ fontsDir.listFiles()?.forEach { file ->
+ val fileName = file.name
+ if (!fileName.startsWith(".")) {
+ val fontName = fileName.split('.')[0]
+ if (fontDef.main.keys.none { it == fontName }) {
+ Log.i("Bunny", "Deleting font file: $fileName")
+ file.delete()
+ }
+ }
+ }
+
+ val scope = MainScope()
+ val downloadJob = scope.async(Dispatchers.IO) {
+ fontDef.main.forEach { (name, url) ->
+ try {
+ Log.i("Bunny", "Downloading $name from $url")
+ val file = File(fontsDir, "$name${FILE_EXTENSIONS.first { url.endsWith(it) }}")
+ val client = HttpClient(CIO) {
+ install(UserAgent) { agent = "BunnyXposed" }
+ }
+
+ val response: HttpResponse = client.get(url)
+
+ if (response.status == HttpStatusCode.OK) {
+ file.writeBytes(response.body())
+ }
+
+ return@async
+ } catch (e: Throwable) {
+ Log.e("Bunny", "Failed to download fonts ($name from $url)")
+ }
+ }
+ }
+
XposedHelpers.findAndHookMethod("com.facebook.react.views.text.ReactFontManager", classLoader, "createAssetTypeface",
String::class.java,
Int::class.java,
@@ -44,6 +109,7 @@ class FontsModule: PyonModule() {
val fontFamilyName: String = param.args[0].toString();
val style: Int = param.args[1] as Int;
val assetManager: AssetManager = param.args[2] as AssetManager;
+ runBlocking { downloadJob.join() }
return createAssetTypeface(fontFamilyName, style, assetManager)
}
});
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt b/app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt
index da572ca..b0d52d9 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt
+++ b/app/src/main/kotlin/io/github/pyoncord/xposed/Main.kt
@@ -7,6 +7,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.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
@@ -34,12 +35,12 @@ class Main : IXposedHookLoadPackage {
ThemeModule(),
SysColorsModule(),
FontsModule(),
- // BubbleModule()
)
fun buildLoaderJsonString(): String {
val obj = buildJsonObject {
- put("loaderName", "PyoncordXposed")
+ put("loaderName", "BunnyXposed")
+ put("loaderVersion", BuildConfig.VERSION_NAME)
for (module in pyonModules) {
module.buildJson(this)
@@ -104,7 +105,7 @@ class Main : IXposedHookLoadPackage {
install(HttpTimeout) {
requestTimeoutMillis = if (bundle.exists()) 3000 else HttpTimeout.INFINITE_TIMEOUT_MS
}
- install(UserAgent) { agent = "PyoncordXposed" }
+ install(UserAgent) { agent = "BunnyXposed" }
}
val url =
@@ -127,7 +128,7 @@ class Main : IXposedHookLoadPackage {
return@async
} catch (e: Exception) {
- Log.e("Pyoncord", "Failed to download Pyoncord")
+ Log.e("Bunny", "Failed to download bundle")
}
}
diff --git a/app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt b/app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt
index 5a3376e..7b1cb6c 100644
--- a/app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt
+++ b/app/src/main/kotlin/io/github/pyoncord/xposed/ThemeModule.kt
@@ -77,9 +77,8 @@ class ThemeModule : PyonModule() {
if (!themeFile.isValidish()) return null
- val themeText = themeFile.readText()
-
return try {
+ val themeText = themeFile.readText()
Json { ignoreUnknownKeys = true }.decodeFromString(themeText)
} catch (e: Exception) { null }
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 261dd6d..a327e5c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- PyoncordXposed
+ BunnyXposed
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index c288f19..808d13f 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,2 +1,2 @@
-rootProject.name = "PyoncordXposed"
+rootProject.name = "BunnyXposed"
include(":app")