Compare commits

..

3 Commits

Author SHA1 Message Date
semantic-release-bot
4935c00aa5 chore(release): 2.127.0 [skip ci]
# [2.127.0](https://github.com/revanced/revanced-patches/compare/v2.126.1...v2.127.0) (2022-11-28)

### Features

* **twitch:** `settings` patch ([#1075](https://github.com/revanced/revanced-patches/issues/1075)) ([632a083](06098713bb))
2022-11-28 22:52:01 +00:00
Tim Schneeberger
06098713bb feat(twitch): settings patch (#1075)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-11-28 23:50:04 +01:00
Tim Schneeberger
eb505d802a refactor: abstract settings patch (#1109)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-11-28 20:13:34 +01:00
75 changed files with 736 additions and 270 deletions

View File

@@ -1,3 +1,10 @@
# [2.127.0](https://github.com/revanced/revanced-patches/compare/v2.126.1...v2.127.0) (2022-11-28)
### Features
* **twitch:** `settings` patch ([#1075](https://github.com/revanced/revanced-patches/issues/1075)) ([6e95b86](https://github.com/revanced/revanced-patches/commit/6e95b86e50cb09b60d82b456d4650218436ed154))
## [2.126.1](https://github.com/revanced/revanced-patches/compare/v2.126.0...v2.126.1) (2022-11-28)

View File

@@ -182,6 +182,7 @@ The official Patch bundle provided by ReVanced and the community.
| `debug-mode` | Enables Twitch's internal debugging mode. | all |
| `block-audio-ads` | Blocks audio ads in streams and VODs. | all |
| `block-video-ads` | Blocks video ads in streams and VODs. | all |
| `settings` | Adds settings menu to Twitch. | all |
| `show-deleted-messages` | Shows deleted chat messages behind a clickable spoiler. | all |
</details>

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 2.126.1
version = 2.127.0

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.shared.settings
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.shared.settings
package app.revanced.patches.shared.settings.preference
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.shared.settings
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Element
import org.w3c.dom.Node

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.shared.settings
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.StringResource
/**
* Preference

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.shared.settings
package app.revanced.patches.shared.settings.preference
/**
* Resource

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.shared.settings
package app.revanced.patches.shared.settings.preference
enum class SummaryType(val type: String) {
DEFAULT("summary"), ON("summaryOn"), OFF("summaryOff")

View File

@@ -1,7 +1,7 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BaseResource
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.preference.BaseResource
import app.revanced.patches.shared.settings.preference.IResource
import org.w3c.dom.Document
import org.w3c.dom.Element
@@ -19,8 +19,10 @@ internal data class ArrayResource(
override fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)?): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("name", name)
items.forEach { item ->
setAttribute("name", item.also { resourceCallback?.invoke(it) }.name)
resourceCallback?.invoke(item)
this.appendChild(ownerDocument.createElement("item").also { itemNode ->
itemNode.textContent = item.value

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
enum class InputType(val type: String) {
STRING("text"),

View File

@@ -1,9 +1,9 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.addDefault
import app.revanced.patches.shared.settings.addSummary
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addDefault
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.addSummary
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -1,7 +1,7 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import org.w3c.dom.Document
import org.w3c.dom.Element
@@ -15,7 +15,7 @@ import org.w3c.dom.Element
internal open class PreferenceCategory(
key: String,
title: StringResource,
val preferences: List<BasePreference>
var preferences: List<BasePreference>
) : BasePreference(key, title) {
override val tag: String = "PreferenceCategory"

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.addSummary
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element
@@ -17,7 +17,7 @@ import org.w3c.dom.Element
internal open class PreferenceScreen(
key: String,
title: StringResource,
val preferences: List<BasePreference>,
var preferences: List<BasePreference>,
val summary: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "PreferenceScreen"

View File

@@ -1,7 +1,7 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BaseResource
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.preference.BaseResource
import app.revanced.patches.shared.settings.preference.IResource
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -1,10 +1,10 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.*
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.addDefault
import app.revanced.patches.shared.settings.addSummary
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addDefault
import app.revanced.patches.shared.settings.preference.addSummary
import app.revanced.patches.shared.settings.preference.SummaryType
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -1,9 +1,9 @@
package app.revanced.patches.shared.settings.impl
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.BasePreference
import app.revanced.patches.shared.settings.IResource
import app.revanced.patches.shared.settings.addDefault
import app.revanced.patches.shared.settings.addSummary
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addDefault
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -0,0 +1,135 @@
package app.revanced.patches.shared.settings.resource.patch
import app.revanced.patcher.data.DomFileEditor
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addPreference
import app.revanced.patches.shared.settings.preference.addResource
import app.revanced.patches.shared.settings.preference.impl.ArrayResource
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.util.resources.ResourceUtils
import app.revanced.util.resources.ResourceUtils.copyResources
import org.w3c.dom.Node
/**
* Abstract settings resource patch
*
* @param preferenceFileName Name of the settings preference xml file
* @param sourceDirectory Source directory to copy the preference template from
*/
abstract class AbstractSettingsResourcePatch(
private val preferenceFileName: String,
private val sourceDirectory: String,
) : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
/*
* used for self-restart
*/
context.xmlEditor["AndroidManifest.xml"].use { editor ->
editor.file.getElementsByTagName("manifest").item(0).also {
it.appendChild(it.ownerDocument.createElement("uses-permission").also { element ->
element.setAttribute("android:name", "android.permission.SCHEDULE_EXACT_ALARM")
})
}
}
/* copy preference template from source dir */
context.copyResources(
sourceDirectory,
ResourceUtils.ResourceGroup(
"xml", "$preferenceFileName.xml"
)
)
/* prepare xml editors */
stringsEditor = context.xmlEditor["res/values/strings.xml"]
arraysEditor = context.xmlEditor["res/values/arrays.xml"]
revancedPreferencesEditor = context.xmlEditor["res/xml/$preferenceFileName.xml"]
return PatchResultSuccess()
}
internal companion object {
private var revancedPreferenceNode: Node? = null
private var stringsNode: Node? = null
private var arraysNode: Node? = null
private var strings = mutableListOf<StringResource>()
private var revancedPreferencesEditor: DomFileEditor? = null
set(value) {
field = value
revancedPreferenceNode = value.getNode("PreferenceScreen")
}
private var stringsEditor: DomFileEditor? = null
set(value) {
field = value
stringsNode = value.getNode("resources")
}
private var arraysEditor: DomFileEditor? = null
set(value) {
field = value
arraysNode = value.getNode("resources")
}
/**
* Add a new string to the resources.
*
* @param identifier The key of the string.
* @param value The value of the string.
* @throws IllegalArgumentException if the string already exists.
*/
fun addString(identifier: String, value: String, formatted: Boolean) =
StringResource(identifier, value, formatted).include()
/**
* Add an array to the resources.
*
* @param arrayResource The array resource to add.
*/
fun addArray(arrayResource: ArrayResource) =
arraysNode!!.addResource(arrayResource)
/**
* Add a preference to the settings.
*
* @param preference The preference to add.
*/
fun addPreference(preference: BasePreference) =
revancedPreferenceNode!!.addPreference(preference) { it.include() }
/**
* Add a new resource to the resources.
*
* @throws IllegalArgumentException if the resource already exists.
*/
internal 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")
}
}
internal fun DomFileEditor?.getNode(tagName: String) = this!!.file.getElementsByTagName(tagName).item(0)
}
override fun close() {
// merge all strings, skip duplicates
strings.forEach {
stringsNode!!.addResource(it)
}
revancedPreferencesEditor?.close()
stringsEditor?.close()
arraysEditor?.close()
}
}

View File

@@ -0,0 +1,89 @@
package app.revanced.patches.shared.settings.util
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.impl.PreferenceCategory
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import java.io.Closeable
internal abstract class AbstractPreferenceScreen(
private val root: MutableList<Screen> = mutableListOf()
) : Closeable {
override fun close() {
if (root.isEmpty())
return
for (preference in root.sortedBy { it.title }) {
commit(preference.transform())
}
}
/**
* Finalize and insert root preference into resource patch
*/
abstract fun commit(screen: PreferenceScreen)
open inner class Screen(
key: String,
title: String,
val summary: String? = null,
preferences: MutableList<BasePreference> = mutableListOf(),
val categories: MutableList<Category> = mutableListOf()
) : BasePreferenceCollection(key, title, preferences) {
override fun transform(): PreferenceScreen {
return PreferenceScreen(
key,
StringResource("${key}_title", title),
preferences.sortedBy { it.title.value } +
categories.sortedBy { it.title }.map { it.transform() },
summary?.let { summary ->
StringResource("${key}_summary", summary)
}
)
}
private fun ensureScreenInserted() {
// Add to screens if not yet done
if(!this@AbstractPreferenceScreen.root.contains(this))
this@AbstractPreferenceScreen.root.add(this)
}
fun addPreferences(vararg preferences: BasePreference) {
ensureScreenInserted()
this.preferences.addAll(preferences)
}
open inner class Category(
key: String,
title: String,
preferences: MutableList<BasePreference> = mutableListOf()
): BasePreferenceCollection(key, title, preferences) {
override fun transform(): PreferenceCategory {
return PreferenceCategory(
key,
StringResource("${key}_title", title),
preferences.sortedBy { it.title.value }
)
}
fun addPreferences(vararg preferences: BasePreference) {
ensureScreenInserted()
// Add to categories if not yet done
if(!this@Screen.categories.contains(this))
this@Screen.categories.add(this)
this.preferences.addAll(preferences)
}
}
}
abstract class BasePreferenceCollection(
val key: String,
val title: String,
val preferences: MutableList<BasePreference> = mutableListOf()
) {
abstract fun transform(): BasePreference
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.twitch.misc.settings.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("tv.twitch.android.app")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SettingsCompatibility

View File

@@ -0,0 +1,195 @@
package app.revanced.patches.twitch.misc.settings.bytecode.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.*
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult
import app.revanced.patcher.patch.BytecodePatch
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.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.settings.preference.impl.PreferenceCategory
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.util.AbstractPreferenceScreen
import app.revanced.patches.twitch.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.twitch.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.twitch.misc.settings.components.CustomPreferenceCategory
import app.revanced.patches.twitch.misc.settings.fingerprints.*
import app.revanced.patches.twitch.misc.settings.resource.patch.SettingsResourcePatch
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.immutable.ImmutableField
@Patch
@DependsOn([IntegrationsPatch::class, SettingsResourcePatch::class])
@Name("settings")
@Description("Adds settings menu to Twitch.")
@SettingsCompatibility
@Version("0.0.1")
class SettingsPatch : BytecodePatch(
listOf(
SettingsActivityOnCreateFingerprint,
SettingsMenuItemEnumFingerprint,
MenuGroupsUpdatedFingerprint,
MenuGroupsOnClickFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
// Hook onCreate to handle fragment creation
with(SettingsActivityOnCreateFingerprint.result!!) {
val insertIndex = mutableMethod.implementation!!.instructions.size - 2
mutableMethod.addInstructions(
insertIndex,
"""
invoke-static {p0}, $SETTINGS_HOOKS_CLASS->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z
move-result v0
if-eqz v0, :no_rv_settings_init
return-void
""",
listOf(ExternalLabel("no_rv_settings_init", mutableMethod.instruction(insertIndex)))
)
}
// Create new menu item for settings menu
with(SettingsMenuItemEnumFingerprint.result!!) {
injectMenuItem(
REVANCED_SETTINGS_MENU_ITEM_NAME,
REVANCED_SETTINGS_MENU_ITEM_ID,
REVANCED_SETTINGS_MENU_ITEM_TITLE_RES,
REVANCED_SETTINGS_MENU_ITEM_ICON_RES
)
}
// Intercept settings menu creation and add new menu item
with(MenuGroupsUpdatedFingerprint.result!!) {
mutableMethod.addInstructions(
0,
"""
sget-object v0, $MENU_ITEM_ENUM_CLASS->$REVANCED_SETTINGS_MENU_ITEM_NAME:$MENU_ITEM_ENUM_CLASS
invoke-static {p1, v0}, $SETTINGS_HOOKS_CLASS->handleSettingMenuCreation(Ljava/util/List;Ljava/lang/Object;)Ljava/util/List;
move-result-object p1
"""
)
}
// Intercept onclick events for the settings menu
with(MenuGroupsOnClickFingerprint.result!!) {
val insertIndex = 0
mutableMethod.addInstructions(
insertIndex,
"""
invoke-static {p1}, $SETTINGS_HOOKS_CLASS->handleSettingMenuOnClick(Ljava/lang/Enum;)Z
move-result p2
if-eqz p2, :no_rv_settings_onclick
sget-object p1, $MENU_DISMISS_EVENT_CLASS->INSTANCE:$MENU_DISMISS_EVENT_CLASS
invoke-virtual {p0, p1}, Ltv/twitch/android/core/mvp/viewdelegate/RxViewDelegate;->pushEvent(Ltv/twitch/android/core/mvp/viewdelegate/ViewDelegateEvent;)V
return-void
""",
listOf(ExternalLabel("no_rv_settings_onclick", mutableMethod.instruction(insertIndex)))
)
}
addString("revanced_settings", "ReVanced Settings", false)
addString("revanced_reboot_message", "Twitch needs to restart to apply your changes. Restart now?", false)
addString("revanced_reboot", "Restart", false)
addString("revanced_cancel", "Cancel", false)
return PatchResultSuccess()
}
internal companion object {
fun addString(identifier: String, value: String, formatted: Boolean = true) =
SettingsResourcePatch.addString(identifier, value, formatted)
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) =
SettingsResourcePatch.addPreferenceScreen(preferenceScreen)
/* Private members */
private const val REVANCED_SETTINGS_MENU_ITEM_NAME = "RevancedSettings"
private const val REVANCED_SETTINGS_MENU_ITEM_ID = 0x7
private const val REVANCED_SETTINGS_MENU_ITEM_TITLE_RES = "revanced_settings"
private const val REVANCED_SETTINGS_MENU_ITEM_ICON_RES = "ic_settings"
private const val MENU_ITEM_ENUM_CLASS = "Ltv/twitch/android/feature/settings/menu/SettingsMenuItem;"
private const val MENU_DISMISS_EVENT_CLASS = "Ltv/twitch/android/feature/settings/menu/SettingsMenuViewDelegate\$Event\$OnDismissClicked;"
private const val INTEGRATIONS_PACKAGE = "app/revanced/twitch"
private const val SETTINGS_HOOKS_CLASS = "L$INTEGRATIONS_PACKAGE/settingsmenu/SettingsHooks;"
private const val REVANCED_UTILS_CLASS = "L$INTEGRATIONS_PACKAGE/utils/ReVancedUtils;"
private fun MethodFingerprintResult.injectMenuItem(
name: String,
value: Int,
titleResourceName: String,
iconResourceName: String
) {
// Add new static enum member field
mutableClass.staticFields.add(
ImmutableField(
mutableMethod.definingClass,
name,
MENU_ITEM_ENUM_CLASS,
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.ENUM or AccessFlags.STATIC,
null,
null,
null
).toMutable()
)
// Add initializer for the new enum member
mutableMethod.addInstructions(
mutableMethod.implementation!!.instructions.size - 4,
"""
new-instance v0, $MENU_ITEM_ENUM_CLASS
const-string v1, "$titleResourceName"
invoke-static {v1}, $REVANCED_UTILS_CLASS->getStringId(Ljava/lang/String;)I
move-result v1
const-string v3, "$iconResourceName"
invoke-static {v3}, $REVANCED_UTILS_CLASS->getDrawableId(Ljava/lang/String;)I
move-result v3
const-string v4, "$name"
const/4 v5, $value
invoke-direct {v0, v4, v5, v1, v3}, $MENU_ITEM_ENUM_CLASS-><init>(Ljava/lang/String;III)V
sput-object v0, $MENU_ITEM_ENUM_CLASS->$name:$MENU_ITEM_ENUM_CLASS
"""
)
}
}
/**
* Preference screens patches should add their settings to.
*/
internal object PreferenceScreen : AbstractPreferenceScreen() {
val ADS = CustomScreen("ads", "Ads", "Ad blocking settings")
val CHAT = CustomScreen("chat", "Chat", "Chat settings")
val MISC = CustomScreen("misc", "Misc", "Miscellaneous patches")
internal class CustomScreen(key: String, title: String, summary: String) : Screen(key, title, summary) {
/* Categories */
val GENERAL = CustomCategory("general", "General settings")
val OTHER = CustomCategory("other", "Other settings")
val CLIENT_SIDE = CustomCategory("client_ads", "Client-side ads")
internal inner class CustomCategory(key: String, title: String) : Screen.Category(key, title) {
/* For Twitch, we need to load our CustomPreferenceCategory class instead of the default one. */
override fun transform(): PreferenceCategory {
return CustomPreferenceCategory(
key,
StringResource("${key}_title", title),
preferences.sortedBy { it.title.value }
)
}
}
}
override fun commit(screen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) {
addPreferenceScreen(screen)
}
}
override fun close() = PreferenceScreen.close()
}

View File

@@ -0,0 +1,20 @@
package app.revanced.patches.twitch.misc.settings.components
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.impl.PreferenceCategory
import app.revanced.patches.shared.settings.preference.impl.StringResource
/**
* Customized preference category for Twitch.
*
* @param key The key of the preference.
* @param title The title of the preference.
* @param preferences Child preferences of this category.
*/
internal open class CustomPreferenceCategory(
key: String,
title: StringResource,
preferences: List<BasePreference>
) : PreferenceCategory(key, title, preferences) {
override val tag: String = "app.revanced.twitch.settingsmenu.preference.CustomPreferenceCategory"
}

View File

@@ -0,0 +1,15 @@
package app.revanced.patches.twitch.misc.settings.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object MenuGroupsOnClickFingerprint : MethodFingerprint(
"V",
AccessFlags.PRIVATE or AccessFlags.STATIC or AccessFlags.FINAL,
listOf("L", "L", "L"),
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("/SettingsMenuViewDelegate;")
&& methodDef.name.contains("render")
}
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.twitch.misc.settings.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object MenuGroupsUpdatedFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("/SettingsMenuPresenter\$Event\$MenuGroupsUpdated;")
&& methodDef.name == "<init>"
}
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.twitch.misc.settings.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SettingsActivityOnCreateFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("/SettingsActivity;") &&
methodDef.name == "onCreate"
}
)

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.twitch.misc.settings.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SettingsMenuItemEnumFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("/SettingsMenuItem;") && methodDef.name == "<clinit>"
}
)

