Skip to content

Commit c7c5e5b

Browse files
feat(YouTube): Add Open Shorts in regular player patch (ReVanced#4153)
1 parent a4db3f1 commit c7c5e5b

File tree

18 files changed

+463
-62
lines changed

18 files changed

+463
-62
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package app.revanced.extension.youtube.patches;
2+
3+
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
4+
5+
import android.app.Activity;
6+
import android.content.Intent;
7+
import android.net.Uri;
8+
9+
import java.lang.ref.WeakReference;
10+
11+
import app.revanced.extension.shared.Logger;
12+
import app.revanced.extension.youtube.settings.Settings;
13+
14+
@SuppressWarnings("unused")
15+
public class OpenShortsInRegularPlayerPatch {
16+
17+
public enum ShortsPlayerType {
18+
SHORTS_PLAYER,
19+
REGULAR_PLAYER,
20+
REGULAR_PLAYER_FULLSCREEN
21+
}
22+
23+
static {
24+
if (!VersionCheckPatch.IS_19_46_OR_GREATER
25+
&& Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) {
26+
// User imported newer settings to an older app target.
27+
Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE);
28+
Settings.SHORTS_PLAYER_TYPE.resetToDefault();
29+
}
30+
}
31+
32+
private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);
33+
34+
/**
35+
* Injection point.
36+
*/
37+
public static void setMainActivity(Activity activity) {
38+
mainActivityRef = new WeakReference<>(activity);
39+
}
40+
41+
/**
42+
* Injection point.
43+
*/
44+
public static boolean openShort(String videoID) {
45+
try {
46+
ShortsPlayerType type = Settings.SHORTS_PLAYER_TYPE.get();
47+
if (type == ShortsPlayerType.SHORTS_PLAYER) {
48+
return false; // Default unpatched behavior.
49+
}
50+
51+
if (videoID.isEmpty()) {
52+
// Shorts was opened using launcher app shortcut.
53+
//
54+
// This check will not detect if the Shorts app shortcut is used
55+
// while the app is running in the background (instead the regular player is opened).
56+
// To detect that the hooked method map parameter can be checked
57+
// if integer key 'com.google.android.apps.youtube.app.endpoint.flags'
58+
// has bitmask 16 set.
59+
//
60+
// This use case seems unlikely if the user has the Shorts
61+
// set to open in the regular player, so it's ignored as
62+
// checking the map makes the patch more complicated.
63+
Logger.printDebug(() -> "Ignoring Short with no videoId");
64+
return false;
65+
}
66+
67+
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.SHORTS) {
68+
return false; // Always use Shorts player for the Shorts nav button.
69+
}
70+
71+
final boolean forceFullScreen = (type == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN);
72+
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(forceFullScreen);
73+
74+
// Can use the application context and add intent flags of
75+
// FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP
76+
// But the activity context seems to fix random app crashes
77+
// if Shorts urls are opened outside the app.
78+
var context = mainActivityRef.get();
79+
80+
Intent videoPlayerIntent = new Intent(
81+
Intent.ACTION_VIEW,
82+
Uri.parse("https://youtube.com/watch?v=" + videoID)
83+
);
84+
videoPlayerIntent.setPackage(context.getPackageName());
85+
86+
context.startActivity(videoPlayerIntent);
87+
return true;
88+
} catch (Exception ex) {
89+
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(null);
90+
Logger.printException(() -> "openShort failure", ex);
91+
return false;
92+
}
93+
}
94+
}

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreen.java

