Skip to content
This repository was archived by the owner on Oct 26, 2024. It is now read-only.

Commit 61569ba

Browse files
zainarbaniLisoUseInAIKyrioskitadai31
authored
feat(YouTube): Support versions 19.25 and 19.34 (#689)
Co-authored-by: LisoUseInAIKyrios <[email protected]> Co-authored-by: kitadai31 <[email protected]>
1 parent 99d17bf commit 61569ba

27 files changed

+989
-521
lines changed

app/src/main/java/app/revanced/integrations/shared/Utils.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package app.revanced.integrations.shared;
22

33
import android.annotation.SuppressLint;
4-
import android.app.Activity;
5-
import android.app.AlertDialog;
6-
import android.app.Dialog;
7-
import android.app.DialogFragment;
4+
import android.app.*;
85
import android.content.Context;
96
import android.content.Intent;
107
import android.content.pm.PackageInfo;
@@ -268,6 +265,20 @@ public interface MatchFilter<T> {
268265
boolean matches(T object);
269266
}
270267

268+
/**
269+
* Includes sub children.
270+
*
271+
* @noinspection unchecked
272+
*/
273+
public static <R extends View> R getChildViewByResourceName(@NonNull View view, @NonNull String str) {
274+
var child = view.findViewById(Utils.getResourceIdentifier(str, "id"));
275+
if (child != null) {
276+
return (R) child;
277+
}
278+
279+
throw new IllegalArgumentException("View with resource name '" + str + "' not found");
280+
}
281+
271282
/**
272283
* @param searchRecursively If children ViewGroups should also be
273284
* recursively searched using depth first search.
@@ -710,4 +721,21 @@ public static void sortPreferenceGroups(@NonNull PreferenceGroup group) {
710721
pref.setOrder(order);
711722
}
712723
}
724+
725+
/**
726+
* If {@link Fragment} uses [Android library] rather than [AndroidX library],
727+
* the Dialog theme corresponding to [Android library] should be used.
728+
* <p>
729+
* If not, the following issues will occur:
730+
* <a href="https://github.com/ReVanced/revanced-patches/issues/3061">ReVanced/revanced-patches#3061</a>
731+
* <p>
732+
* To prevent these issues, apply the Dialog theme corresponding to [Android library].
733+
*/
734+
public static void setEditTextDialogTheme(AlertDialog.Builder builder) {
735+
final int editTextDialogStyle = getResourceIdentifier(
736+
"revanced_edit_text_dialog_style", "style");
737+
if (editTextDialogStyle != 0) {
738+
builder.getContext().setTheme(editTextDialogStyle);
739+
}
740+
}
713741
}

app/src/main/java/app/revanced/integrations/shared/settings/preference/AbstractPreferenceFragment.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import app.revanced.integrations.shared.Logger;
1313
import app.revanced.integrations.shared.Utils;
14+
import app.revanced.integrations.shared.settings.BaseSettings;
1415
import app.revanced.integrations.shared.settings.BooleanSetting;
1516
import app.revanced.integrations.shared.settings.Setting;
1617

@@ -141,8 +142,13 @@ private void updatePreferenceScreen(@NonNull PreferenceScreen screen,
141142
} else if (pref.hasKey()) {
142143
String key = pref.getKey();
143144
Setting<?> setting = Setting.getSettingFromPath(key);
145+
144146
if (setting != null) {
145147
updatePreference(pref, setting, syncSettingValue, applySettingToPreference);
148+
} else if (BaseSettings.DEBUG.get() && (pref instanceof SwitchPreference
149+
|| pref instanceof EditTextPreference || pref instanceof ListPreference)) {
150+
// Probably a typo in the patches preference declaration.
151+
Logger.printException(() -> "Preference key has no setting: " + key);
146152
}
147153
}
148154
}