View File

@@ -0,0 +1,44 @@
package app.revanced.patches.twitch.misc.settings.resource.patch
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patches.shared.settings.preference.impl.ArrayResource
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.resource.patch.AbstractSettingsResourcePatch
import app.revanced.patches.twitch.misc.settings.annotations.SettingsCompatibility
@Name("settings-resource-patch")
@SettingsCompatibility
@Version("0.0.1")
class SettingsResourcePatch : AbstractSettingsResourcePatch(
"revanced_prefs",
"twitch/settings"
) {
internal companion object {
/* Companion delegates */
/**
* Add a new string to the resources.
*
* @param identifier The key of the string.
* @param value The value of the string.
* @throws IllegalArgumentException if the string already exists.
*/
fun addString(identifier: String, value: String, formatted: Boolean) =
AbstractSettingsResourcePatch.addString(identifier, value, formatted)
/**
* Add an array to the resources.
*
* @param arrayResource The array resource to add.
*/
fun addArray(arrayResource: ArrayResource) = AbstractSettingsResourcePatch.addArray(arrayResource)
/**
* Add a preference to the settings.
*
* @param preferenceScreen The name of the preference screen.
*/
fun addPreferenceScreen(preferenceScreen: PreferenceScreen) = AbstractSettingsResourcePatch.addPreference(preferenceScreen)
}
}

