[general] Bunny-fication

This commit is contained in:
amsyarasyiq 2024-04-30 13:42:18 +08:00
parent e2b762d387
commit 68d03add10
8 changed files with 82 additions and 13 deletions

2
.idea/.name generated
View file

@ -1 +1 @@
PyoncordXposed
BunnyXposed

View file

@ -33,6 +33,9 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
buildConfig = true
}
}
dependencies {

View file

@ -13,7 +13,7 @@
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="An Xposed module to inject Pyoncord, a mod for Discord's mobile apps." />
android:value="An Xposed module to inject Bunny, a mod for Discord's mobile apps." />
<meta-data
android:name="xposedminversion"
android:value="82" />

View file

@ -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<String, String>,
)
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<FontDefinition>(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)
}
});

View file

@ -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")
}
}

View file

@ -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<Theme>(themeText)
} catch (e: Exception) { null }
}

View file

@ -1,3 +1,3 @@
<resources>
<string name="app_name">PyoncordXposed</string>
<string name="app_name">BunnyXposed</string>
</resources>

View file

@ -1,2 +1,2 @@
rootProject.name = "PyoncordXposed"
rootProject.name = "BunnyXposed"
include(":app")