-14
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package app.revanced.extension.youtube.patches;
2+
3+
import androidx.annotation.Nullable;
4+
5+
import app.revanced.extension.youtube.settings.Settings;
6+
7+
@SuppressWarnings("unused")
8+
public class OpenVideosFullscreenHookPatch {
9+
10+
@Nullable
11+
private static volatile Boolean openNextVideoFullscreen;
12+
13+
public static void setOpenNextVideoFullscreen(@Nullable Boolean forceFullScreen) {
14+
openNextVideoFullscreen = forceFullScreen;
15+
}
16+
17+
/**
18+
* Changed during patching since this class is also
19+
* used by {@link OpenVideosFullscreenHookPatch}.
20+
*/
21+
private static boolean isFullScreenPatchIncluded() {
22+
return false;
23+
}
24+
25+
/**
26+
* Injection point.
27+
*/
28+
public static boolean openVideoFullscreenPortrait(boolean original) {
29+
Boolean openFullscreen = openNextVideoFullscreen;
30+
if (openFullscreen != null) {
31+
openNextVideoFullscreen = null;
32+
return openFullscreen;
33+
}
34+
35+
if (!isFullScreenPatchIncluded()) {
36+
return false;
37+
}
38+
39+
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
40+
}
41+
}

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java

+1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ public class VersionCheckPatch {
99
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
1010
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
1111
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
12+
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
1213
}

extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_2;
1717
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
1818
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
19+
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
1920
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
2021
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
2122
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
@@ -224,6 +225,7 @@ public class Settings extends BaseSettings {
224225
// Shorts
225226
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
226227
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
228+
public static final EnumSetting<ShortsPlayerType> SHORTS_PLAYER_TYPE = new EnumSetting<>("revanced_shorts_player_type", ShortsPlayerType.SHORTS_PLAYER);
227229
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
228230
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
229231
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);

patches/api/patches.api

+8
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,10 @@ public final class app/revanced/patches/youtube/layout/player/background/PlayerC
11771177
}
11781178

11791179
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt {
1180+
public static final fun getOpenVideosFullscreen ()Lapp/revanced/patcher/patch/BytecodePatch;
1181+
}
1182+
1183+
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt {
11801184
public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
11811185
}
11821186

@@ -1210,6 +1214,10 @@ public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAuto
12101214
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
12111215
}
12121216

1217+
public final class app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatchKt {
1218+
public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
1219+
}
1220+
12131221
public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
12141222
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
12151223
}

patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.checks
22

33
import android.os.Build.*
44
import app.revanced.patcher.Fingerprint
5-
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
5+
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
66
import app.revanced.patcher.patch.Patch
77
import app.revanced.patcher.patch.bytecodePatch
88
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
@@ -82,7 +82,7 @@ fun checkEnvironmentPatch(
8282
}
8383
}
8484

85-
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstructions(
85+
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
8686
0,
8787
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
8888
)

patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt

+12
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,15 @@ internal val openVideosFullscreenPortraitFingerprint = fingerprint {
1414
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG
1515
}
1616
}
17+
18+
/**
19+
* Used to enable opening regular videos fullscreen.
20+
*/
21+
internal val openVideosFullscreenHookPatchExtensionFingerprint = fingerprint {
22+
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
23+
returns("Z")
24+
parameters()
25+
custom { methodDef, classDef ->
26+
methodDef.name == "isFullScreenPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,9 @@
11
package app.revanced.patches.youtube.layout.player.fullscreen
22

33
import app.revanced.patcher.patch.bytecodePatch
4-
import app.revanced.patches.all.misc.resources.addResources
5-
import app.revanced.patches.all.misc.resources.addResourcesPatch
6-
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
7-
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
8-
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
9-
import app.revanced.patches.youtube.misc.settings.settingsPatch
10-
import app.revanced.util.insertFeatureFlagBooleanOverride
11-
12-
private const val EXTENSION_CLASS_DESCRIPTOR =
13-
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreen;"
144

155
@Suppress("unused")
16-
val openVideosFullscreenPatch = bytecodePatch(
17-
name = "Open videos fullscreen",
18-
description = "Adds an option to open videos in full screen portrait mode.",
19-
) {
20-
dependsOn(
21-
sharedExtensionPatch,
22-
settingsPatch,
23-
addResourcesPatch,
24-
)
25-
26-
compatibleWith(
27-
"com.google.android.youtube"(
28-
"19.46.42",
29-
)
30-
)
31-
32-
execute {
33-
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
34-
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
35-
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
36-
)
37-
38-
// Add resources and setting last, in case the user force patches an old incompatible version.
39-
40-
addResources("youtube", "layout.player.fullscreen.openVideosFullscreen")
41-
42-
PreferenceScreen.PLAYER.addPreferences(
43-
SwitchPreference("revanced_open_videos_fullscreen_portrait")
44-
)
45-
}
46-
}
6+
@Deprecated("Renamed to openVideosFullscreenPatch", ReplaceWith("openVideosFullscreenPatch"))
7+
val openVideosFullscreen = bytecodePatch{
8+
dependsOn(openVideosFullscreenPatch)
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package app.revanced.patches.youtube.layout.player.fullscreen
2+
3+
import app.revanced.patcher.patch.bytecodePatch
4+
import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlayerPatch
5+
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
6+
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
7+
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
8+
import app.revanced.util.insertFeatureFlagBooleanOverride
9+
10+
internal const val EXTENSION_CLASS_DESCRIPTOR =
11+
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
12+
13+
/**
14+
* Used by both [openVideosFullscreenPatch] and [openShortsInRegularPlayerPatch].
15+
*/
16+
internal val openVideosFullscreenHookPatch = bytecodePatch {
17+
dependsOn(
18+
sharedExtensionPatch,
19+
versionCheckPatch
20+
)
21+
22+
execute {
23+
if (!is_19_46_or_greater) {
24+
return@execute
25+
}
26+
27+
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
28+
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
29+
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
30+
)
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package app.revanced.patches.youtube.layout.player.fullscreen
2+
3+
import app.revanced.patcher.patch.PatchException
4+
import app.revanced.patcher.patch.bytecodePatch
5+
import app.revanced.patches.all.misc.resources.addResources
6+
import app.revanced.patches.all.misc.resources.addResourcesPatch
7+
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
8+
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
9+
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
10+
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
11+
import app.revanced.patches.youtube.misc.settings.settingsPatch
12+
import app.revanced.util.returnEarly
13+
14+
@Suppress("unused")
15+
val openVideosFullscreenPatch = bytecodePatch(
16+
name = "Open videos fullscreen",
17+
description = "Adds an option to open videos in full screen portrait mode.",
18+
) {
19+
dependsOn(
20+
openVideosFullscreenHookPatch,
21+
settingsPatch,
22+
addResourcesPatch,
23+
versionCheckPatch
24+
)
25+
26+
compatibleWith(
27+
"com.google.android.youtube"(
28+
"19.46.42",
29+
)
30+
)
31+
32+
execute {
33+
if (!is_19_46_or_greater) {
34+
throw PatchException("'Open videos fullscreen' requires 19.46.42 or greater")
35+
}
36+
37+
addResources("youtube", "layout.player.fullscreen.openVideosFullscreen")
38+
39+
PreferenceScreen.PLAYER.addPreferences(
40+
SwitchPreference("revanced_open_videos_fullscreen_portrait")
41+
)
42+
43+
// Enable the logic for the user Setting to open regular videos fullscreen.
44+
openVideosFullscreenHookPatchExtensionFingerprint.method.returnEarly(true)
45+
}
46+
}

patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package app.revanced.patches.youtube.layout.shortsautoplay
22

3+
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
34
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
45
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
56
import app.revanced.patcher.patch.bytecodePatch
@@ -56,7 +57,7 @@ val shortsAutoplayPatch = bytecodePatch(
5657
}
5758

5859
// Main activity is used to check if app is in pip mode.
59-
mainActivityOnCreateFingerprint.method.addInstructions(
60+
mainActivityOnCreateFingerprint.method.addInstruction(
6061
1,
6162
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
6263
"setMainActivity(Landroid/app/Activity;)V",

0 commit comments

Comments
 (0)