View File

@@ -7,10 +7,10 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.shared.settings.impl.InputType
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.impl.TextPreference
import app.revanced.patches.shared.settings.preference.impl.InputType
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.TextPreference
import app.revanced.patches.youtube.ad.general.annotation.GeneralAdsCompatibility
import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
@@ -187,7 +187,7 @@ class GeneralAdsResourcePatch : ResourcePatch {
"Chapter teasers are shown"
)
),
app.revanced.patches.shared.settings.impl.PreferenceScreen(
app.revanced.patches.shared.settings.preference.impl.PreferenceScreen(
"revanced_adremover_custom",
StringResource("revanced_adremover_custom_title", "Custom filter"),
listOf(

View File

@@ -17,8 +17,8 @@ import app.revanced.patches.youtube.ad.video.fingerprints.LoadVideoAdsFingerprin
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.playback.fix.patch.FixPlaybackPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class, FixPlaybackPatch::class])

View File

@@ -8,10 +8,11 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.shared.settings.impl.*
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.impl.TextPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.TextPreference
import app.revanced.patches.shared.settings.preference.impl.InputType
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.youtube.interaction.downloads.annotation.DownloadsCompatibility
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.youtube.misc.playercontrols.resource.patch.BottomControlsResourcePatch

View File

@@ -16,8 +16,8 @@ import app.revanced.patches.youtube.interaction.seekbar.fingerprints.SeekbarTapp
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.SeekbarTappingParentFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
import org.jf.dexlib2.iface.Method

View File

@@ -7,11 +7,11 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.shared.settings.impl.*
import app.revanced.patches.shared.settings.impl.PreferenceScreen
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.impl.TextPreference
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.TextPreference
import app.revanced.patches.shared.settings.preference.impl.InputType
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.util.resources.ResourceUtils

View File

@@ -16,8 +16,8 @@ import app.revanced.patches.youtube.layout.autocaptions.fingerprints.SubtitleBut
import app.revanced.patches.youtube.layout.autocaptions.fingerprints.SubtitleTrackFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -17,8 +17,8 @@ import app.revanced.patches.youtube.layout.autoplaybutton.fingerprints.LayoutCon
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction

View File

@@ -13,9 +13,9 @@ import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch
import app.revanced.patches.youtube.layout.buttons.annotations.HideButtonsCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.PreferenceScreen
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([ResourceMappingPatch::class, LithoFilterPatch::class])

View File

@@ -14,8 +14,8 @@ import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.youtube.layout.castbutton.annotations.CastButtonCompatibility
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -10,9 +10,9 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.comments.annotations.CommentsCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.PreferenceScreen
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Name("comments-resource-patch")
@CommentsCompatibility

View File

@@ -18,8 +18,8 @@ import app.revanced.patches.youtube.layout.fullscreenpanels.fingerprints.Fullscr
import app.revanced.patches.youtube.layout.fullscreenpanels.fingerprints.FullscreenViewAdderParentFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@Name("disable-fullscreen-panels")

View File

@@ -10,8 +10,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.hidealbumcards.annotations.AlbumCardsCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Name("hide-album-cards-resource-patch")
@AlbumCardsCompatibility

View File

@@ -13,8 +13,8 @@ import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch
import app.revanced.patches.youtube.layout.buttons.annotations.HideArtistCardCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([ResourceMappingPatch::class, LithoFilterPatch::class])

View File

@@ -14,8 +14,8 @@ import app.revanced.patches.youtube.layout.autocaptions.annotations.AutoCaptions
import app.revanced.patches.youtube.layout.autocaptions.fingerprints.SubtitleButtonControllerFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.Opcode
@Patch

View File

@@ -10,8 +10,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.hidecrowdfundingbox.annotations.CrowdfundingBoxCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Name("crowdfunding-box-resource-patch")
@CrowdfundingBoxCompatibility

View File

@@ -10,8 +10,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.hideendscreencards.annotations.HideEndscreenCardsCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Name("hide-endscreen-cards-resource-patch")
@HideEndscreenCardsCompatibility

View File

@@ -9,8 +9,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.hideinfocards.annotations.HideInfocardsCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@HideInfocardsCompatibility
@DependsOn([SettingsPatch::class, ResourceMappingPatch::class])

View File

@@ -17,8 +17,8 @@ import app.revanced.patches.youtube.layout.hidemixplaylists.fingerprints.CreateM
import app.revanced.patches.youtube.layout.hidemixplaylists.fingerprints.SecondCreateMixPlaylistFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch

View File

@@ -15,8 +15,8 @@ import app.revanced.patches.youtube.layout.hidetimeandseekbar.fingerprints.TimeC
import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.CreateVideoPlayerSeekbarFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -14,8 +14,8 @@ import app.revanced.patches.youtube.layout.oldqualitylayout.annotations.OldQuali
import app.revanced.patches.youtube.layout.oldqualitylayout.fingerprints.QualityMenuViewInflateFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction
@Patch

View File

@@ -10,8 +10,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.personalinformation.annotations.HideEmailAddressCompatibility
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Name("hide-email-address-resource-patch")
@HideEmailAddressCompatibility

View File

@@ -20,8 +20,8 @@ import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.injectH
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, ResourceMappingPatch::class, SettingsPatch::class])

View File

@@ -20,8 +20,8 @@ import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.REGISTE
import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.injectHook
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -14,8 +14,8 @@ import app.revanced.patches.youtube.layout.playerpopuppanels.annotations.PlayerP
import app.revanced.patches.youtube.layout.playerpopuppanels.fingerprints.EngagementPanelControllerFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -12,8 +12,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.reels.annotations.HideReelsCompatibility
import app.revanced.patches.youtube.layout.reels.fingerprints.HideReelsFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
//@Patch TODO: this is currently in the general-bytecode-ads patch due to the integrations having a preference for including reels or not. Move it here.
@Name("hide-reels")

View File

@@ -11,8 +11,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.youtube.layout.returnyoutubedislike.annotations.ReturnYouTubeDislikeCompatibility
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.Preference
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.util.resources.ResourceUtils.Settings.mergeStrings
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsPatch::class])

View File

@@ -11,8 +11,8 @@ import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlock
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.Preference
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.util.resources.ResourceUtils
import app.revanced.util.resources.ResourceUtils.Settings.mergeStrings
import app.revanced.util.resources.ResourceUtils.copyResources

View File

@@ -14,8 +14,8 @@ import app.revanced.patches.youtube.layout.startupshortsreset.annotations.Startu
import app.revanced.patches.youtube.layout.startupshortsreset.fingerprints.UserWasInShortsFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -20,8 +20,8 @@ import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPla
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerResponseModelSizeCheckFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch

View File

@@ -14,8 +14,8 @@ import app.revanced.patches.youtube.layout.watchinvr.annotations.WatchinVRCompat
import app.revanced.patches.youtube.layout.watchinvr.fingerprints.WatchinVRFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -18,8 +18,8 @@ import app.revanced.patches.youtube.layout.watermark.fingerprints.HideWatermarkF
import app.revanced.patches.youtube.layout.watermark.fingerprints.HideWatermarkParentFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -20,8 +20,8 @@ import app.revanced.patches.youtube.layout.widesearchbar.fingerprints.WideSearch
import app.revanced.patches.youtube.layout.widesearchbar.fingerprints.WideSearchbarTwoParentFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])

View File

@@ -18,8 +18,8 @@ import app.revanced.patches.youtube.misc.autorepeat.fingerprints.AutoRepeatFinge
import app.revanced.patches.youtube.misc.autorepeat.fingerprints.AutoRepeatParentFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
@Patch
@DependsOn([IntegrationsPatch::class])

View File

@@ -10,8 +10,8 @@ import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.youtube.misc.debugging.annotations.DebuggingCompatibility
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.w3c.dom.Element
@Patch

View File

@@ -14,8 +14,8 @@ import app.revanced.patches.youtube.misc.hdrbrightness.annotations.HDRBrightness
import app.revanced.patches.youtube.misc.hdrbrightness.fingerprints.HDRBrightnessFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
import org.jf.dexlib2.iface.reference.FieldReference

