mirror of
https://github.com/revanced/revanced-patches
synced 2024-11-19 04:29:22 +01:00
feat(YouTube): Add 'About' preference to settings menu (#2981)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
c00256dea6
commit
5abf89444a
@ -844,6 +844,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
|
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
|
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public final fun getSelectable ()Z
|
public final fun getSelectable ()Z
|
||||||
|
@ -2,17 +2,21 @@ package app.revanced.patches.shared.misc.integrations
|
|||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint.IRegisterResolver
|
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint.IRegisterResolver
|
||||||
|
import app.revanced.patches.shared.misc.integrations.fingerprints.ReVancedUtilsPatchesVersionFingerprint
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
abstract class BaseIntegrationsPatch(
|
abstract class BaseIntegrationsPatch(
|
||||||
private val hooks: Set<IntegrationsFingerprint>,
|
private val hooks: Set<IntegrationsFingerprint>,
|
||||||
) : BytecodePatch(hooks) {
|
) : BytecodePatch(hooks + setOf(ReVancedUtilsPatchesVersionFingerprint)) {
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the constructor without the integrationsDescriptor parameter",
|
"Use the constructor without the integrationsDescriptor parameter",
|
||||||
@ -34,6 +38,46 @@ abstract class BaseIntegrationsPatch(
|
|||||||
hooks.forEach { hook ->
|
hooks.forEach { hook ->
|
||||||
hook.invoke(INTEGRATIONS_CLASS_DESCRIPTOR)
|
hook.invoke(INTEGRATIONS_CLASS_DESCRIPTOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modify Utils method to include the patches release version version.
|
||||||
|
ReVancedUtilsPatchesVersionFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
val manifestValue = getPatchesManifestEntry("Version")
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const-string v0, "$manifestValue"
|
||||||
|
return-object v0
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The value for the manifest entry,
|
||||||
|
* or "Unknown" if the entry does not exist or is blank.
|
||||||
|
*/
|
||||||
|
@Suppress("SameParameterValue")
|
||||||
|
private fun getPatchesManifestEntry(attributeKey: String) = JarFile(getCurrentJarFilePath()).use { jarFile ->
|
||||||
|
jarFile.manifest.mainAttributes.entries.firstOrNull { it.key.toString() == attributeKey }?.value?.toString()
|
||||||
|
?: "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The file path for the jar this classfile is contained inside.
|
||||||
|
*/
|
||||||
|
private fun getCurrentJarFilePath(): String {
|
||||||
|
val className = object {}::class.java.enclosingClass.name.replace('.', '/') + ".class"
|
||||||
|
val classUrl = object {}::class.java.classLoader.getResource(className)
|
||||||
|
if (classUrl != null) {
|
||||||
|
val urlString = classUrl.toString()
|
||||||
|
|
||||||
|
if (urlString.startsWith("jar:file:")) {
|
||||||
|
val end = urlString.indexOf('!')
|
||||||
|
return urlString.substring("jar:file:".length, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw IllegalStateException("Not running from inside a JAR file.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +94,7 @@ abstract class BaseIntegrationsPatch(
|
|||||||
strings: Iterable<String>? = null,
|
strings: Iterable<String>? = null,
|
||||||
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
|
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
|
||||||
private val insertIndexResolver: ((Method) -> Int) = object : IHookInsertIndexResolver {},
|
private val insertIndexResolver: ((Method) -> Int) = object : IHookInsertIndexResolver {},
|
||||||
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {}
|
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
|
||||||
) : MethodFingerprint(
|
) : MethodFingerprint(
|
||||||
returnType,
|
returnType,
|
||||||
accessFlags,
|
accessFlags,
|
||||||
@ -59,9 +103,11 @@ abstract class BaseIntegrationsPatch(
|
|||||||
strings,
|
strings,
|
||||||
customFingerprint,
|
customFingerprint,
|
||||||
) {
|
) {
|
||||||
@Deprecated("Previous constructor that is missing the insert index." +
|
@Deprecated(
|
||||||
|
"Previous constructor that is missing the insert index." +
|
||||||
"Here only for binary compatibility, " +
|
"Here only for binary compatibility, " +
|
||||||
"and this can be removed after the next major version update.")
|
"and this can be removed after the next major version update.",
|
||||||
|
)
|
||||||
constructor(
|
constructor(
|
||||||
returnType: String? = null,
|
returnType: String? = null,
|
||||||
accessFlags: Int? = null,
|
accessFlags: Int? = null,
|
||||||
@ -69,7 +115,7 @@ abstract class BaseIntegrationsPatch(
|
|||||||
opcodes: Iterable<Opcode?>? = null,
|
opcodes: Iterable<Opcode?>? = null,
|
||||||
strings: Iterable<String>? = null,
|
strings: Iterable<String>? = null,
|
||||||
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
|
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
|
||||||
contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {}
|
contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
|
||||||
) : this(
|
) : this(
|
||||||
returnType,
|
returnType,
|
||||||
accessFlags,
|
accessFlags,
|
||||||
@ -78,7 +124,7 @@ abstract class BaseIntegrationsPatch(
|
|||||||
strings,
|
strings,
|
||||||
customFingerprint,
|
customFingerprint,
|
||||||
object : IHookInsertIndexResolver {},
|
object : IHookInsertIndexResolver {},
|
||||||
contextRegisterResolver
|
contextRegisterResolver,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun invoke(integrationsDescriptor: String) {
|
fun invoke(integrationsDescriptor: String) {
|
||||||
@ -103,7 +149,7 @@ abstract class BaseIntegrationsPatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
internal companion object {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
|
internal const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.shared.misc.integrations.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object ReVancedUtilsPatchesVersionFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
parameters = listOf(),
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
methodDef.name == "getPatchesReleaseVersion" &&
|
||||||
|
classDef.type == BaseIntegrationsPatch.INTEGRATIONS_CLASS_DESCRIPTOR
|
||||||
|
}
|
||||||
|
)
|
@ -16,10 +16,19 @@ import org.w3c.dom.Document
|
|||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
class NonInteractivePreference(
|
class NonInteractivePreference(
|
||||||
key: String,
|
key: String,
|
||||||
|
titleKey: String = "${key}_title",
|
||||||
summaryKey: String? = "${key}_summary",
|
summaryKey: String? = "${key}_summary",
|
||||||
tag: String = "Preference",
|
tag: String = "Preference",
|
||||||
val selectable: Boolean = false
|
val selectable: Boolean = false
|
||||||
) : BasePreference(null, "${key}_title", summaryKey, tag) {
|
) : BasePreference(key, titleKey, summaryKey, tag) {
|
||||||
|
|
||||||
|
@Deprecated("Here only for binary compatibility, and should be removed after the next major version update.")
|
||||||
|
constructor(
|
||||||
|
key: String,
|
||||||
|
summaryKey: String? = "${key}_summary",
|
||||||
|
tag: String = "Preference",
|
||||||
|
selectable: Boolean = false
|
||||||
|
) : this(key, "${key}_title", summaryKey, tag, selectable)
|
||||||
|
|
||||||
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
|
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
|
||||||
super.serialize(ownerDocument, resourceCallback).apply {
|
super.serialize(ownerDocument, resourceCallback).apply {
|
||||||
|
@ -10,11 +10,14 @@ import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatc
|
|||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.layout.seekbar.SeekbarColorBytecodePatch
|
import app.revanced.patches.youtube.layout.seekbar.SeekbarColorBytecodePatch
|
||||||
|
import app.revanced.patches.youtube.layout.theme.fingerprints.ThemeHelperDarkColorFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.theme.fingerprints.ThemeHelperLightColorFingerprint
|
||||||
import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoadingScreenFingerprint
|
import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoadingScreenFingerprint
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
@ -54,7 +57,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object ThemeBytecodePatch : BytecodePatch(
|
object ThemeBytecodePatch : BytecodePatch(
|
||||||
setOf(UseGradientLoadingScreenFingerprint)
|
setOf(
|
||||||
|
UseGradientLoadingScreenFingerprint,
|
||||||
|
ThemeHelperLightColorFingerprint,
|
||||||
|
ThemeHelperDarkColorFingerprint
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/integrations/youtube/patches/theme/ThemePatch;"
|
"Lapp/revanced/integrations/youtube/patches/theme/ThemePatch;"
|
||||||
@ -121,6 +128,21 @@ object ThemeBytecodePatch : BytecodePatch(
|
|||||||
)
|
)
|
||||||
} ?: throw UseGradientLoadingScreenFingerprint.exception
|
} ?: throw UseGradientLoadingScreenFingerprint.exception
|
||||||
|
|
||||||
|
|
||||||
|
mapOf(
|
||||||
|
ThemeHelperLightColorFingerprint to lightThemeBackgroundColor,
|
||||||
|
ThemeHelperDarkColorFingerprint to darkThemeBackgroundColor
|
||||||
|
).forEach { (fingerprint, color) ->
|
||||||
|
fingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
addInstructions(
|
||||||
|
0, """
|
||||||
|
const-string v0, "$color"
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LithoColorHookPatch.lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getValue")
|
LithoColorHookPatch.lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getValue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.theme.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object ThemeHelperDarkColorFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
parameters = listOf(),
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
methodDef.name == "darkThemeResourceName" &&
|
||||||
|
classDef.type == SettingsPatch.THEME_HELPER_DESCRIPTOR
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.theme.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object ThemeHelperLightColorFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
parameters = listOf(),
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
methodDef.name == "lightThemeResourceName" &&
|
||||||
|
classDef.type == SettingsPatch.THEME_HELPER_DESCRIPTOR
|
||||||
|
}
|
||||||
|
)
|
@ -12,6 +12,7 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
||||||
import app.revanced.patches.shared.misc.settings.preference.InputType
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
@ -39,12 +40,20 @@ object SettingsPatch :
|
|||||||
private const val INTEGRATIONS_PACKAGE = "app/revanced/integrations/youtube"
|
private const val INTEGRATIONS_PACKAGE = "app/revanced/integrations/youtube"
|
||||||
private const val ACTIVITY_HOOK_CLASS_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/settings/LicenseActivityHook;"
|
private const val ACTIVITY_HOOK_CLASS_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/settings/LicenseActivityHook;"
|
||||||
|
|
||||||
private const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/ThemeHelper;"
|
internal const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/ThemeHelper;"
|
||||||
private const val SET_THEME_METHOD_NAME: String = "setTheme"
|
private const val SET_THEME_METHOD_NAME: String = "setTheme"
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
AddResourcesPatch(this::class)
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
|
// Add an about preference to the top.
|
||||||
|
SettingsResourcePatch += NonInteractivePreference(
|
||||||
|
key = "revanced_settings_screen_00_about",
|
||||||
|
summaryKey = null,
|
||||||
|
tag = "app.revanced.integrations.youtube.settings.preference.ReVancedYouTubeAboutPreference",
|
||||||
|
selectable = true,
|
||||||
|
)
|
||||||
|
|
||||||
PreferenceScreen.MISC.addPreferences(
|
PreferenceScreen.MISC.addPreferences(
|
||||||
TextPreference(
|
TextPreference(
|
||||||
key = null,
|
key = null,
|
||||||
@ -52,7 +61,7 @@ object SettingsPatch :
|
|||||||
summaryKey = "revanced_pref_import_export_summary",
|
summaryKey = "revanced_pref_import_export_summary",
|
||||||
inputType = InputType.TEXT_MULTI_LINE,
|
inputType = InputType.TEXT_MULTI_LINE,
|
||||||
tag = "app.revanced.integrations.shared.settings.preference.ImportExportPreference",
|
tag = "app.revanced.integrations.shared.settings.preference.ImportExportPreference",
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod ->
|
SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod ->
|
||||||
@ -69,7 +78,7 @@ object SettingsPatch :
|
|||||||
replaceInstruction(
|
replaceInstruction(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
"invoke-static { v$register }, " +
|
"invoke-static { v$register }, " +
|
||||||
"$THEME_HELPER_DESCRIPTOR->$SET_THEME_METHOD_NAME(Ljava/lang/Object;)V",
|
"$THEME_HELPER_DESCRIPTOR->$SET_THEME_METHOD_NAME(Ljava/lang/Enum;)V",
|
||||||
)
|
)
|
||||||
addInstruction(returnIndex + 1, "return-object v$register")
|
addInstruction(returnIndex + 1, "return-object v$register")
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,15 @@
|
|||||||
<app id="youtube">
|
<app id="youtube">
|
||||||
<patch id="misc.settings.SettingsResourcePatch">
|
<patch id="misc.settings.SettingsResourcePatch">
|
||||||
<string name="revanced_settings">ReVanced</string>
|
<string name="revanced_settings">ReVanced</string>
|
||||||
|
<string name="revanced_settings_about_links_body">You are using ReVanced Patches version <i>%s</i></string>
|
||||||
|
<string name="revanced_settings_about_links_dev_header">Note</string>
|
||||||
|
<string name="revanced_settings_about_links_dev_body">This version is a pre-release and you may experience unexpected issues</string>
|
||||||
|
<string name="revanced_settings_about_links_header">Official links</string>
|
||||||
<string name="revanced_pref_import_export_title">Import / Export</string>
|
<string name="revanced_pref_import_export_title">Import / Export</string>
|
||||||
<string name="revanced_pref_import_export_summary">Import / Export ReVanced settings</string>
|
<string name="revanced_pref_import_export_summary">Import / Export ReVanced settings</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.settings.SettingsPatch">
|
<patch id="misc.settings.SettingsPatch">
|
||||||
|
<string name="revanced_settings_screen_00_about_title">About</string>
|
||||||
<string name="revanced_settings_screen_01_ads_title">Ads</string>
|
<string name="revanced_settings_screen_01_ads_title">Ads</string>
|
||||||
<string name="revanced_settings_screen_02_alt_thumbnails_title">Alternative thumbnails</string>
|
<string name="revanced_settings_screen_02_alt_thumbnails_title">Alternative thumbnails</string>
|
||||||
<string name="revanced_settings_screen_03_feed_title">Feed</string>
|
<string name="revanced_settings_screen_03_feed_title">Feed</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user