Update ThemesModule.kt
This commit is contained in:
parent
f210de66f2
commit
ccd0a1f066
1 changed files with 2 additions and 146 deletions
|
|
@ -1,9 +1,10 @@
|
|||
package GoonXposed.xposed.module.appearance
|
||||
package GoonXposed.xposed.modules.appearance
|
||||
|
||||
import GoonXposed.xposed.Module
|
||||
import GoonXposed.xposed.Constants
|
||||
import GoonXposed.xposed.Utils.Companion.JSON
|
||||
import GoonXposed.xposed.MethodHookBuilder
|
||||
// import GoonXposed.xposed.modules.appearance.ThemesModule
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.core.graphics.toColorInt
|
||||
|
|
@ -14,148 +15,3 @@ import kotlinx.serialization.Serializable
|
|||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
@Serializable data class Author(val name: String, val id: String? = null)
|
||||
|
||||
@Serializable
|
||||
data class ThemeData(
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val authors: List<Author>? = null,
|
||||
val spec: Int,
|
||||
val semanticColors: Map<String, List<String>>? = null,
|
||||
val rawColors: Map<String, String>? = null
|
||||
)
|
||||
|
||||
@Serializable data class Theme(val id: String, val selected: Boolean, val data: ThemeData)
|
||||
|
||||
class ThemesModule : Module() {
|
||||
private lateinit var param: XC_LoadPackage.LoadPackageParam
|
||||
|
||||
private var theme: Theme? = null
|
||||
private val rawColorMap = mutableMapOf<String, Int>()
|
||||
|
||||
private companion object {
|
||||
const val THEME_FILE = "current-theme.json"
|
||||
}
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
override fun buildPayload(builder: JsonObjectBuilder) {
|
||||
builder.apply {
|
||||
put("hasThemeSupport", true)
|
||||
if (theme != null) put("storedTheme", JSON.encodeToJsonElement<Theme>(theme!!))
|
||||
else put("storedTheme", null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.fromScreamingSnakeToCamelCase() =
|
||||
this.split("_").joinToString("") { it ->
|
||||
it.lowercase().replaceFirstChar { it.uppercase() }
|
||||
}
|
||||
|
||||
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {
|
||||
param = packageParam
|
||||
theme = getTheme()
|
||||
hookTheme()
|
||||
}
|
||||
|
||||
private fun File.isValidish(): Boolean {
|
||||
if (!this.exists()) return false
|
||||
|
||||
val text = this.readText()
|
||||
return text.isNotBlank() && text != "{}" && text != "null"
|
||||
}
|
||||
|
||||
private fun getTheme(): Theme? {
|
||||
val themeFile =
|
||||
File(param.appInfo.dataDir, "${Constants.FILES_DIR}/${THEME_FILE}").apply {
|
||||
asFile()
|
||||
}
|
||||
if (!themeFile.isValidish()) return null
|
||||
|
||||
return try {
|
||||
val themeText = themeFile.readText()
|
||||
JSON.decodeFromString<Theme>(themeText)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun hookTheme() {
|
||||
val themeManager = param.classLoader.loadClass("com.discord.theme.utils.ColorUtilsKt")
|
||||
val darkTheme = param.classLoader.loadClass("com.discord.theme.DarkerTheme")
|
||||
val lightTheme = param.classLoader.loadClass("com.discord.theme.LightTheme")
|
||||
|
||||
val theme = this.theme ?: return
|
||||
|
||||
// Apply rawColors
|
||||
theme.data.rawColors?.forEach { (key, value) ->
|
||||
rawColorMap[key.lowercase()] = hexStringToColorInt(value)
|
||||
}
|
||||
|
||||
// Apply semanticColors
|
||||
theme.data.semanticColors?.forEach { (key, value) ->
|
||||
// TEXT_NORMAL -> getTextNormal
|
||||
val methodName = "get${key.fromScreamingSnakeToCamelCase()}"
|
||||
value.forEachIndexed { index, v ->
|
||||
when (index) {
|
||||
0 -> hookThemeMethod(darkTheme, methodName, hexStringToColorInt(v))
|
||||
1 -> hookThemeMethod(lightTheme, methodName, hexStringToColorInt(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's any rawColors value, hook the color getter
|
||||
if (!theme.data.rawColors.isNullOrEmpty()) {
|
||||
val getColorCompat =
|
||||
themeManager.getDeclaredMethod(
|
||||
"getColorCompat",
|
||||
Resources::class.java,
|
||||
Int::class.javaPrimitiveType,
|
||||
Resources.Theme::class.java,
|
||||
)
|
||||
|
||||
val getColorCompatLegacy =
|
||||
themeManager.getDeclaredMethod(
|
||||
"getColorCompat",
|
||||
Context::class.java,
|
||||
Int::class.javaPrimitiveType
|
||||
)
|
||||
|
||||
val patch =
|
||||
MethodHookBuilder().run {
|
||||
before {
|
||||
val arg1 = args[0]
|
||||
val resources =
|
||||
if (arg1 is Context) arg1.resources else (arg1 as Resources)
|
||||
val name = resources.getResourceEntryName(args[1] as Int)
|
||||
|
||||
if (rawColorMap[name] != null) result = rawColorMap[name]
|
||||
}
|
||||
|
||||
build()
|
||||
}
|
||||
|
||||
getColorCompat.hook(patch)
|
||||
getColorCompatLegacy.hook(patch)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse HEX colour string to INT. Takes "#RRGGBBAA" or "#RRGGBB"
|
||||
private fun hexStringToColorInt(hexString: String): Int {
|
||||
return if (hexString.length == 9) {
|
||||
// Rearrange RRGGBBAA -> AARRGGBB so parseColor() is happy
|
||||
val alpha = hexString.substring(7, 9)
|
||||
val rrggbb = hexString.substring(1, 7)
|
||||
"#$alpha$rrggbb".toColorInt()
|
||||
} else hexString.toColorInt()
|
||||
}
|
||||
|
||||
private fun hookThemeMethod(themeClass: Class<*>, methodName: String, themeValue: Int) {
|
||||
try {
|
||||
themeClass.getDeclaredMethod(methodName).let { method ->
|
||||
method.hook { before { result = themeValue } }
|
||||
}
|
||||
} catch (_: NoSuchMethodException) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue