Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(YouTube - Remove background playback restrictions): Enable for Shorts as well #3671

Merged
merged 6 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.revanced.extension.youtube.patches;

import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;

@SuppressWarnings("unused")
Expand All @@ -8,10 +9,10 @@ public class BackgroundPlaybackPatch {
/**
* Injection point.
*/
public static boolean allowBackgroundPlayback(boolean original) {
public static boolean isBackgroundPlaybackAllowed(boolean original) {
if (original) return true;

// Steps to verify most edge cases:
// Steps to verify most edge cases (with Shorts background playback set to off):
// 1. Open a regular video
// 2. Minimize app (PIP should appear)
// 3. Reopen app
Expand All @@ -22,13 +23,13 @@ public static boolean allowBackgroundPlayback(boolean original) {
// 7. Close the Short
// 8. Resume playing the regular video
// 9. Minimize the app (PIP should appear)

if (!VideoInformation.lastVideoIdIsShort()) {
return true; // Definitely is not a Short.
}

// Might be a Short, or might be a prior regular video on screen again after a Short was closed.
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Short,
// TODO: Add better hook.
// Might be a Shorts, or might be a prior regular video on screen again after a Shorts was closed.
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Shorts,
// But there's no way around this unless an additional hook is added to definitively detect
// the Shorts player is on screen. This use case is unusual anyways so it's not a huge concern.
return !PlayerType.getCurrent().isNoneHiddenOrMinimized();
Expand All @@ -37,10 +38,7 @@ public static boolean allowBackgroundPlayback(boolean original) {
/**
* Injection point.
*/
public static boolean overrideBackgroundPlaybackAvailable() {
// This could be done entirely in the patch,
// but having a unique method to search for makes manually inspecting the patched apk much easier.
return true;
public static boolean isBackgroundShortsPlaybackAllowed(boolean original) {
return !Settings.DISABLE_SHORTS_BACKGROUND_PLAYBACK.get();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);

// Shorts
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
public static final BooleanSetting HIDE_SHORTS_HOME = new BooleanSetting("revanced_hide_shorts_home", FALSE);
public static final BooleanSetting HIDE_SHORTS_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_shorts_subscriptions", FALSE);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
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.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findInstructionIndicesReversedOrThrow
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.reference.MethodReference

internal var prefBackgroundAndOfflineCategoryId = -1L
private set

private val backgroundPlaybackResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
dependsOn(resourceMappingPatch, addResourcesPatch)

execute {
prefBackgroundAndOfflineCategoryId = resourceMappings["string", "pref_background_and_offline_category"]
Expand Down Expand Up @@ -58,45 +61,54 @@ val backgroundPlaybackPatch = bytecodePatch(

val backgroundPlaybackManagerMatch by backgroundPlaybackManagerFingerprint()
val backgroundPlaybackSettingsMatch by backgroundPlaybackSettingsFingerprint()

val shortsBackgroundPlaybackFeatureFlagMatch by shortsBackgroundPlaybackFeatureFlagFingerprint()
val backgroundPlaybackManagerShortsMatch by backgroundPlaybackManagerShortsFingerprint()

val kidsBackgroundPlaybackPolicyControllerMatch by kidsBackgroundPlaybackPolicyControllerFingerprint()

execute { context ->
backgroundPlaybackManagerMatch.mutableMethod.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA

addInstructionsAtControlFlowLabel(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->allowBackgroundPlayback(Z)Z
move-result v$register
""",
)
addResources("youtube", "misc.backgroundplayback.backgroundPlaybackPatch")

PreferenceScreen.SHORTS.addPreferences(
SwitchPreference("revanced_shorts_disable_background_playback")
)

arrayOf(
backgroundPlaybackManagerMatch to "isBackgroundPlaybackAllowed",
backgroundPlaybackManagerShortsMatch to "isBackgroundShortsPlaybackAllowed"
).forEach { (match, integrationsMethod) ->
match.mutableMethod.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA

addInstructionsAtControlFlowLabel(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$integrationsMethod(Z)Z
move-result v$register
"""
)
}
}
}

// Enable background playback option in YouTube settings
backgroundPlaybackSettingsMatch.mutableMethod.apply {
val booleanCalls = instructions.withIndex()
.filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" }
val booleanCalls = instructions.withIndex().filter {
it.value.getReference<MethodReference>()?.returnType == "Z"
}

val settingsBooleanIndex = booleanCalls.elementAt(1).index
val settingsBooleanMethod = context.navigate(this).at(settingsBooleanIndex).mutable()

settingsBooleanMethod.addInstructions(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->overrideBackgroundPlaybackAvailable()Z
move-result v0
return v0
""",
)
settingsBooleanMethod.returnEarly(true)
}

// Force allowing background play for Shorts.
shortsBackgroundPlaybackFeatureFlagMatch.mutableMethod.returnEarly(true)

// Force allowing background play for videos labeled for kids.
kidsBackgroundPlaybackPolicyControllerMatch.mutableMethod.addInstruction(
0,
"return-void",
)
kidsBackgroundPlaybackPolicyControllerMatch.mutableMethod.returnEarly()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,17 @@ internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint {
)
literal { 5 }
}

internal val backgroundPlaybackManagerShortsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Z")
parameters("L")
literal { 151635310 }
}

internal val shortsBackgroundPlaybackFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
parameters()
literal { 45415425 }
}
5 changes: 5 additions & 0 deletions patches/src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_settings_screen_11_misc_title">Misc</string>
<string name="revanced_settings_screen_12_video_title">Video</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Disable Shorts background play</string>
<string name="revanced_shorts_disable_background_playback_summary_on">Shorts background play is disabled</string>
<string name="revanced_shorts_disable_background_playback_summary_off">Shorts background play is enabled</string>
</patch>
<patch id="misc.debugging.enableDebuggingPatch">
<string name="revanced_debug_screen_title">Debugging</string>
<string name="revanced_debug_screen_summary">Enable or disable debugging options</string>
Expand Down
Loading