From 06f1750bd14ebaad38d59d943c0f791fc74a52b7 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:26:57 +0900 Subject: [PATCH] fix(YouTube): Unable to access `Autoplay next video` setting https://github.com/inotia00/ReVanced_Extended/issues/2664 --- .../shared/mapping/ResourceMappingPatch.kt | 3 +- .../spoofappversion/SpoofAppVersionPatch.kt | 5 + .../utils/fix/cairo/CairoFragmentPatch.kt | 120 ++++++++++++++++++ .../utils/fix/cairo/CairoSettingsPatch.kt | 27 ++-- .../youtube/utils/fix/cairo/Fingerprints.kt | 15 ++- .../utils/resourceid/SharedResourceIdPatch.kt | 13 ++ .../youtube/utils/settings/SettingsPatch.kt | 4 +- .../youtube/settings/host/values/strings.xml | 2 +- 8 files changed, 175 insertions(+), 14 deletions(-) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoFragmentPatch.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/mapping/ResourceMappingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/mapping/ResourceMappingPatch.kt index a19a19599..f48e8f201 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/mapping/ResourceMappingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/mapping/ResourceMappingPatch.kt @@ -77,5 +77,6 @@ enum class ResourceType(val value: String) { INTEGER("integer"), LAYOUT("layout"), STRING("string"), - STYLE("style") + STYLE("style"), + XML("xml") } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/spoofappversion/SpoofAppVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/spoofappversion/SpoofAppVersionPatch.kt index fd9711fde..c942b4489 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/spoofappversion/SpoofAppVersionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/spoofappversion/SpoofAppVersionPatch.kt @@ -8,6 +8,7 @@ import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR +import app.revanced.patches.youtube.utils.fix.cairo.cairoFragmentPatch import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_APP_VERSION import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater @@ -30,6 +31,10 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +/** + * No longer needed due to [cairoFragmentPatch]. + * TODO: Test sufficiently and remove the patch. + */ private val spoofAppVersionBytecodePatch = bytecodePatch( description = "spoofAppVersionBytecodePatch" ) { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoFragmentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoFragmentPatch.kt new file mode 100644 index 000000000..a6ead82f6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoFragmentPatch.kt @@ -0,0 +1,120 @@ +package app.revanced.patches.youtube.utils.fix.cairo + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater +import app.revanced.patches.youtube.utils.playservice.versionCheckPatch +import app.revanced.patches.youtube.utils.resourceid.settingsFragmentCairo +import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch +import app.revanced.util.fingerprint.methodCall +import app.revanced.util.fingerprint.methodOrThrow +import app.revanced.util.getReference +import app.revanced.util.getWalkerMethod +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstLiteralInstructionOrThrow +import app.revanced.util.insertNode +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import org.w3c.dom.Element + +private val cairoFragmentResourcePatch = resourcePatch( + description = "cairoFragmentResourcePatch" +) { + dependsOn(versionCheckPatch) + + execute { + /** + * Cairo fragment have been widely rolled out in YouTube 19.34+. + */ + if (!is_19_34_or_greater) { + return@execute + } + + /** + * The Preference key for 'Playback' is '@string/playback_key'. + * Copy the node to add the Preference 'Playback' to the legacy settings fragment. + */ + document("res/xml/settings_fragment.xml").use { document -> + val tags = document.getElementsByTagName("Preference") + List(tags.length) { tags.item(it) as Element } + .find { it.getAttribute("android:key") == "@string/auto_play_key" } + ?.let { node -> + node.insertNode("Preference", node) { + for (index in 0 until node.attributes.length) { + with (node.attributes.item(index)) { + setAttribute(nodeName, nodeValue) + } + } + setAttribute("android:key", "@string/playback_key") + } + } + } + + } +} + +/** + * What [cairoFragmentPatch] does: + * 1. Disable Cairo fragment settings. + * 2. Fix - When spoofing the app version to 19.20 or earlier, the app crashes or the Notifications tab is inaccessible. + * 3. Fix - Preference 'Playback' is hidden. + * 4. Some settings that were in Preference 'General' are moved to Preference 'Playback'. + */ +val cairoFragmentPatch = bytecodePatch( + description = "cairoFragmentPatch" +) { + dependsOn( + cairoFragmentResourcePatch, + sharedResourceIdPatch, + versionCheckPatch + ) + + execute { + /** + * Cairo fragment have been widely rolled out in YouTube 19.34+. + */ + if (!is_19_34_or_greater) { + return@execute + } + + // Instead of disabling all Cairo fragment configs, + // Just disable 'Load Cairo fragment xml' and 'Set style to Cairo preference'. + val cairoFragmentConfigMethodCall = cairoFragmentConfigFingerprint + .methodCall() + + fun MutableMethod.disableCairoFragmentConfig() { + val insertIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.toString() == cairoFragmentConfigMethodCall + } + 2 + val insertRegister = getInstruction(insertIndex - 1).registerA + + addInstruction(insertIndex, "const/4 v$insertRegister, 0x0") + } + + settingsFragmentSyntheticFingerprint.methodOrThrow().apply { + val literalIndex = indexOfFirstLiteralInstructionOrThrow(settingsFragmentCairo) + val fragmentStyleIndex = indexOfFirstInstructionOrThrow(literalIndex) { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL_RANGE && + reference?.returnType == "V" && + reference.parameterTypes.firstOrNull() == "Ljava/lang/String;" + } + val fragmentStyleMethod = getWalkerMethod(fragmentStyleIndex) + + arrayOf( + // Load cairo fragment xml. + this, + // Set style to cairo preference. + fragmentStyleMethod + ).forEach { method -> + method.disableCairoFragmentConfig() + } + } + + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoSettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoSettingsPatch.kt index c4474b53f..14fadf405 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoSettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/CairoSettingsPatch.kt @@ -2,31 +2,42 @@ package app.revanced.patches.youtube.utils.fix.cairo import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.misc.backgroundplayback.backgroundPlaybackPatch -import app.revanced.patches.youtube.utils.playservice.is_19_04_or_greater -import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall +/** + * As of 2025, responses to [Account/Get Setting](https://youtubei.googleapis.com/youtubei/v1/account/get_setting) + * requests no longer include the Preference 'Autoplay' (YouTube 19.34+). + * + * In YouTube 19.34+, the Preference 'Playback' of the Cairo fragment replaces the Preference 'Autoplay'. + * + * Since RVX disables the Cairo fragment, + * users who have newly installed RVX 19.34+ will no longer be able to turn 'Autoplay next video' on or off in YouTube settings. + * + * As a workaround, [cairoSettingsPatch] has been replaced by [cairoFragmentPatch]. + */ +@Deprecated("Use 'cairoFragmentPatch' instead.") +@Suppress("unused") val cairoSettingsPatch = bytecodePatch( description = "cairoSettingsPatch" ) { - dependsOn(versionCheckPatch) + dependsOn(cairoFragmentPatch) execute { - if (!is_19_04_or_greater) { + if (true) { return@execute } /** - * Cairo Fragment was added since YouTube v19.04.38. + * Cairo fragment was added since YouTube v19.04.38. * Disable this for the following reasons: * 1. [backgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment. - * 2. Some patches implemented in RVX do not yet support Cairo Fragments. + * 2. Some patches implemented in RVX do not yet support Cairo fragments. * * See ReVanced_Extended#2099 * or uYouPlus#1468 - * for screenshots of the Cairo Fragment. + * for screenshots of the Cairo fragment. */ - carioFragmentConfigFingerprint.injectLiteralInstructionBooleanCall( + cairoFragmentConfigFingerprint.injectLiteralInstructionBooleanCall( CAIRO_FRAGMENT_FEATURE_FLAG, "0x0" ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/Fingerprints.kt index 11ef1f932..1e592cbc8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/cairo/Fingerprints.kt @@ -3,6 +3,9 @@ package app.revanced.patches.youtube.utils.fix.cairo import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.or import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patches.youtube.utils.resourceid.settingsFragment +import app.revanced.patches.youtube.utils.resourceid.settingsFragmentCairo +import com.android.tools.smali.dexlib2.Opcode /** * Added in YouTube v19.04.38 @@ -12,9 +15,17 @@ import com.android.tools.smali.dexlib2.AccessFlags */ internal const val CAIRO_FRAGMENT_FEATURE_FLAG = 45532100L -internal val carioFragmentConfigFingerprint = legacyFingerprint( - name = "carioFragmentConfigFingerprint", +internal val cairoFragmentConfigFingerprint = legacyFingerprint( + name = "cairoFragmentConfigFingerprint", returnType = "Z", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, literals = listOf(CAIRO_FRAGMENT_FEATURE_FLAG), ) + +internal val settingsFragmentSyntheticFingerprint = legacyFingerprint( + name = "settingsFragmentSyntheticFingerprint", + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + opcodes = listOf(Opcode.INVOKE_VIRTUAL_RANGE), + literals = listOf(settingsFragment, settingsFragmentCairo), +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt index b2e4a1e4d..64a23bfc0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt @@ -10,6 +10,7 @@ import app.revanced.patches.shared.mapping.ResourceType.INTEGER import app.revanced.patches.shared.mapping.ResourceType.LAYOUT import app.revanced.patches.shared.mapping.ResourceType.STRING import app.revanced.patches.shared.mapping.ResourceType.STYLE +import app.revanced.patches.shared.mapping.ResourceType.XML import app.revanced.patches.shared.mapping.get import app.revanced.patches.shared.mapping.resourceMappingPatch import app.revanced.patches.shared.mapping.resourceMappings @@ -190,6 +191,10 @@ var seekEasyHorizontalTouchOffsetToStartScrubbing = -1L private set var seekUndoEduOverlayStub = -1L private set +var settingsFragment = -1L + private set +var settingsFragmentCairo = -1L + private set var slidingDialogAnimation = -1L private set var subtitleMenuSettingsFooterInfo = -1L @@ -594,6 +599,14 @@ internal val sharedResourceIdPatch = resourcePatch( ID, "seek_undo_edu_overlay_stub" ] + settingsFragment = resourceMappings[ + XML, + "settings_fragment" + ] + settingsFragmentCairo = resourceMappings[ + XML, + "settings_fragment_cairo" + ] slidingDialogAnimation = resourceMappings[ STYLE, "SlidingDialogAnimation" diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt index caa5ebb0c..40b123ba0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt @@ -14,7 +14,7 @@ import app.revanced.patches.shared.mainactivity.injectOnCreateMethodCall import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch -import app.revanced.patches.youtube.utils.fix.cairo.cairoSettingsPatch +import app.revanced.patches.youtube.utils.fix.cairo.cairoFragmentPatch import app.revanced.patches.youtube.utils.fix.playbackspeed.playbackSpeedWhilePlayingPatch import app.revanced.patches.youtube.utils.fix.splash.darkModeSplashScreenPatch import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch @@ -122,7 +122,7 @@ val settingsPatch = resourcePatch( dependsOn( settingsBytecodePatch, - cairoSettingsPatch, + cairoFragmentPatch, darkModeSplashScreenPatch, playbackSpeedWhilePlayingPatch, ) diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml index 1894a2714..29974fd4e 100644 --- a/patches/src/main/resources/youtube/settings/host/values/strings.xml +++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml @@ -730,7 +730,7 @@ Note: "Suggested video end screen is hidden when autoplay is turned off. Autoplay can be changed in YouTube settings: -Settings → Autoplay → Autoplay next video" +Settings → Autoplay / Playback → Autoplay next video" Suggested video end screen is shown. Skip autoplay countdown If autoplay is enabled, the next video will play immediately.