app/src/main/java/app/revanced/integrations/shared/settings/preference/ImportExportPreference.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public boolean onPreferenceClick(Preference preference) {
6666
@Override
6767
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
6868
try {
69+
Utils.setEditTextDialogTheme(builder);
70+
6971
// Show the user the settings in JSON format.
7072
builder.setNeutralButton(str("revanced_settings_import_copy"), (dialog, which) -> {
7173
Utils.setClipboard(getEditText().getText().toString());

app/src/main/java/app/revanced/integrations/shared/settings/preference/ResettableEditTextPreference.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import android.util.AttributeSet;
88
import android.widget.Button;
99
import android.widget.EditText;
10+
11+
import app.revanced.integrations.shared.Utils;
1012
import app.revanced.integrations.shared.settings.Setting;
1113
import app.revanced.integrations.shared.Logger;
1214

@@ -33,6 +35,8 @@ public ResettableEditTextPreference(Context context) {
3335
@Override
3436
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
3537
super.onPrepareDialogBuilder(builder);
38+
Utils.setEditTextDialogTheme(builder);
39+
3640
Setting<?> setting = Setting.getSettingFromPath(getKey());
3741
if (setting != null) {
3842
builder.setNeutralButton(str("revanced_settings_reset"), null);

app/src/main/java/app/revanced/integrations/youtube/patches/BackgroundPlaybackPatch.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ public class BackgroundPlaybackPatch {
88
/**
99
* Injection point.
1010
*/
11-
public static boolean playbackIsNotShort() {
11+
public static boolean allowBackgroundPlayback(boolean original) {
12+
if (original) return true;
13+
1214
// Steps to verify most edge cases:
1315
// 1. Open a regular video
1416
// 2. Minimize app (PIP should appear)
Lines changed: 117 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,129 @@
11
package app.revanced.integrations.youtube.patches;
22

3+
import static java.lang.Boolean.FALSE;
4+
import static java.lang.Boolean.TRUE;
5+
36
import android.content.Intent;
4-
import android.net.Uri;
7+
8+
import androidx.annotation.NonNull;
9+
import androidx.annotation.Nullable;
10+
511
import app.revanced.integrations.shared.Logger;
612
import app.revanced.integrations.youtube.settings.Settings;
713

814
@SuppressWarnings("unused")
915
public final class ChangeStartPagePatch {
10-
public static void changeIntent(final Intent intent) {
11-
final var startPage = Settings.START_PAGE.get();
12-
if (startPage.isEmpty()) return;
1316

14-
Logger.printDebug(() -> "Changing start page to " + startPage);
17+
public enum StartPage {
18+
/**
19+
* Unmodified type, and same as un-patched.
20+
*/
21+
ORIGINAL("", null),
22+
23+
/**
24+
* Browse id.
25+
*/
26+
BROWSE("FEguide_builder", TRUE),
27+
EXPLORE("FEexplore", TRUE),
28+
HISTORY("FEhistory", TRUE),
29+
LIBRARY("FElibrary", TRUE),
30+
MOVIE("FEstorefront", TRUE),
31+
SUBSCRIPTIONS("FEsubscriptions", TRUE),
32+
TRENDING("FEtrending", TRUE),
33+
34+
/**
35+
* Channel id, this can be used as a browseId.
36+
*/
37+
GAMING("UCOpNcN46UbXVtpKMrmU4Abg", TRUE),
38+
LIVE("UC4R8DWoMoI7CAwX8_LjQHig", TRUE),
39+
MUSIC("UC-9-kyTW8ZkZNDHQJ6FgpwQ", TRUE),
40+
SPORTS("UCEgdi0XIXXZ-qJOFPf4JSKw", TRUE),
41+
42+
/**
43+
* Playlist id, this can be used as a browseId.
44+
*/
45+
LIKED_VIDEO("VLLL", TRUE),
46+
WATCH_LATER("VLWL", TRUE),
47+
48+
/**
49+
* Intent action.
50+
*/
51+
SEARCH("com.google.android.youtube.action.open.search", FALSE),
52+
SHORTS("com.google.android.youtube.action.open.shorts", FALSE);
53+
54+
@Nullable
55+
final Boolean isBrowseId;
56+
57+
@NonNull
58+
final String id;
59+
60+
StartPage(@NonNull String id, @Nullable Boolean isBrowseId) {
61+
this.id = id;
62+
this.isBrowseId = isBrowseId;
63+
}
64+
65+
private boolean isBrowseId() {
66+
return TRUE.equals(isBrowseId);
67+
}
68+
69+
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
70+
private boolean isIntentAction() {
71+
return FALSE.equals(isBrowseId);
72+
}
73+
}
74+
75+
/**
76+
* Intent action when YouTube is cold started from the launcher.
77+
* <p>
78+
* If you don't check this, the hooking will also apply in the following cases:
79+
* Case 1. The user clicked Shorts button on the YouTube shortcut.
80+
* Case 2. The user clicked Shorts button on the YouTube widget.
81+
* In this case, instead of opening Shorts, the start page specified by the user is opened.
82+
*/
83+
private static final String ACTION_MAIN = "android.intent.action.MAIN";
84+
85+
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
86+
87+
/**
88+
* There is an issue where the back button on the toolbar doesn't work properly.
89+
* As a workaround for this issue, instead of overriding the browserId multiple times, just override it once.
90+
*/
91+
private static boolean appLaunched = false;
92+
93+
public static String overrideBrowseId(@NonNull String original) {
94+
if (!START_PAGE.isBrowseId()) {
95+
return original;
96+
}
97+
98+
if (appLaunched) {
99+
Logger.printDebug(() -> "Ignore override browseId as the app already launched");
100+
return original;
101+
}
102+
appLaunched = true;
103+
104+
Logger.printDebug(() -> "Changing browseId to " + START_PAGE.id);
105+
return START_PAGE.id;
106+
}
107+
108+
public static void overrideIntentAction(@NonNull Intent intent) {
109+
if (!START_PAGE.isIntentAction()) {
110+
return;
111+
}
112+
113+
if (!ACTION_MAIN.equals(intent.getAction())) {
114+
Logger.printDebug(() -> "Ignore override intent action" +
115+
" as the current activity is not the entry point of the application");
116+
return;
117+
}
118+
119+
if (appLaunched) {
120+
Logger.printDebug(() -> "Ignore override intent action as the app already launched");
121+
return;
122+
}
123+
appLaunched = true;
15124

16-
if (startPage.startsWith("www"))
17-
intent.setData(Uri.parse(startPage));
18-
else
19-
intent.setAction("com.google.android.youtube.action." + startPage);
125+
final String intentAction = START_PAGE.id;
126+
Logger.printDebug(() -> "Changing intent action to " + intentAction);
127+
intent.setAction(intentAction);
20128
}
21129
}
Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,47 @@
11
package app.revanced.integrations.youtube.patches;
22

3+
import android.view.View;
4+
5+
import app.revanced.integrations.shared.Logger;
6+
import app.revanced.integrations.shared.Utils;
37
import app.revanced.integrations.youtube.settings.Settings;
48

59
@SuppressWarnings("unused")
610
public final class HidePlayerButtonsPatch {
711

12+
private static final boolean HIDE_PLAYER_BUTTONS_ENABLED = Settings.HIDE_PLAYER_BUTTONS.get();
13+
14+
private static final int PLAYER_CONTROL_PREVIOUS_BUTTON_TOUCH_AREA_ID =
15+
Utils.getResourceIdentifier("player_control_previous_button_touch_area", "id");
16+
17+
private static final int PLAYER_CONTROL_NEXT_BUTTON_TOUCH_AREA_ID =
18+
Utils.getResourceIdentifier("player_control_next_button_touch_area", "id");
19+
820
/**
921
* Injection point.
1022
*/
11-
public static boolean previousOrNextButtonIsVisible(boolean previousOrNextButtonVisible) {
12-
if (Settings.HIDE_PLAYER_BUTTONS.get()) {
13-
return false;
23+
public static void hidePreviousNextButtons(View parentView) {
24+
if (!HIDE_PLAYER_BUTTONS_ENABLED) {
25+
return;
26+
}
27+
28+
// Must use a deferred call to main thread to hide the button.
29+
// Otherwise the layout crashes if set to hidden now.
30+
Utils.runOnMainThread(() -> {
31+
hideView(parentView, PLAYER_CONTROL_PREVIOUS_BUTTON_TOUCH_AREA_ID);
32+
hideView(parentView, PLAYER_CONTROL_NEXT_BUTTON_TOUCH_AREA_ID);
33+
});
34+
}
35+
36+
private static void hideView(View parentView, int resourceId) {
37+
View nextPreviousButton = parentView.findViewById(resourceId);
38+
39+
if (nextPreviousButton == null) {
40+
Logger.printException(() -> "Could not find player previous/next button");
41+
return;
1442
}
15-
return previousOrNextButtonVisible;
43+
44+
Logger.printDebug(() -> "Hiding previous/next button");
45+
Utils.hideViewByRemovingFromParentUnderCondition(true, nextPreviousButton);
1646
}
1747
}

0 commit comments

Comments
 (0)