Skip to content

Commit 5abf894

Browse files
feat(YouTube): Add 'About' preference to settings menu (ReVanced#2981)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
1 parent c00256d commit 5abf894

File tree

9 files changed

+154
-13
lines changed

9 files changed

+154
-13
lines changed

api/revanced-patches.api

+2
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
844844
}
845845

846846
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
847+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
848+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
847849
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
848850
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
849851
public final fun getSelectable ()Z

src/main/kotlin/app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch.kt

+54-8
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ package app.revanced.patches.shared.misc.integrations
22

33
import app.revanced.patcher.data.BytecodeContext
44
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
5+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
56
import app.revanced.patcher.fingerprint.MethodFingerprint
67
import app.revanced.patcher.patch.BytecodePatch
78
import app.revanced.patcher.patch.PatchException
89
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint.IRegisterResolver
10+
import app.revanced.patches.shared.misc.integrations.fingerprints.ReVancedUtilsPatchesVersionFingerprint
11+
import app.revanced.util.resultOrThrow
912
import com.android.tools.smali.dexlib2.Opcode
1013
import com.android.tools.smali.dexlib2.iface.ClassDef
1114
import com.android.tools.smali.dexlib2.iface.Method
15+
import java.util.jar.JarFile
1216

1317
abstract class BaseIntegrationsPatch(
1418
private val hooks: Set<IntegrationsFingerprint>,
15-
) : BytecodePatch(hooks) {
19+
) : BytecodePatch(hooks + setOf(ReVancedUtilsPatchesVersionFingerprint)) {
1620

1721
@Deprecated(
1822
"Use the constructor without the integrationsDescriptor parameter",
@@ -34,6 +38,46 @@ abstract class BaseIntegrationsPatch(
3438
hooks.forEach { hook ->
3539
hook.invoke(INTEGRATIONS_CLASS_DESCRIPTOR)
3640
}
41+
42+
// Modify Utils method to include the patches release version version.
43+
ReVancedUtilsPatchesVersionFingerprint.resultOrThrow().mutableMethod.apply {
44+
val manifestValue = getPatchesManifestEntry("Version")
45+
46+
addInstructions(
47+
0,
48+
"""
49+
const-string v0, "$manifestValue"
50+
return-object v0
51+
""",
52+
)
53+
}
54+
}
55+
56+
/**
57+
* @return The value for the manifest entry,
58+
* or "Unknown" if the entry does not exist or is blank.
59+
*/
60+
@Suppress("SameParameterValue")
61+
private fun getPatchesManifestEntry(attributeKey: String) = JarFile(getCurrentJarFilePath()).use { jarFile ->
62+
jarFile.manifest.mainAttributes.entries.firstOrNull { it.key.toString() == attributeKey }?.value?.toString()
63+
?: "Unknown"
64+
}
65+
66+
/**
67+
* @return The file path for the jar this classfile is contained inside.
68+
*/
69+
private fun getCurrentJarFilePath(): String {
70+
val className = object {}::class.java.enclosingClass.name.replace('.', '/') + ".class"
71+
val classUrl = object {}::class.java.classLoader.getResource(className)
72+
if (classUrl != null) {
73+
val urlString = classUrl.toString()
74+
75+
if (urlString.startsWith("jar:file:")) {
76+
val end = urlString.indexOf('!')
77+
return urlString.substring("jar:file:".length, end)
78+
}
79+
}
80+
throw IllegalStateException("Not running from inside a JAR file.")
3781
}
3882

3983
/**
@@ -50,7 +94,7 @@ abstract class BaseIntegrationsPatch(
5094
strings: Iterable<String>? = null,
5195
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
5296
private val insertIndexResolver: ((Method) -> Int) = object : IHookInsertIndexResolver {},
53-
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {}
97+
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
5498
) : MethodFingerprint(
5599
returnType,
56100
accessFlags,
@@ -59,17 +103,19 @@ abstract class BaseIntegrationsPatch(
59103
strings,
60104
customFingerprint,
61105
) {
62-
@Deprecated("Previous constructor that is missing the insert index." +
106+
@Deprecated(
107+
"Previous constructor that is missing the insert index." +
63108
"Here only for binary compatibility, " +
64-
"and this can be removed after the next major version update.")
109+
"and this can be removed after the next major version update.",
110+
)
65111
constructor(
66112
returnType: String? = null,
67113
accessFlags: Int? = null,
68114
parameters: Iterable<String>? = null,
69115
opcodes: Iterable<Opcode?>? = null,
70116
strings: Iterable<String>? = null,
71117
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
72-
contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {}
118+
contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
73119
) : this(
74120
returnType,
75121
accessFlags,
@@ -78,7 +124,7 @@ abstract class BaseIntegrationsPatch(
78124
strings,
79125
customFingerprint,
80126
object : IHookInsertIndexResolver {},
81-
contextRegisterResolver
127+
contextRegisterResolver,
82128
)
83129

84130
fun invoke(integrationsDescriptor: String) {
@@ -103,7 +149,7 @@ abstract class BaseIntegrationsPatch(
103149
}
104150
}
105151

106-
private companion object {
107-
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
152+
internal companion object {
153+
internal const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
108154
}
109155
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package app.revanced.patches.shared.misc.integrations.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
6+
import com.android.tools.smali.dexlib2.AccessFlags
7+
8+
internal object ReVancedUtilsPatchesVersionFingerprint : MethodFingerprint(
9+
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
10+
returnType = "Ljava/lang/String;",
11+
parameters = listOf(),
12+
customFingerprint = { methodDef, classDef ->
13+
methodDef.name == "getPatchesReleaseVersion" &&
14+
classDef.type == BaseIntegrationsPatch.INTEGRATIONS_CLASS_DESCRIPTOR
15+
}
16+
)

src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt

+10-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,19 @@ import org.w3c.dom.Document
1616
@Suppress("MemberVisibilityCanBePrivate")
1717
class NonInteractivePreference(
1818
key: String,
19+
titleKey: String = "${key}_title",
1920
summaryKey: String? = "${key}_summary",
2021
tag: String = "Preference",
2122
val selectable: Boolean = false
22-
) : BasePreference(null, "${key}_title", summaryKey, tag) {
23+
) : BasePreference(key, titleKey, summaryKey, tag) {
24+
25+
@Deprecated("Here only for binary compatibility, and should be removed after the next major version update.")
26+
constructor(
27+
key: String,
28+
summaryKey: String? = "${key}_summary",
29+
tag: String = "Preference",
30+
selectable: Boolean = false
31+
) : this(key, "${key}_title", summaryKey, tag, selectable)
2332

2433
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
2534
super.serialize(ownerDocument, resourceCallback).apply {

src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch.kt

+23-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatc
1010
import app.revanced.patches.all.misc.resources.AddResourcesPatch
1111
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
1212
import app.revanced.patches.youtube.layout.seekbar.SeekbarColorBytecodePatch
13+
import app.revanced.patches.youtube.layout.theme.fingerprints.ThemeHelperDarkColorFingerprint
14+
import app.revanced.patches.youtube.layout.theme.fingerprints.ThemeHelperLightColorFingerprint
1315
import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoadingScreenFingerprint
1416
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
1517
import app.revanced.patches.youtube.misc.settings.SettingsPatch
1618
import app.revanced.util.exception
1719
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
20+
import app.revanced.util.resultOrThrow
1821
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
1922

2023
@Patch(
@@ -54,7 +57,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
5457
)
5558
@Suppress("unused")
5659
object ThemeBytecodePatch : BytecodePatch(
57-
setOf(UseGradientLoadingScreenFingerprint)
60+
setOf(
61+
UseGradientLoadingScreenFingerprint,
62+
ThemeHelperLightColorFingerprint,
63+
ThemeHelperDarkColorFingerprint
64+
)
5865
) {
5966
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
6067
"Lapp/revanced/integrations/youtube/patches/theme/ThemePatch;"
@@ -121,6 +128,21 @@ object ThemeBytecodePatch : BytecodePatch(
121128
)
122129
} ?: throw UseGradientLoadingScreenFingerprint.exception
123130

131+
132+
mapOf(
133+
ThemeHelperLightColorFingerprint to lightThemeBackgroundColor,
134+
ThemeHelperDarkColorFingerprint to darkThemeBackgroundColor
135+
).forEach { (fingerprint, color) ->
136+
fingerprint.resultOrThrow().mutableMethod.apply {
137+
addInstructions(
138+
0, """
139+
const-string v0, "$color"
140+
return-object v0
141+
"""
142+
)
143+
}
144+
}
145+
124146
LithoColorHookPatch.lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getValue")
125147
}
126148
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package app.revanced.patches.youtube.layout.theme.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import app.revanced.patches.youtube.misc.settings.SettingsPatch
6+
import com.android.tools.smali.dexlib2.AccessFlags
7+
8+
internal object ThemeHelperDarkColorFingerprint : MethodFingerprint(
9+
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
10+
returnType = "Ljava/lang/String;",
11+
parameters = listOf(),
12+
customFingerprint = { methodDef, classDef ->
13+
methodDef.name == "darkThemeResourceName" &&
14+
classDef.type == SettingsPatch.THEME_HELPER_DESCRIPTOR
15+
}
16+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package app.revanced.patches.youtube.layout.theme.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import app.revanced.patches.youtube.misc.settings.SettingsPatch
6+
import com.android.tools.smali.dexlib2.AccessFlags
7+
8+
internal object ThemeHelperLightColorFingerprint : MethodFingerprint(
9+
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
10+
returnType = "Ljava/lang/String;",
11+
parameters = listOf(),
12+
customFingerprint = { methodDef, classDef ->
13+
methodDef.name == "lightThemeResourceName" &&
14+
classDef.type == SettingsPatch.THEME_HELPER_DESCRIPTOR
15+
}
16+
)

src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt

+12-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch
1212
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
1313
import app.revanced.patches.shared.misc.settings.preference.InputType
1414
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
15+
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
1516
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
1617
import app.revanced.patches.shared.misc.settings.preference.TextPreference
1718
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
@@ -39,20 +40,28 @@ object SettingsPatch :
3940
private const val INTEGRATIONS_PACKAGE = "app/revanced/integrations/youtube"
4041
private const val ACTIVITY_HOOK_CLASS_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/settings/LicenseActivityHook;"
4142

42-
private const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/ThemeHelper;"
43+
internal const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/ThemeHelper;"
4344
private const val SET_THEME_METHOD_NAME: String = "setTheme"
4445

4546
override fun execute(context: BytecodeContext) {
4647
AddResourcesPatch(this::class)
4748

49+
// Add an about preference to the top.
50+
SettingsResourcePatch += NonInteractivePreference(
51+
key = "revanced_settings_screen_00_about",
52+
summaryKey = null,
53+
tag = "app.revanced.integrations.youtube.settings.preference.ReVancedYouTubeAboutPreference",
54+
selectable = true,
55+
)
56+
4857
PreferenceScreen.MISC.addPreferences(
4958
TextPreference(
5059
key = null,
5160
titleKey = "revanced_pref_import_export_title",
5261
summaryKey = "revanced_pref_import_export_summary",
5362
inputType = InputType.TEXT_MULTI_LINE,
5463
tag = "app.revanced.integrations.shared.settings.preference.ImportExportPreference",
55-
),
64+
)
5665
)
5766

5867
SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod ->
@@ -69,7 +78,7 @@ object SettingsPatch :
6978
replaceInstruction(
7079
returnIndex,
7180
"invoke-static { v$register }, " +
72-
"$THEME_HELPER_DESCRIPTOR->$SET_THEME_METHOD_NAME(Ljava/lang/Object;)V",
81+
"$THEME_HELPER_DESCRIPTOR->$SET_THEME_METHOD_NAME(Ljava/lang/Enum;)V",
7382
)
7483
addInstruction(returnIndex + 1, "return-object v$register")
7584
}

src/main/resources/addresources/values/strings.xml

+5
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,15 @@
5353
<app id="youtube">
5454
<patch id="misc.settings.SettingsResourcePatch">
5555
<string name="revanced_settings">ReVanced</string>
56+
<string name="revanced_settings_about_links_body">You are using ReVanced Patches version &lt;i>%s&lt;/i></string>
57+
<string name="revanced_settings_about_links_dev_header">Note</string>
58+
<string name="revanced_settings_about_links_dev_body">This version is a pre-release and you may experience unexpected issues</string>
59+
<string name="revanced_settings_about_links_header">Official links</string>
5660
<string name="revanced_pref_import_export_title">Import / Export</string>
5761
<string name="revanced_pref_import_export_summary">Import / Export ReVanced settings</string>
5862
</patch>
5963
<patch id="misc.settings.SettingsPatch">
64+
<string name="revanced_settings_screen_00_about_title">About</string>
6065
<string name="revanced_settings_screen_01_ads_title">Ads</string>
6166
<string name="revanced_settings_screen_02_alt_thumbnails_title">Alternative thumbnails</string>
6267
<string name="revanced_settings_screen_03_feed_title">Feed</string>

0 commit comments

Comments
 (0)