View File

@@ -16,8 +16,8 @@ import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAG
import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.Preference
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
import app.revanced.util.microg.Constants.MICROG_VENDOR
import app.revanced.util.microg.MicroGManifestHelper

View File

@@ -18,8 +18,8 @@ import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.Minimize
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.MethodReference

View File

@@ -16,8 +16,8 @@ import app.revanced.patches.youtube.misc.openlinksdirectly.annotations.OpenLinks
import app.revanced.patches.youtube.misc.openlinksdirectly.fingerprints.OpenLinksDirectlyFingerprintPrimary
import app.revanced.patches.youtube.misc.openlinksdirectly.fingerprints.OpenLinksDirectlyFingerprintSecondary
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
import org.jf.dexlib2.iface.instruction.formats.Instruction35c

View File

@@ -12,8 +12,8 @@ import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.playback.fix.annotations.FixPlaybackCompatibility
import app.revanced.patches.youtube.misc.video.information.patch.VideoInformationPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.youtube.misc.video.videoid.patch.VideoIdPatch
@DependsOn([

View File

@@ -11,10 +11,8 @@ 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.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.util.AbstractPreferenceScreen
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
@@ -22,7 +20,6 @@ import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSet
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterSystemFingerprint
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
import org.jf.dexlib2.util.MethodUtil
import java.io.Closeable
@Patch
@DependsOn(
@@ -134,7 +131,7 @@ class SettingsPatch : BytecodePatch(
fun addString(identifier: String, value: String, formatted: Boolean = true) =
SettingsResourcePatch.addString(identifier, value, formatted)
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.shared.settings.impl.PreferenceScreen) =
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) =
SettingsResourcePatch.addPreferenceScreen(preferenceScreen)
fun addPreference(preference: Preference) =
@@ -148,38 +145,16 @@ class SettingsPatch : BytecodePatch(
/**
* Preference screens patches should add their settings to.
*/
internal enum class PreferenceScreen(
private val key: String,
private val title: String,
private val summary: String? = null,
private val preferences: MutableList<BasePreference> = mutableListOf()
) : Closeable {
ADS("ads", "Ads", "Ad related settings"),
INTERACTIONS("interactions", "Interaction", "Settings related to interactions"),
LAYOUT("layout", "Layout", "Settings related to the layout"),
MISC("misc", "Miscellaneous", "Miscellaneous patches");
internal object PreferenceScreen : AbstractPreferenceScreen() {
val ADS = Screen("ads", "Ads", "Ad related settings")
val INTERACTIONS = Screen("interactions", "Interaction", "Settings related to interactions")
val LAYOUT = Screen("layout", "Layout", "Settings related to the layout")
val MISC = Screen("misc", "Misc", "Miscellaneous patches")
override fun close() {
if (preferences.size == 0) return
addPreferenceScreen(
PreferenceScreen(
key,
StringResource("${key}_title", title),
preferences,
summary?.let { summary ->
StringResource("${key}_summary", summary)
}
)
)
override fun commit(screen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) {
addPreferenceScreen(screen)
}
/**
* Add preferences to the preference screen.
*/
fun addPreferences(vararg preferences: BasePreference) = this.preferences.addAll(preferences)
}
override fun close() = PreferenceScreen.values().forEach(PreferenceScreen::close)
override fun close() = PreferenceScreen.close()
}

View File

@@ -1,34 +1,38 @@
package app.revanced.patches.youtube.misc.settings.resource.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.DomFileEditor
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
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.misc.patch.ResourceMappingPatch
import app.revanced.patches.shared.settings.preference.addPreference
import app.revanced.patches.shared.settings.preference.impl.ArrayResource
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.resource.patch.AbstractSettingsResourcePatch
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
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.Node
@Name("settings-resource-patch")
@DependsOn([ResourceMappingPatch::class])
@SettingsCompatibility
@DependsOn([FixLocaleConfigErrorPatch::class, ResourceMappingPatch::class])
@Description("Applies mandatory patches to implement ReVanced settings into the application.")
@Version("0.0.1")
class SettingsResourcePatch : ResourcePatch {
class SettingsResourcePatch : AbstractSettingsResourcePatch(
"revanced_prefs",
"settings"
) {
override fun execute(context: ResourceContext): PatchResult {
super.execute(context)
/*
* used by a fingerprint of SettingsPatch
*/
@@ -50,8 +54,6 @@ class SettingsResourcePatch : ResourcePatch {
"revanced_settings_toolbar.xml",
"revanced_settings_with_toolbar.xml",
"revanced_settings_with_toolbar_layout.xml"
), ResourceUtils.ResourceGroup(
"xml", "revanced_prefs.xml" // template for new preferences
), ResourceUtils.ResourceGroup(
// required resource for back button, because when the base APK is used, this resource will not exist
"drawable-xxxhdpi", "quantum_ic_arrow_back_white_24.png"
@@ -63,20 +65,8 @@ class SettingsResourcePatch : ResourcePatch {
context.copyResources("settings", resourceGroup)
}
context.xmlEditor["AndroidManifest.xml"].use { editor ->
editor.file.getElementsByTagName("manifest").item(0).also {
it.appendChild(it.ownerDocument.createElement("uses-permission").also { element ->
element.setAttribute("android:name", "android.permission.SCHEDULE_EXACT_ALARM")
})
}
}
revancedPreferencesEditor = context.xmlEditor["res/xml/revanced_prefs.xml"]
preferencesEditor = context.xmlEditor["res/xml/settings_fragment.xml"]
stringsEditor = context.xmlEditor["res/values/strings.xml"]
arraysEditor = context.xmlEditor["res/values/arrays.xml"]
// Add the ReVanced settings to the YouTube settings
val youtubePackage = "com.google.android.youtube"
SettingsPatch.addPreference(
@@ -103,35 +93,23 @@ class SettingsResourcePatch : ResourcePatch {
// if this is not null, all intents will be renamed to this
var overrideIntentsTargetPackage: String? = null
private var revancedPreferenceNode: Node? = null
private var preferencesNode: Node? = null
private var stringsNode: Node? = null
private var arraysNode: Node? = null
private var strings = mutableListOf<StringResource>()
private var revancedPreferencesEditor: DomFileEditor? = null
set(value) {
field = value
revancedPreferenceNode = value.getNode("PreferenceScreen")
}
private var preferencesEditor: DomFileEditor? = null
set(value) {
field = value
preferencesNode = value.getNode("PreferenceScreen")
}
private var stringsEditor: DomFileEditor? = null
set(value) {
field = value
stringsNode = value.getNode("resources")
}
private var arraysEditor: DomFileEditor? = null
set(value) {
field = value
arraysNode = value.getNode("resources")
}
/* Companion delegates */
/**
* Add a preference fragment to the main preferences.
*
* @param preference The preference to add.
*/
fun addPreference(preference: Preference) =
preferencesNode!!.addPreference(preference) { it.include() }
/**
* Add a new string to the resources.
@@ -141,60 +119,25 @@ class SettingsResourcePatch : ResourcePatch {
* @throws IllegalArgumentException if the string already exists.
*/
fun addString(identifier: String, value: String, formatted: Boolean) =
StringResource(identifier, value, formatted).include()
AbstractSettingsResourcePatch.addString(identifier, value, formatted)
/**
* Add an array to the resources.
*
* @param arrayResource The array resource to add.
*/
fun addArray(arrayResource: ArrayResource) {
arraysNode!!.appendChild(arrayResource.serialize(arraysNode!!.ownerDocument) { it.include() })
}
fun addArray(arrayResource: ArrayResource) = AbstractSettingsResourcePatch.addArray(arrayResource)
/**
* Add a preference screen to the settings.
* Add a preference to the settings.
*
* @param preferenceScreen The name of the preference screen.
*/
fun addPreferenceScreen(preferenceScreen: PreferenceScreen) =
revancedPreferenceNode!!.addPreference(preferenceScreen) { it.include() }
/**
* Add a preference fragment to the preferences.
*
* @param preference The preference to add.
*/
fun addPreference(preference: Preference) {
preferencesNode!!.addPreference(preference) { it.include() }
}
/**
* Add a new resource to the resources.
*
* @throws IllegalArgumentException if the resource already exists.
*/
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)
fun addPreferenceScreen(preferenceScreen: PreferenceScreen) = addPreference(preferenceScreen)
}
override fun close() {
// merge all strings, skip duplicates
strings.forEach {
stringsNode!!.addResource(it)
}
super.close()
// rename the intent package names if it was set
overrideIntentsTargetPackage?.let { packageName ->
@@ -221,9 +164,6 @@ class SettingsResourcePatch : ResourcePatch {
}
}
revancedPreferencesEditor?.close()
preferencesEditor?.close()
stringsEditor?.close()
arraysEditor?.close()
}
}

View File

@@ -18,8 +18,8 @@ import app.revanced.patches.youtube.misc.video.quality.fingerprints.VideoQuality
import app.revanced.patches.youtube.misc.video.quality.fingerprints.VideoQualitySetterFingerprint
import app.revanced.patches.youtube.misc.video.quality.fingerprints.VideoUserQualityChangeFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.youtube.misc.video.videoid.patch.VideoIdPatch
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.FieldReference

View File

@@ -14,10 +14,10 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.InputType
import app.revanced.patches.shared.settings.impl.PreferenceScreen
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.TextPreference
import app.revanced.patches.shared.settings.preference.impl.InputType
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.TextPreference
import app.revanced.patches.youtube.misc.videobuffer.annotations.CustomVideoBufferCompatibility
import app.revanced.patches.youtube.misc.videobuffer.fingerprints.MaxBufferFingerprint
import app.revanced.patches.youtube.misc.videobuffer.fingerprints.PlaybackBufferFingerprint

View File

@@ -13,8 +13,8 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.impl.SwitchPreference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.youtube.misc.zoomhaptics.annotations.ZoomHapticsCompatibility
import app.revanced.patches.youtube.misc.zoomhaptics.fingerprints.ZoomHapticsFingerprint

View File

@@ -3,7 +3,7 @@ package app.revanced.util.resources
import app.revanced.patcher.data.DomFileEditor
import app.revanced.patcher.data.ResourceContext
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.shared.settings.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Node
import java.nio.file.Files
import java.nio.file.StandardCopyOption

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
</PreferenceScreen>