diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/misc/BackgroundPlaybackPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/misc/BackgroundPlaybackPatch.java index 4cf3456ee..39dcff1a7 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/misc/BackgroundPlaybackPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/misc/BackgroundPlaybackPatch.java @@ -1,12 +1,24 @@ package app.revanced.extension.youtube.patches.misc; +import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.ShortsPlayerState; @SuppressWarnings("unused") public class BackgroundPlaybackPatch { - public static boolean allowBackgroundPlayback(boolean original) { - return original || ShortsPlayerState.getCurrent().isClosed(); + /** + * Injection point. + */ + public static boolean isBackgroundPlaybackAllowed(boolean original) { + if (original) return true; + return ShortsPlayerState.getCurrent().isClosed(); + } + + /** + * Injection point. + */ + public static boolean isBackgroundShortsPlaybackAllowed(boolean original) { + return !Settings.DISABLE_SHORTS_BACKGROUND_PLAYBACK.get(); } } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java index 8a5eda148..a85472821 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -419,6 +419,7 @@ public class Settings extends BaseSettings { // PreferenceScreen: Shorts public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", TRUE); + public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_disable_shorts_background_playback", FALSE); public static final BooleanSetting HIDE_SHORTS_FLOATING_BUTTON = new BooleanSetting("revanced_hide_shorts_floating_button", TRUE); public static final BooleanSetting HIDE_SHORTS_SHELF = new BooleanSetting("revanced_hide_shorts_shelf", TRUE, true); public static final BooleanSetting HIDE_SHORTS_SHELF_CHANNEL = new BooleanSetting("revanced_hide_shorts_shelf_channel", FALSE); diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt index 06d763e8c..cf3729409 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -1,9 +1,7 @@ package app.revanced.patches.youtube.misc.backgroundplayback -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.extension.Constants.MISC_PATH @@ -11,16 +9,19 @@ import app.revanced.patches.youtube.utils.patch.PatchList.REMOVE_BACKGROUND_PLAY import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.settingsPatch +import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findInstructionIndicesReversedOrThrow -import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodOrThrow -import app.revanced.util.getWalkerMethod +import app.revanced.util.fingerprint.originalMethodOrThrow +import app.revanced.util.getReference +import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +private const val EXTENSION_CLASS_DESCRIPTOR = + "$MISC_PATH/BackgroundPlaybackPatch;" + @Suppress("unused") val backgroundPlaybackPatch = bytecodePatch( REMOVE_BACKGROUND_PLAYBACK_RESTRICTIONS.title, @@ -35,69 +36,54 @@ val backgroundPlaybackPatch = bytecodePatch( execute { - backgroundPlaybackManagerFingerprint.methodOrThrow().apply { - findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> - val register = getInstruction(index).registerA + arrayOf( + backgroundPlaybackManagerFingerprint to "isBackgroundPlaybackAllowed", + backgroundPlaybackManagerShortsFingerprint to "isBackgroundShortsPlaybackAllowed", + ).forEach { (fingerprint, integrationsMethod) -> + fingerprint.methodOrThrow().apply { + findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> + val register = getInstruction(index).registerA - // Replace to preserve control flow label. - replaceInstruction( - index, - "invoke-static { v$register }, $MISC_PATH/BackgroundPlaybackPatch;->allowBackgroundPlayback(Z)Z" - ) - - addInstructions( - index + 1, - """ - move-result v$register - return v$register + addInstructionsAtControlFlowLabel( + index, """ - ) + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$integrationsMethod(Z)Z + move-result v$register + """, + ) + } } } // Enable background playback option in YouTube settings - backgroundPlaybackSettingsFingerprint.methodOrThrow().apply { - val booleanCalls = implementation!!.instructions.withIndex() - .filter { instruction -> - ((instruction.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" - } + backgroundPlaybackSettingsFingerprint.originalMethodOrThrow().apply { + val booleanCalls = instructions.withIndex().filter { + it.value.getReference()?.returnType == "Z" + } - val booleanIndex = booleanCalls.elementAt(1).index - val booleanMethod = getWalkerMethod(booleanIndex) + val settingsBooleanIndex = booleanCalls.elementAt(1).index + val settingsBooleanMethod by navigate(this).to(settingsBooleanIndex) - booleanMethod.addInstructions( - 0, """ - const/4 v0, 0x1 - return v0 - """ - ) + settingsBooleanMethod.returnEarly(true) } + // Force allowing background play for Shorts. + shortsBackgroundPlaybackFeatureFlagFingerprint.methodOrThrow().returnEarly(true) + // Force allowing background play for videos labeled for kids. kidsBackgroundPlaybackPolicyControllerFingerprint.methodOrThrow( kidsBackgroundPlaybackPolicyControllerParentFingerprint - ).addInstruction( - 0, - "return-void" - ) - - pipControllerFingerprint.matchOrThrow().let { - val targetMethod = - it.getWalkerMethod(it.patternMatch!!.endIndex) - - targetMethod.apply { - val targetRegister = getInstruction(0).registerA - - addInstruction( - 1, - "const/4 v$targetRegister, 0x1" - ) - } - } + ).returnEarly() // region add settings - addPreference(REMOVE_BACKGROUND_PLAYBACK_RESTRICTIONS) + addPreference( + arrayOf( + "PREFERENCE_SCREEN: SHORTS", + "SETTINGS: DISABLE_SHORTS_BACKGROUND_PLAYBACK" + ), + REMOVE_BACKGROUND_PLAYBACK_RESTRICTIONS + ) // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt index 5ef3d2c08..7fd5299c8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt @@ -56,14 +56,18 @@ internal val kidsBackgroundPlaybackPolicyControllerParentFingerprint = legacyFin } ) -internal val pipControllerFingerprint = legacyFingerprint( - name = "pipControllerFingerprint", - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - parameters = emptyList(), - opcodes = listOf( - Opcode.IF_NEZ, - Opcode.INVOKE_DIRECT - ), +internal val backgroundPlaybackManagerShortsFingerprint = legacyFingerprint( + name = "backgroundPlaybackManagerShortsFingerprint", + accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, + returnType = "Z", + parameters = listOf("L"), literals = listOf(151635310L), +) + +internal val shortsBackgroundPlaybackFeatureFlagFingerprint = legacyFingerprint( + name = "shortsBackgroundPlaybackFeatureFlagFingerprint", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Z", + parameters = emptyList(), + literals = listOf(45415425L), ) \ No newline at end of file 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 3a236f234..4222dea96 100644 --- a/patches/src/main/resources/youtube/settings/host/values/strings.xml +++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml @@ -1196,6 +1196,9 @@ The Expand video description option may not work if the entered string does not Shorts + Disable Shorts background play + Shorts background play is disabled. + Shorts background play is enabled. Disable resuming Shorts player Shorts player will not resume on app startup. Shorts player will resume on app startup. diff --git a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml index fc3ed7607..f6e780376 100644 --- a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -596,6 +596,9 @@ SETTINGS: SHORTS_COMPONENTS --> + +