refactor: abstract settings patch away from YouTube (#1103)

Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
Tim Schneeberger
2022-11-25 00:05:54 +01:00
committed by GitHub
co-authored by oSumAtrIX
parent e79ad750d4
commit 5afd49a111
84 changed files with 554 additions and 336 deletions
@@ -11,15 +11,15 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.impl.Preference
import app.revanced.patches.shared.settings.impl.PreferenceScreen
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.LicenseActivityFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterAppFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterSystemFingerprint
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
import org.jf.dexlib2.util.MethodUtil
import java.io.Closeable
@@ -134,7 +134,7 @@ class SettingsPatch : BytecodePatch(
fun addString(identifier: String, value: String, formatted: Boolean = true) =
SettingsResourcePatch.addString(identifier, value, formatted)
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.youtube.misc.settings.framework.components.impl.PreferenceScreen) =
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.shared.settings.impl.PreferenceScreen) =
SettingsResourcePatch.addPreferenceScreen(preferenceScreen)
fun addPreference(preference: Preference) =
@@ -1,14 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
/**
* Base preference class for all preferences.
*
* @param key The key of the preference.
* @param title The title of the preference.
*/
internal abstract class BasePreference(
override val key: String,
override val title: StringResource,
) : IPreference
@@ -1,23 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
/**
* Preference
*/
internal interface IPreference {
/**
* Key of the preference.
*/
val key: String
/**
* Title of the preference.
*/
val title: StringResource
/**
* Tag name of the preference.
*/
val tag: String
}
@@ -1,9 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
/**
* Represents an array resource.
*
* @param name The name of the array resource.
* @param items The items of the array resource.
*/
internal data class ArrayResource(val name: String, val items: List<StringResource>)
@@ -1,6 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
enum class InputType(val type: String) {
STRING("text"),
NUMBER("number"),
}
@@ -1,18 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
/**
* A Preference object.
*
* @param title The title of the preference.
* @param intent The intent of the preference.
* @param summary The summary of the text preference.
*/
internal class Preference(
val title: StringResource,
val intent: Intent,
val summary: StringResource? = null
) {
val tag: String = "Preference"
data class Intent(val targetPackage: String, val data: String, val targetClass: String)
}
@@ -1,20 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
/**
* Preference screen.
*
* @param key The key of the preference.
* @param title The title of the preference.
* @param preferences Child preferences of this screen.
* @param summary The summary of the text preference.
*/
internal open class PreferenceScreen(
key: String,
title: StringResource,
val preferences: List<BasePreference>,
var summary: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "PreferenceScreen"
}
@@ -1,10 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
/**
* Represents a string value in the strings.xml file
*
* @param name The name of the string
* @param value The value of the string
* @param formatted If the string is formatted. If false, the attribute will be set
*/
internal data class StringResource(val name: String, val value: String, val formatted: Boolean = true)
@@ -1,21 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
/**
* Switch preference.
*
* @param key The key of the switch.
* @param title The title of the switch.
* @param default The default value of the switch.
* @param summaryOn The summary to show when the preference is enabled.
* @param summaryOff The summary to show when the preference is disabled.
*/
internal class SwitchPreference(
key: String, title: StringResource,
val default: Boolean = false,
var summaryOn: StringResource? = null,
var summaryOff: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "SwitchPreference"
}
@@ -1,22 +0,0 @@
package app.revanced.patches.youtube.misc.settings.framework.components.impl
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
/**
* Text preference.
*
* @param key The key of the text preference.
* @param title The title of the text preference.
* @param inputType The input type of the text preference.
* @param default The default value of the text preference.
* @param summary The summary of the text preference.
*/
internal class TextPreference(
key: String,
title: StringResource,
var inputType: InputType = InputType.STRING,
var default: String? = null,
var summary: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "EditTextPreference"
}
@@ -9,14 +9,18 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.shared.mapping.patch.ResourceMappingPatch
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.*
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.addPreference
import app.revanced.patches.shared.settings.addResource
import app.revanced.patches.shared.settings.impl.ArrayResource
import app.revanced.patches.shared.settings.impl.Preference
import app.revanced.patches.shared.settings.impl.PreferenceScreen
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.util.resources.ResourceUtils
import app.revanced.util.resources.ResourceUtils.copyResources
import org.w3c.dom.Element
import org.w3c.dom.Node
@Name("settings-resource-patch")
@@ -145,17 +149,7 @@ class SettingsResourcePatch : ResourcePatch {
* @param arrayResource The array resource to add.
*/
fun addArray(arrayResource: ArrayResource) {
arraysNode!!.appendChild(arraysNode!!.ownerDocument.createElement("string-array").also { arrayNode ->
arrayResource.items.forEach { item ->
item.include()
arrayNode.setAttribute("name", item.name)
arrayNode.appendChild(arrayNode.ownerDocument.createElement("item").also { itemNode ->
itemNode.textContent = item.value
})
}
})
arraysNode!!.appendChild(arrayResource.serialize(arraysNode!!.ownerDocument) { it.include() })
}
/**
@@ -164,7 +158,7 @@ class SettingsResourcePatch : ResourcePatch {
* @param preferenceScreen The name of the preference screen.
*/
fun addPreferenceScreen(preferenceScreen: PreferenceScreen) =
revancedPreferenceNode!!.addPreference(preferenceScreen)
revancedPreferenceNode!!.addPreference(preferenceScreen) { it.include() }
/**
* Add a preference fragment to the preferences.
@@ -172,101 +166,34 @@ class SettingsResourcePatch : ResourcePatch {
* @param preference The preference to add.
*/
fun addPreference(preference: Preference) {
preferencesNode!!.appendChild(preferencesNode.createElement(preference.tag).also { preferenceNode ->
preferenceNode.setAttribute(
"android:title", "@string/${preference.title.also { it.include() }.name}"
)
preference.summary?.let { summary ->
preferenceNode.setAttribute("android:summary", "@string/${summary.also { it.include() }.name}")
}
preferenceNode.appendChild(preferenceNode.createElement("intent").also { intentNode ->
intentNode.setAttribute("android:targetPackage", preference.intent.targetPackage)
intentNode.setAttribute("android:data", preference.intent.data)
intentNode.setAttribute("android:targetClass", preference.intent.targetClass)
})
})
}
/**
* Add a preference to the settings.
*
* @param preference The preference to add.
*/
private fun Node.addPreference(preference: BasePreference) {
// add a summary to the element
fun Element.addSummary(summaryResource: StringResource?, summaryType: SummaryType = SummaryType.DEFAULT) =
summaryResource?.let { summary ->
setAttribute("android:${summaryType.type}", "@string/${summary.also { it.include() }.name}")
}
fun <T> Element.addDefault(default: T) {
default?.let {
setAttribute(
"android:defaultValue", when (it) {
is Boolean -> if (it) "true" else "false"
is String -> it
else -> throw IllegalArgumentException("Unsupported default value type: ${it::class.java.name}")
}
)
}
}
val preferenceElement = ownerDocument.createElement(preference.tag)
preferenceElement.setAttribute("android:key", preference.key)
preferenceElement.setAttribute("android:title", "@string/${preference.title.also { it.include() }.name}")
when (preference) {
is PreferenceScreen -> {
for (childPreference in preference.preferences) preferenceElement.addPreference(childPreference)
preferenceElement.addSummary(preference.summary)
}
is SwitchPreference -> {
preferenceElement.addDefault(preference.default)
preferenceElement.addSummary(preference.summaryOn, SummaryType.ON)
preferenceElement.addSummary(preference.summaryOff, SummaryType.OFF)
}
is TextPreference -> {
preferenceElement.setAttribute("android:inputType", preference.inputType.type)
preferenceElement.addDefault(preference.default)
preferenceElement.addSummary(preference.summary)
}
}
appendChild(preferenceElement)
preferencesNode!!.addPreference(preference) { it.include() }
}
/**
* Add a new string to the resources.
* Add a new resource to the resources.
*
* @throws IllegalArgumentException if the string already exists.
* @throws IllegalArgumentException if the resource already exists.
*/
private fun StringResource.include() {
if (strings.any { it.name == name }) return
strings.add(this)
private fun IResource.include() {
when(this) {
is StringResource -> {
if (strings.any { it.name == name }) return
strings.add(this)
}
is ArrayResource -> addArray(this)
else -> throw NotImplementedError("Unsupported resource type")
}
}
private fun DomFileEditor?.getNode(tagName: String) = this!!.file.getElementsByTagName(tagName).item(0)
private fun Node?.createElement(tagName: String) = this!!.ownerDocument.createElement(tagName)
private enum class SummaryType(val type: String) {
DEFAULT("summary"), ON("summaryOn"), OFF("summaryOff")
}
}
override fun close() {
// merge all strings, skip duplicates
strings.forEach { stringResource ->
stringsNode!!.appendChild(stringsNode!!.ownerDocument.createElement("string").also { stringElement ->
stringElement.setAttribute("name", stringResource.name)
// if the string is un-formatted, explicitly add the formatted attribute
if (!stringResource.formatted) stringElement.setAttribute("formatted", "false")
stringElement.textContent = stringResource.value
})
strings.forEach {
stringsNode!!.addResource(it)
}
// rename the intent package names if it was set