192 lines
7.9 KiB
Kotlin

package app.revanced.util.resources
import app.revanced.extensions.doRecursively
import app.revanced.patcher.data.ResourceContext
import org.w3c.dom.Element
import org.w3c.dom.Node
private fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
val child = ownerDocument.createElement(tagName)
child.block()
appendChild(child)
}
private fun Node.cloneNodes(parent: Node) {
val node = cloneNode(true)
parent.appendChild(node)
parent.removeChild(this)
}
private fun Node.insertNode(tagName: String, targetNode: Node, block: Element.() -> Unit) {
val child = ownerDocument.createElement(tagName)
child.block()
parentNode.insertBefore(child, targetNode)
}
internal object MusicResourceHelper {
private const val YOUTUBE_MUSIC_SETTINGS_PATH = "res/xml/settings_headers.xml"
private const val YOUTUBE_MUSIC_SETTINGS_KEY = "revanced_extended_settings"
private const val YOUTUBE_MUSIC_CATEGORY_TAG_NAME = "com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat"
private const val YOUTUBE_MUSIC_PREFERENCE_TAG_NAME = "com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference"
private const val YOUTUBE_MUSIC_PREFERENCE_TARGET_CLASS = "com.google.android.libraries.strictmode.penalties.notification.FullStackTraceActivity"
private var currentMusicPreferenceCategory = emptyArray<String>()
private var targetPackage = "com.google.android.apps.youtube.music"
internal fun ResourceContext.setMicroG (newPackage: String) {
targetPackage = newPackage
replacePackageName()
}
private fun setMusicPreferenceCategory (newCategory: String) {
currentMusicPreferenceCategory += listOf(newCategory)
}
internal fun ResourceContext.addMusicPreferenceCategory(
category: String
) {
this.xmlEditor[YOUTUBE_MUSIC_SETTINGS_PATH].use { editor ->
val tags = editor.file.getElementsByTagName("PreferenceScreen")
List(tags.length) { tags.item(it) as Element }
.filter { it.getAttribute("android:key").contains(YOUTUBE_MUSIC_SETTINGS_KEY) }
.forEach {
if (!currentMusicPreferenceCategory.contains(category)) {
it.adoptChild(YOUTUBE_MUSIC_CATEGORY_TAG_NAME) {
setAttribute("android:title", "@string/revanced_category_$category")
setAttribute("android:key", "revanced_settings_$category")
}
setMusicPreferenceCategory(category)
}
}
}
}
internal fun ResourceContext.sortMusicPreferenceCategory(
category: String
) {
this.xmlEditor[YOUTUBE_MUSIC_SETTINGS_PATH].use { editor ->
editor.file.doRecursively loop@{
if (it !is Element) return@loop
it.getAttributeNode("android:key")?.let { attribute ->
if (attribute.textContent == "revanced_settings_$category") {
it.cloneNodes(it.parentNode)
}
}
}
}
replacePackageName()
}
private fun ResourceContext.replacePackageName(){
this[YOUTUBE_MUSIC_SETTINGS_PATH].writeText(
this[YOUTUBE_MUSIC_SETTINGS_PATH].readText().replace("\"com.google.android.apps.youtube.music\"", "\"" + targetPackage + "\"")
)
}
internal fun ResourceContext.addMusicPreference(
category: String,
key: String,
defaultValue: String
) {
this.xmlEditor[YOUTUBE_MUSIC_SETTINGS_PATH].use { editor ->
val tags = editor.file.getElementsByTagName(YOUTUBE_MUSIC_CATEGORY_TAG_NAME)
List(tags.length) { tags.item(it) as Element }
.filter { it.getAttribute("android:key").contains("revanced_settings_$category") }
.forEach {
it.adoptChild(YOUTUBE_MUSIC_PREFERENCE_TAG_NAME) {
setAttribute("android:title", "@string/$key" + "_title")
setAttribute("android:summary", "@string/$key" + "_summary")
setAttribute("android:key", key)
setAttribute("android:defaultValue", defaultValue)
}
}
}
}
internal fun ResourceContext.addMusicPreferenceAlt(
category: String,
key: String,
defaultValue: String,
dependencyKey: String
) {
this.xmlEditor[YOUTUBE_MUSIC_SETTINGS_PATH].use { editor ->
val tags = editor.file.getElementsByTagName(YOUTUBE_MUSIC_CATEGORY_TAG_NAME)
List(tags.length) { tags.item(it) as Element }
.filter { it.getAttribute("android:key").contains("revanced_settings_$category") }
.forEach {
it.adoptChild(YOUTUBE_MUSIC_PREFERENCE_TAG_NAME) {
setAttribute("android:title", "@string/$key" + "_title")
setAttribute("android:summaryOn", "@string/$key" + "_summary_on")
setAttribute("android:summaryOff", "@string/$key" + "_summary_off")
setAttribute("android:key", key)
setAttribute("android:defaultValue", defaultValue)
setAttribute("android:dependency", dependencyKey)
}
}
}
}
internal fun ResourceContext.addMusicPreferenceWithIntent(
category: String,
key: String,
dependencyKey: String
) {
this.xmlEditor[YOUTUBE_MUSIC_SETTINGS_PATH].use { editor ->
val tags = editor.file.getElementsByTagName(YOUTUBE_MUSIC_CATEGORY_TAG_NAME)
List(tags.length) { tags.item(it) as Element }
.filter { it.getAttribute("android:key").contains("revanced_settings_$category") }
.forEach {
it.adoptChild("Preference") {
setAttribute("android:title", "@string/$key" + "_title")
setAttribute("android:summary", "@string/$key" + "_summary")
setAttribute("android:key", key)
setAttribute("android:dependency", dependencyKey)
this.adoptChild("intent") {
setAttribute("android:targetPackage", targetPackage)
setAttribute("android:data", key)
setAttribute("android:targetClass", YOUTUBE_MUSIC_PREFERENCE_TARGET_CLASS)
}
}
}
}
}
internal fun ResourceContext.addReVancedMusicPreference() {
this.xmlEditor[YOUTUBE_MUSIC_SETTINGS_PATH].use { editor ->
with (editor.file) {
doRecursively loop@{
if (it !is Element) return@loop
it.getAttributeNode("android:key")?.let { attribute ->
if (attribute.textContent == "settings_header_about_youtube_music" && it.getAttributeNode("app:allowDividerBelow").textContent == "false") {
it.insertNode("PreferenceScreen", it) {
setAttribute("android:title", "@string/" + YOUTUBE_MUSIC_SETTINGS_KEY + "_title")
setAttribute("android:key", YOUTUBE_MUSIC_SETTINGS_KEY)
}
it.getAttributeNode("app:allowDividerBelow").textContent = "true"
return@loop
}
}
}
doRecursively loop@{
if (it !is Element) return@loop
it.getAttributeNode("app:allowDividerBelow")?.let { attribute ->
if (attribute.textContent == "true") {
attribute.textContent = "false"
}
}
}
}
}
}
}