From 68d03add1078462c96b7589a1c7c2e89d86c2397 Mon Sep 17 00:00:00 2001 From: amsyarasyiq <82711525+amsyarasyiq@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:42:18 +0800 Subject: [PATCH] [general] Bunny-fication --- .idea/.name | 2 +- app/build.gradle.kts | 3 + app/src/main/AndroidManifest.xml | 2 +- .../io/github/pyoncord/xposed/FontsModule.kt | 72 ++++++++++++++++++- .../kotlin/io/github/pyoncord/xposed/Main.kt | 9 +-- .../io/github/pyoncord/xposed/ThemeModule.kt | 3 +- app/src/main/res/values/strings.xml | 2 +- settings.gradle.kts | 2 +- 8 files changed, 82 insertions(+), 13 deletions(-) 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")