Compare commits

..

No commits in common. "revanced-extended" and "v5.6.1-dev.4" have entirely different histories.

138 changed files with 1342 additions and 2482 deletions

165
README.md
View File

@ -11,73 +11,73 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `Alternative thumbnails` | Adds options to replace video thumbnails using the DeArrow API or image captures from the video. | 19.05.36 ~ 19.47.53 |
| `Ambient mode control` | Adds options to disable Ambient mode and to bypass Ambient mode restrictions. | 19.05.36 ~ 19.47.53 |
| `Bypass URL redirects` | Adds an option to bypass URL redirects and open the original URL directly. | 19.05.36 ~ 19.47.53 |
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 19.05.36 ~ 19.47.53 |
| `Change form factor` | Adds an option to change the UI appearance to a phone, tablet, or automotive device. | 19.05.36 ~ 19.47.53 |
| `Change live ring click action` | Adds an option to open the channel instead of the live stream when clicking on the live ring. | 19.05.36 ~ 19.47.53 |
| `Change player flyout menu toggles` | Adds an option to use text toggles instead of switch toggles within the additional settings menu. | 19.05.36 ~ 19.47.53 |
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 19.05.36 ~ 19.47.53 |
| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 19.05.36 ~ 19.47.53 |
| `Custom Shorts action buttons` | Changes, at compile time, the icon of the action buttons of the Shorts player. | 19.05.36 ~ 19.47.53 |
| `Custom branding icon for YouTube` | Changes the YouTube app icon to the icon specified in patch options. | 19.05.36 ~ 19.47.53 |
| `Custom branding name for YouTube` | Changes the YouTube app name to the name specified in patch options. | 19.05.36 ~ 19.47.53 |
| `Custom double tap length` | Adds Double-tap to seek values that are specified in patch options. | 19.05.36 ~ 19.47.53 |
| `Custom header for YouTube` | Applies a custom header in the top left corner within the app. | 19.05.36 ~ 19.47.53 |
| `Description components` | Adds options to hide and disable description components. | 19.05.36 ~ 19.47.53 |
| `Disable QUIC protocol` | Adds an option to disable CronetEngine's QUIC protocol. | 19.05.36 ~ 19.47.53 |
| `Disable forced auto audio tracks` | Adds an option to disable audio tracks from being automatically enabled. | 19.05.36 ~ 19.47.53 |
| `Disable forced auto captions` | Adds an option to disable captions from being automatically enabled. | 19.05.36 ~ 19.47.53 |
| `Disable haptic feedback` | Adds options to disable haptic feedback when swiping in the video player. | 19.05.36 ~ 19.47.53 |
| `Disable layout updates` | Adds an option to disable layout updates by server. | 19.05.36 ~ 19.47.53 |
| `Disable resuming Miniplayer on startup` | Adds an option to disable the Miniplayer 'Continue watching' from resuming on app startup. | 19.05.36 ~ 19.47.53 |
| `Disable resuming Shorts on startup` | Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched. | 19.05.36 ~ 19.47.53 |
| `Disable splash animation` | Adds an option to disable the splash animation on app startup. | 19.05.36 ~ 19.47.53 |
| `Enable OPUS codec` | Adds an option to enable the OPUS audio codec if the player response includes it. | 19.05.36 ~ 19.47.53 |
| `Enable debug logging` | Adds an option to enable debug logging. | 19.05.36 ~ 19.47.53 |
| `Enable gradient loading screen` | Adds an option to enable the gradient loading screen. | 19.05.36 ~ 19.47.53 |
| `Force hide player buttons background` | Removes, at compile time, the dark background surrounding the video player controls. | 19.05.36 ~ 19.47.53 |
| `Fullscreen components` | Adds options to hide or change components related to fullscreen. | 19.05.36 ~ 19.47.53 |
| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 19.05.36 ~ 19.47.53 |
| `Hide Shorts dimming` | Removes, at compile time, the dimming effect at the top and bottom of Shorts videos. | 19.05.36 ~ 19.47.53 |
| `Hide accessibility controls dialog` | Removes, at compile time, accessibility controls dialog 'Turn on accessibility controls for the video player?'. | 19.05.36 ~ 19.47.53 |
| `Hide action buttons` | Adds options to hide action buttons under videos. | 19.05.36 ~ 19.47.53 |
| `Hide ads` | Adds options to hide ads. | 19.05.36 ~ 19.47.53 |
| `Hide comments components` | Adds options to hide components related to comments. | 19.05.36 ~ 19.47.53 |
| `Hide feed components` | Adds options to hide components related to feeds. | 19.05.36 ~ 19.47.53 |
| `Hide feed flyout menu` | Adds the ability to hide feed flyout menu components using a custom filter. | 19.05.36 ~ 19.47.53 |
| `Hide layout components` | Adds options to hide general layout components. | 19.05.36 ~ 19.47.53 |
| `Hide player buttons` | Adds options to hide buttons in the video player. | 19.05.36 ~ 19.47.53 |
| `Hide player flyout menu` | Adds options to hide player flyout menu components. | 19.05.36 ~ 19.47.53 |
| `Hide shortcuts` | Remove, at compile time, the app shortcuts that appears when the app icon is long pressed. | 19.05.36 ~ 19.47.53 |
| `Hook YouTube Music actions` | Adds support for opening music in RVX Music using the in-app YouTube Music button. | 19.05.36 ~ 19.47.53 |
| `Hook download actions` | Adds support to download videos with an external downloader app using the in-app download button. | 19.05.36 ~ 19.47.53 |
| `MaterialYou` | Applies the MaterialYou theme for Android 12+ devices. | 19.05.36 ~ 19.47.53 |
| `Miniplayer` | Adds options to change the in-app minimized player, and if patching target 19.16+ adds options to use modern miniplayers. | 19.05.36 ~ 19.47.53 |
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 19.05.36 ~ 19.47.53 |
| `Open links externally` | Adds an option to always open links in your browser instead of the in-app browser. | 19.05.36 ~ 19.47.53 |
| `Overlay buttons` | Adds options to display useful overlay buttons in the video player. | 19.05.36 ~ 19.47.53 |
| `Player components` | Adds options to hide or change components related to the video player. | 19.05.36 ~ 19.47.53 |
| `Remove background playback restrictions` | Removes restrictions on background playback, including for music and kids videos. | 19.05.36 ~ 19.47.53 |
| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 19.05.36 ~ 19.47.53 |
| `Return YouTube Dislike` | Adds an option to show the dislike count of videos using the Return YouTube Dislike API. | 19.05.36 ~ 19.47.53 |
| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 19.05.36 ~ 19.47.53 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 19.05.36 ~ 19.47.53 |
| `Seekbar components` | Adds options to hide or change components related to the seekbar. | 19.05.36 ~ 19.47.53 |
| `Settings for YouTube` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 19.05.36 ~ 19.47.53 |
| `Shorts components` | Adds options to hide or change components related to YouTube Shorts. | 19.05.36 ~ 19.47.53 |
| `Snack bar components` | Adds options to hide or change components related to the snack bar. | 19.05.36 ~ 19.47.53 |
| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as sponsored content. | 19.05.36 ~ 19.47.53 |
| `Spoof app version` | Adds options to spoof the YouTube client version. This can be used to restore old UI elements and features. | 19.05.36 ~ 19.47.53 |
| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 19.05.36 ~ 19.47.53 |
| `Swipe controls` | Adds options for controlling volume and brightness with swiping, and whether to enter fullscreen when swiping down below the player. | 19.05.36 ~ 19.47.53 |
| `Theme` | Changes the app's themes to the values specified in patch options. | 19.05.36 ~ 19.47.53 |
| `Toolbar components` | Adds options to hide or change components located on the toolbar, such as the search bar, header, and toolbar buttons. | 19.05.36 ~ 19.47.53 |
| `Translations for YouTube` | Add translations or remove string resources. | 19.05.36 ~ 19.47.53 |
| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 19.05.36 ~ 19.47.53 |
| `Visual preferences icons for YouTube` | Adds icons to specific preferences in the settings. | 19.05.36 ~ 19.47.53 |
| `Watch history` | Adds an option to change the domain of the watch history or check its status. | 19.05.36 ~ 19.47.53 |
| `Alternative thumbnails` | Adds options to replace video thumbnails using the DeArrow API or image captures from the video. | 19.05.36 ~ 20.03.43 |
| `Ambient mode control` | Adds options to disable Ambient mode and to bypass Ambient mode restrictions. | 19.05.36 ~ 20.03.43 |
| `Bypass URL redirects` | Adds an option to bypass URL redirects and open the original URL directly. | 19.05.36 ~ 20.03.43 |
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 19.05.36 ~ 20.03.43 |
| `Change form factor` | Adds an option to change the UI appearance to a phone, tablet, or automotive device. | 19.05.36 ~ 20.03.43 |
| `Change live ring click action` | Adds an option to open the channel instead of the live stream when clicking on the live ring. | 19.05.36 ~ 20.03.43 |
| `Change player flyout menu toggles` | Adds an option to use text toggles instead of switch toggles within the additional settings menu. | 19.05.36 ~ 20.03.43 |
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 19.05.36 ~ 20.03.43 |
| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 19.05.36 ~ 20.03.43 |
| `Custom Shorts action buttons` | Changes, at compile time, the icon of the action buttons of the Shorts player. | 19.05.36 ~ 20.03.43 |
| `Custom branding icon for YouTube` | Changes the YouTube app icon to the icon specified in patch options. | 19.05.36 ~ 20.03.43 |
| `Custom branding name for YouTube` | Changes the YouTube app name to the name specified in patch options. | 19.05.36 ~ 20.03.43 |
| `Custom double tap length` | Adds Double-tap to seek values that are specified in patch options. | 19.05.36 ~ 20.03.43 |
| `Custom header for YouTube` | Applies a custom header in the top left corner within the app. | 19.05.36 ~ 20.03.43 |
| `Description components` | Adds options to hide and disable description components. | 19.05.36 ~ 20.03.43 |
| `Disable QUIC protocol` | Adds an option to disable CronetEngine's QUIC protocol. | 19.05.36 ~ 20.03.43 |
| `Disable forced auto audio tracks` | Adds an option to disable audio tracks from being automatically enabled. | 19.05.36 ~ 20.03.43 |
| `Disable forced auto captions` | Adds an option to disable captions from being automatically enabled. | 19.05.36 ~ 20.03.43 |
| `Disable haptic feedback` | Adds options to disable haptic feedback when swiping in the video player. | 19.05.36 ~ 20.03.43 |
| `Disable layout updates` | Adds an option to disable layout updates by server. | 19.05.36 ~ 20.03.43 |
| `Disable resuming Miniplayer on startup` | Adds an option to disable the Miniplayer 'Continue watching' from resuming on app startup. | 19.05.36 ~ 20.03.43 |
| `Disable resuming Shorts on startup` | Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched. | 19.05.36 ~ 20.03.43 |
| `Disable splash animation` | Adds an option to disable the splash animation on app startup. | 19.05.36 ~ 20.03.43 |
| `Enable OPUS codec` | Adds an option to enable the OPUS audio codec if the player response includes it. | 19.05.36 ~ 20.03.43 |
| `Enable debug logging` | Adds an option to enable debug logging. | 19.05.36 ~ 20.03.43 |
| `Enable gradient loading screen` | Adds an option to enable the gradient loading screen. | 19.05.36 ~ 20.03.43 |
| `Force hide player buttons background` | Removes, at compile time, the dark background surrounding the video player controls. | 19.05.36 ~ 20.03.43 |
| `Fullscreen components` | Adds options to hide or change components related to fullscreen. | 19.05.36 ~ 20.03.43 |
| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 19.05.36 ~ 20.03.43 |
| `Hide Shorts dimming` | Removes, at compile time, the dimming effect at the top and bottom of Shorts videos. | 19.05.36 ~ 20.03.43 |
| `Hide accessibility controls dialog` | Removes, at compile time, accessibility controls dialog 'Turn on accessibility controls for the video player?'. | 19.05.36 ~ 20.03.43 |
| `Hide action buttons` | Adds options to hide action buttons under videos. | 19.05.36 ~ 20.03.43 |
| `Hide ads` | Adds options to hide ads. | 19.05.36 ~ 20.03.43 |
| `Hide comments components` | Adds options to hide components related to comments. | 19.05.36 ~ 20.03.43 |
| `Hide feed components` | Adds options to hide components related to feeds. | 19.05.36 ~ 20.03.43 |
| `Hide feed flyout menu` | Adds the ability to hide feed flyout menu components using a custom filter. | 19.05.36 ~ 20.03.43 |
| `Hide layout components` | Adds options to hide general layout components. | 19.05.36 ~ 20.03.43 |
| `Hide player buttons` | Adds options to hide buttons in the video player. | 19.05.36 ~ 20.03.43 |
| `Hide player flyout menu` | Adds options to hide player flyout menu components. | 19.05.36 ~ 20.03.43 |
| `Hide shortcuts` | Remove, at compile time, the app shortcuts that appears when the app icon is long pressed. | 19.05.36 ~ 20.03.43 |
| `Hook YouTube Music actions` | Adds support for opening music in RVX Music using the in-app YouTube Music button. | 19.05.36 ~ 20.03.43 |
| `Hook download actions` | Adds support to download videos with an external downloader app using the in-app download button. | 19.05.36 ~ 20.03.43 |
| `MaterialYou` | Applies the MaterialYou theme for Android 12+ devices. | 19.05.36 ~ 20.03.43 |
| `Miniplayer` | Adds options to change the in-app minimized player, and if patching target 19.16+ adds options to use modern miniplayers. | 19.05.36 ~ 20.03.43 |
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 19.05.36 ~ 20.03.43 |
| `Open links externally` | Adds an option to always open links in your browser instead of the in-app browser. | 19.05.36 ~ 20.03.43 |
| `Overlay buttons` | Adds options to display useful overlay buttons in the video player. | 19.05.36 ~ 20.03.43 |
| `Player components` | Adds options to hide or change components related to the video player. | 19.05.36 ~ 20.03.43 |
| `Remove background playback restrictions` | Removes restrictions on background playback, including for music and kids videos. | 19.05.36 ~ 20.03.43 |
| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 19.05.36 ~ 20.03.43 |
| `Return YouTube Dislike` | Adds an option to show the dislike count of videos using the Return YouTube Dislike API. | 19.05.36 ~ 20.03.43 |
| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 19.05.36 ~ 20.03.43 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 19.05.36 ~ 20.03.43 |
| `Seekbar components` | Adds options to hide or change components related to the seekbar. | 19.05.36 ~ 20.03.43 |
| `Settings for YouTube` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 19.05.36 ~ 20.03.43 |
| `Shorts components` | Adds options to hide or change components related to YouTube Shorts. | 19.05.36 ~ 20.03.43 |
| `Snack bar components` | Adds options to hide or change components related to the snack bar. | 19.05.36 ~ 20.03.43 |
| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as sponsored content. | 19.05.36 ~ 20.03.43 |
| `Spoof app version` | Adds options to spoof the YouTube client version. This can be used to restore old UI elements and features. | 19.05.36 ~ 20.03.43 |
| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 19.05.36 ~ 20.03.43 |
| `Swipe controls` | Adds options for controlling volume and brightness with swiping, and whether to enter fullscreen when swiping down below the player. | 19.05.36 ~ 20.03.43 |
| `Theme` | Changes the app's themes to the values specified in patch options. | 19.05.36 ~ 20.03.43 |
| `Toolbar components` | Adds options to hide or change components located on the toolbar, such as the search bar, header, and toolbar buttons. | 19.05.36 ~ 20.03.43 |
| `Translations for YouTube` | Add translations or remove string resources. | 19.05.36 ~ 20.03.43 |
| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 19.05.36 ~ 20.03.43 |
| `Visual preferences icons for YouTube` | Adds icons to specific preferences in the settings. | 19.05.36 ~ 20.03.43 |
| `Watch history` | Adds an option to change the domain of the watch history or check its status. | 19.05.36 ~ 20.03.43 |
</details>
### [📦 `com.google.android.apps.youtube.music`](https://play.google.com/store/apps/details?id=com.google.android.apps.youtube.music)
@ -134,19 +134,19 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `Change package name` | Changes the package name for Reddit to the name specified in patch options. | 2024.17.0 ~ 2025.12.1 |
| `Custom branding name for Reddit` | Changes the Reddit app name to the name specified in patch options. | 2024.17.0 ~ 2025.12.1 |
| `Disable screenshot popup` | Adds an option to disable the popup that appears when taking a screenshot. | 2024.17.0 ~ 2025.12.1 |
| `Hide Recently Visited shelf` | Adds an option to hide the Recently Visited shelf in the sidebar. | 2024.17.0 ~ 2025.12.1 |
| `Hide ads` | Adds options to hide ads. | 2024.17.0 ~ 2025.12.1 |
| `Hide navigation buttons` | Adds options to hide buttons in the navigation bar. | 2024.17.0 ~ 2025.12.1 |
| `Hide recommended communities shelf` | Adds an option to hide the recommended communities shelves in subreddits. | 2024.17.0 ~ 2025.12.1 |
| `Open links directly` | Adds an option to skip over redirection URLs in external links. | 2024.17.0 ~ 2025.12.1 |
| `Open links externally` | Adds an option to always open links in your browser instead of in the in-app-browser. | 2024.17.0 ~ 2025.12.1 |
| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.1 |
| `Remove subreddit dialog` | Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically. | 2024.17.0 ~ 2025.12.1 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 2024.17.0 ~ 2025.12.1 |
| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 2024.17.0 ~ 2025.12.1 |
| `Change package name` | Changes the package name for Reddit to the name specified in patch options. | 2024.17.0 ~ 2025.12.0 |
| `Custom branding name for Reddit` | Changes the Reddit app name to the name specified in patch options. | 2024.17.0 ~ 2025.12.0 |
| `Disable screenshot popup` | Adds an option to disable the popup that appears when taking a screenshot. | 2024.17.0 ~ 2025.12.0 |
| `Hide Recently Visited shelf` | Adds an option to hide the Recently Visited shelf in the sidebar. | 2024.17.0 ~ 2025.12.0 |
| `Hide ads` | Adds options to hide ads. | 2024.17.0 ~ 2025.12.0 |
| `Hide navigation buttons` | Adds options to hide buttons in the navigation bar. | 2024.17.0 ~ 2025.12.0 |
| `Hide recommended communities shelf` | Adds an option to hide the recommended communities shelves in subreddits. | 2024.17.0 ~ 2025.12.0 |
| `Open links directly` | Adds an option to skip over redirection URLs in external links. | 2024.17.0 ~ 2025.12.0 |
| `Open links externally` | Adds an option to always open links in your browser instead of in the in-app-browser. | 2024.17.0 ~ 2025.12.0 |
| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.0 |
| `Remove subreddit dialog` | Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically. | 2024.17.0 ~ 2025.12.0 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 2024.17.0 ~ 2025.12.0 |
| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 2024.17.0 ~ 2025.12.0 |
</details>
@ -169,7 +169,8 @@ Example:
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -200,7 +201,7 @@ Example:
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []

View File

@ -2,10 +2,7 @@ package app.revanced.extension.reddit.patches;
import static app.revanced.extension.shared.utils.StringRef.str;
import android.app.Dialog;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
@ -37,35 +34,6 @@ public class RemoveSubRedditDialogPatch {
return Settings.REMOVE_NSFW_DIALOG.get() || hasBeenVisited;
}
public static void dismissNSFWDialog(Object customDialog) {
if (Settings.REMOVE_NSFW_DIALOG.get() &&
customDialog instanceof Dialog dialog) {
Window window = dialog.getWindow();
if (window != null) {
WindowManager.LayoutParams params = window.getAttributes();
params.height = 0;
params.width = 0;
// Change the size of dialog to 0.
window.setAttributes(params);
// Disable dialog's background dim.
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
// Hide DecorView.
View decorView = window.getDecorView();
decorView.setVisibility(View.GONE);
// Dismiss dialog.
dialog.dismiss();
}
}
}
public static boolean removeNSFWDialog() {
return Settings.REMOVE_NSFW_DIALOG.get();
}
public static boolean spoofLoggedInStatus(boolean isLoggedIn) {
return !Settings.REMOVE_NOTIFICATION_DIALOG.get() && isLoggedIn;
}

View File

@ -5,7 +5,7 @@ import app.revanced.extension.reddit.settings.Settings;
@SuppressWarnings("unused")
public class ScreenshotPopupPatch {
public static Boolean disableScreenshotPopup(Boolean original) {
return Settings.DISABLE_SCREENSHOT_POPUP.get() ? Boolean.FALSE : original;
public static boolean disableScreenshotPopup() {
return Settings.DISABLE_SCREENSHOT_POPUP.get();
}
}

View File

@ -13,7 +13,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NEW_POST_ADS = new BooleanSetting("revanced_hide_new_post_ads", TRUE, true);
// Layout
public static final BooleanSetting DISABLE_SCREENSHOT_POPUP = new BooleanSetting("revanced_disable_screenshot_popup", TRUE, true);
public static final BooleanSetting DISABLE_SCREENSHOT_POPUP = new BooleanSetting("revanced_disable_screenshot_popup", TRUE);
public static final BooleanSetting HIDE_CHAT_BUTTON = new BooleanSetting("revanced_hide_chat_button", FALSE, true);
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", FALSE, true);
public static final BooleanSetting HIDE_DISCOVER_BUTTON = new BooleanSetting("revanced_hide_discover_button", FALSE, true);

View File

@ -279,7 +279,6 @@ object InnerTubeRequestBody {
route: CompiledRoute,
clientType: YouTubeAppClient.ClientType,
requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
) = getInnerTubeResponseConnectionFromRoute(
@ -289,7 +288,6 @@ object InnerTubeRequestBody {
clientVersion = clientType.clientVersion,
supportsCookies = clientType.supportsCookies,
requestHeader = requestHeader,
dataSyncId = dataSyncId,
connectTimeout = connectTimeout,
readTimeout = readTimeout,
)
@ -299,7 +297,6 @@ object InnerTubeRequestBody {
route: CompiledRoute,
clientType: YouTubeWebClient.ClientType,
requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
) = getInnerTubeResponseConnectionFromRoute(
@ -308,7 +305,6 @@ object InnerTubeRequestBody {
clientId = clientType.id.toString(),
clientVersion = clientType.clientVersion,
requestHeader = requestHeader,
dataSyncId = dataSyncId,
connectTimeout = connectTimeout,
readTimeout = readTimeout,
)
@ -321,7 +317,6 @@ object InnerTubeRequestBody {
clientVersion: String,
supportsCookies: Boolean = true,
requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
): HttpURLConnection {
@ -353,11 +348,6 @@ object InnerTubeRequestBody {
}
}
// Used to identify brand accounts
if (dataSyncId != null && dataSyncId.isNotEmpty()) {
connection.setRequestProperty("X-Goog-PageId", dataSyncId)
}
return connection
}

View File

@ -69,21 +69,11 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
* Skip response encryption in OnesiePlayerRequest.
*/
public static boolean skipResponseEncryption(boolean original) {
if (!SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION) {
return original;
if (SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION) {
return false;
}
return false;
}
/**
* Injection point.
* Turns off a feature flag that interferes with video playback.
*/
public static boolean usePlaybackStartFeatureFlag(boolean original) {
if (!SPOOF_STREAMING_DATA) {
return original;
}
return false;
return original;
}
/**

View File

@ -240,11 +240,7 @@ class StreamingDataRequest private constructor(
}
handleConnectionError(str("revanced_spoof_streaming_data_failed_forbidden"), null, true)
handleConnectionError(
str("revanced_spoof_streaming_data_failed_forbidden_suggestion"),
null,
true
)
handleConnectionError(str("revanced_spoof_streaming_data_failed_forbidden_suggestion"), null, true)
return null
}
}

View File

@ -60,7 +60,6 @@ public class Utils {
private static WeakReference<Activity> activityRef = new WeakReference<>(null);
@SuppressLint("StaticFieldLeak")
private static volatile Context context;
private static Locale contextLocale;
protected Utils() {
} // utility class
@ -309,51 +308,34 @@ public class Utils {
* @return Context with locale applied.
*/
public static Context getLocalizedContext(Context mContext) {
try {
Activity mActivity = activityRef.get();
if (mActivity != null && mContext != null) {
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
// Locale of Application.
Locale applicationLocale = language == AppLanguage.DEFAULT
? mActivity.getResources().getConfiguration().locale
: language.getLocale();
// Locale of Context.
Locale contextLocale = mContext.getResources().getConfiguration().locale;
// If they are different, overrides the Locale of the Context and resource.
if (applicationLocale != contextLocale) {
Utils.contextLocale = contextLocale;
// If they are different, overrides the Locale of the Context and resource.
Locale.setDefault(applicationLocale);
Configuration configuration = new Configuration(mContext.getResources().getConfiguration());
configuration.setLocale(applicationLocale);
return mContext.createConfigurationContext(configuration);
}
}
} catch (Exception ex) {
Logger.printException(() -> "getLocalizedContext failed", ex);
Activity mActivity = activityRef.get();
if (mActivity == null) {
return mContext;
}
if (mContext == null) {
return null;
}
return mContext;
}
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
public static void resetLocalizedContext() {
try {
if (contextLocale != null) {
Locale.setDefault(contextLocale);
Context mContext = getContext();
if (mContext != null) {
Configuration config = mContext.getResources().getConfiguration();
config.setLocale(contextLocale);
setContext(mContext.createConfigurationContext(config));
}
}
} catch (Exception ex) {
Logger.printException(() -> "resetLocalizedContext failed", ex);
// Locale of Application.
Locale applicationLocale = language == AppLanguage.DEFAULT
? mActivity.getResources().getConfiguration().locale
: language.getLocale();
// Locale of Context.
Locale contextLocale = mContext.getResources().getConfiguration().locale;
// If they are identical, no need to override them.
if (applicationLocale == contextLocale) {
return mContext;
}
// If they are different, overrides the Locale of the Context and resource.
Locale.setDefault(applicationLocale);
Configuration configuration = new Configuration(mContext.getResources().getConfiguration());
configuration.setLocale(applicationLocale);
return mContext.createConfigurationContext(configuration);
}
public static void setActivity(Activity mainActivity) {
@ -371,6 +353,14 @@ public class Utils {
// Must initially set context to check the app language.
context = appContext;
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language.
Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale());
context = appContext.createConfigurationContext(config);
}
}
public static void setClipboard(@NonNull String text) {
@ -548,6 +538,14 @@ public class Utils {
return Build.VERSION.SDK_INT >= sdk;
}
public static int dpToPx(float dp) {
if (context == null) {
return (int) dp;
} else {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
}
public static int dpToPx(int dp) {
if (context == null) {
return dp;

View File

@ -183,8 +183,8 @@ public class PlayerPatch {
// The type of descriptionView can be either ViewGroup or TextView. (A/B tests)
// If the type of descriptionView is TextView, longer delay is required.
final long delayMillis = descriptionView instanceof TextView
? 750
: 200;
? 500
: 100;
Utils.runOnMainThreadDelayed(() -> Utils.clickView(descriptionView), delayMillis);
}

View File

@ -155,34 +155,6 @@ public final class CustomActionsPatch {
Logger.printInfo(() -> customAction.name() + bottomSheetMenuClass + bottomSheetMenuList + bottomSheetMenuObject);
}
/**
* Injection point.
*/
public static boolean onBottomSheetMenuItemClick(View view) {
try {
if (view instanceof ViewGroup viewGroup) {
TextView textView = Utils.getChildView(viewGroup, v -> v instanceof TextView);
if (textView != null) {
String menuTitle = textView.getText().toString();
for (CustomAction customAction : CustomAction.values()) {
if (customAction.getLabel().equals(menuTitle)) {
View.OnLongClickListener onLongClick = customAction.getOnLongClickListener();
if (onLongClick != null) {
view.setOnLongClickListener(onLongClick);
}
customAction.getOnClickAction().run();
return true;
}
}
}
}
} catch (Exception ex) {
Logger.printException(() -> "onBottomSheetMenuItemClick failed");
}
return false;
}
/**
* Injection point.
*/
@ -207,9 +179,8 @@ public final class CustomActionsPatch {
if (recyclerView.getChildAt(childCount - i - 1) instanceof ViewGroup parentViewGroup) {
childCount = recyclerView.getChildCount();
if (childCount > 3 && parentViewGroup.getChildAt(1) instanceof TextView textView) {
String menuTitle = textView.getText().toString();
for (CustomAction customAction : CustomAction.values()) {
if (customAction.getLabel().equals(menuTitle)) {
if (customAction.getLabel().equals(textView.getText().toString())) {
View.OnClickListener onClick = customAction.getOnClickListener();
View.OnLongClickListener onLongClick = customAction.getOnLongClickListener();
recyclerViewRef = new WeakReference<>(recyclerView);

View File

@ -9,11 +9,6 @@ public class PatchStatus {
return false;
}
// Modified by a patch. Do not touch.
public static boolean OldSeekbarThumbnailsDefaultBoolean() {
return false;
}
public static boolean OldSplashAnimation() {
// Replace this with true if the Restore old splash animation (Custom branding icon) succeeds
return false;
@ -45,22 +40,23 @@ public class PatchStatus {
return false;
}
public static String SpoofAppVersionDefaultString() {
return "18.17.43";
}
public static boolean ToolBarComponents() {
// Replace this with true if the Toolbar components patch succeeds
return false;
}
public static long PatchedTime() {
return 0L;
}
public static String SpoofAppVersionDefaultString() {
return "18.17.43";
}
// Modified by a patch. Do not touch.
public static String RVXMusicPackageName() {
return "com.google.android.apps.youtube.music";
}
// Modified by a patch. Do not touch.
public static boolean OldSeekbarThumbnailsDefaultBoolean() {
return false;
}
}

View File

@ -1,11 +1,5 @@
package app.revanced.extension.youtube.patches.utils;
import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.shared.utils.Utils.runOnMainThreadDelayed;
import static app.revanced.extension.youtube.utils.VideoUtils.dismissPlayer;
import static app.revanced.extension.youtube.utils.VideoUtils.launchVideoExternalDownloader;
import static app.revanced.extension.youtube.utils.VideoUtils.openPlaylist;
import android.content.Context;
import android.view.KeyEvent;
import android.widget.LinearLayout;
@ -34,61 +28,62 @@ import app.revanced.extension.youtube.patches.utils.requests.EditPlaylistRequest
import app.revanced.extension.youtube.patches.utils.requests.GetPlaylistsRequest;
import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.VideoInformation;
import app.revanced.extension.youtube.utils.AuthUtils;
import app.revanced.extension.youtube.utils.ExtendedUtils;
import app.revanced.extension.youtube.utils.VideoUtils;
import kotlin.Pair;
// TODO: Implement sync queue and clean up code.
@SuppressWarnings({"unused", "StaticFieldLeak"})
public class PlaylistPatch extends AuthUtils {
public class PlaylistPatch extends VideoUtils {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String[] REQUEST_HEADER_KEYS = {
AUTHORIZATION_HEADER,
"X-GOOG-API-FORMAT-VERSION",
"X-Goog-Visitor-Id"
};
private static final boolean QUEUE_MANAGER =
Settings.OVERLAY_BUTTON_EXTERNAL_DOWNLOADER_QUEUE_MANAGER.get()
|| Settings.OVERRIDE_VIDEO_DOWNLOAD_BUTTON_QUEUE_MANAGER.get();
private static Context mContext;
private static volatile String authorization = "";
private static volatile boolean isIncognito = false;
private static volatile Map<String, String> requestHeader;
private static volatile String playlistId = "";
private static volatile String videoId = "";
private static String checkFailedAuth;
private static String checkFailedPlaylistId;
private static String checkFailedQueue;
private static String checkFailedVideoId;
private static String checkFailedGeneric;
private static final String checkFailedAuth =
ResourceUtils.getString("revanced_queue_manager_check_failed_auth");
private static final String checkFailedPlaylistId =
ResourceUtils.getString("revanced_queue_manager_check_failed_playlist_id");
private static final String checkFailedQueue =
ResourceUtils.getString("revanced_queue_manager_check_failed_queue");
private static final String checkFailedVideoId =
ResourceUtils.getString("revanced_queue_manager_check_failed_video_id");
private static final String checkFailedGeneric =
ResourceUtils.getString("revanced_queue_manager_check_failed_generic");
private static String fetchFailedAdd;
private static String fetchFailedCreate;
private static String fetchFailedDelete;
private static String fetchFailedRemove;
private static String fetchFailedSave;
private static final String fetchFailedAdd =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_add");
private static final String fetchFailedCreate =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_create");
private static final String fetchFailedDelete =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_delete");
private static final String fetchFailedRemove =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_remove");
private static final String fetchFailedSave =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_save");
private static String fetchSucceededAdd;
private static String fetchSucceededCreate;
private static String fetchSucceededDelete;
private static String fetchSucceededRemove;
private static String fetchSucceededSave;
static {
Context mContext = Utils.getContext();
if (mContext != null && mContext.getResources() != null) {
checkFailedAuth = str("revanced_queue_manager_check_failed_auth");
checkFailedPlaylistId = str("revanced_queue_manager_check_failed_playlist_id");
checkFailedQueue = str("revanced_queue_manager_check_failed_queue");
checkFailedVideoId = str("revanced_queue_manager_check_failed_video_id");
checkFailedGeneric = str("revanced_queue_manager_check_failed_generic");
fetchFailedAdd = str("revanced_queue_manager_fetch_failed_add");
fetchFailedCreate = str("revanced_queue_manager_fetch_failed_create");
fetchFailedDelete = str("revanced_queue_manager_fetch_failed_delete");
fetchFailedRemove = str("revanced_queue_manager_fetch_failed_remove");
fetchFailedSave = str("revanced_queue_manager_fetch_failed_save");
fetchSucceededAdd = str("revanced_queue_manager_fetch_succeeded_add");
fetchSucceededCreate = str("revanced_queue_manager_fetch_succeeded_create");
fetchSucceededDelete = str("revanced_queue_manager_fetch_succeeded_delete");
fetchSucceededRemove = str("revanced_queue_manager_fetch_succeeded_remove");
fetchSucceededSave = str("revanced_queue_manager_fetch_succeeded_save");
}
}
private static final String fetchSucceededAdd =
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_add");
private static final String fetchSucceededCreate =
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_create");
private static final String fetchSucceededDelete =
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_delete");
private static final String fetchSucceededRemove =
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_remove");
private static final String fetchSucceededSave =
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_save");
@GuardedBy("itself")
private static final BidiMap<String, String> lastVideoIds = new DualHashBidiMap<>();
@ -118,12 +113,20 @@ public class PlaylistPatch extends AuthUtils {
if (videoId != null) {
lastVideoIds.remove(videoId, setVideoId);
EditPlaylistRequest.clearVideoId(videoId);
Logger.printDebug(() -> "Video removed by YouTube flyout menu: " + videoId);
}
}
}
}
/**
* Injection point.
*/
public static void setIncognitoStatus(boolean incognito) {
if (QUEUE_MANAGER) {
isIncognito = incognito;
}
}
/**
* Injection point.
*/
@ -133,6 +136,30 @@ public class PlaylistPatch extends AuthUtils {
}
}
/**
* Injection point.
*/
public static void setRequestHeaders(String url, Map<String, String> requestHeaders) {
if (QUEUE_MANAGER) {
try {
// Save requestHeaders whenever an account is switched.
String auth = requestHeaders.get(AUTHORIZATION_HEADER);
if (auth == null || authorization.equals(auth)) {
return;
}
for (String key : REQUEST_HEADER_KEYS) {
if (requestHeaders.get(key) == null) {
return;
}
}
authorization = auth;
requestHeader = requestHeaders;
} catch (Exception ex) {
Logger.printException(() -> "setRequestHeaders failure", ex);
}
}
}
/**
* Invoked by extension.
*/
@ -144,7 +171,7 @@ public class PlaylistPatch extends AuthUtils {
* Invoked by extension.
*/
public static void prepareDialogBuilder(@NonNull String currentVideoId) {
if (authorization.isEmpty() || (dataSyncId.isEmpty() && isIncognito)) {
if (authorization.isEmpty() || isIncognito) {
handleCheckError(checkFailedAuth);
return;
}
@ -153,22 +180,9 @@ public class PlaylistPatch extends AuthUtils {
} else {
videoId = currentVideoId;
synchronized (lastVideoIds) {
QueueManager[] customActionsEntries;
boolean canReload = PlayerType.getCurrent().isMaximizedOrFullscreen() &&
lastVideoIds.get(VideoInformation.getVideoId()) != null;
if (playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null) {
if (canReload) {
customActionsEntries = QueueManager.addToQueueWithReloadEntries;
} else {
customActionsEntries = QueueManager.addToQueueEntries;
}
} else {
if (canReload) {
customActionsEntries = QueueManager.removeFromQueueWithReloadEntries;
} else {
customActionsEntries = QueueManager.removeFromQueueEntries;
}
}
QueueManager[] customActionsEntries = playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null
? QueueManager.addToQueueEntries
: QueueManager.removeFromQueueEntries;
buildBottomSheetDialog(customActionsEntries);
}
@ -197,14 +211,13 @@ public class PlaylistPatch extends AuthUtils {
ExtendedUtils.showBottomSheetDialog(mContext, mScrollView, actionsMap);
}
private static void fetchQueue(boolean remove, boolean openPlaylist,
boolean openVideo, boolean reload) {
private static void fetchQueue(boolean remove, boolean openPlaylist, boolean openVideo) {
try {
String currentPlaylistId = playlistId;
String currentVideoId = videoId;
synchronized (lastVideoIds) {
if (currentPlaylistId.isEmpty()) { // Queue is empty, create new playlist.
CreatePlaylistRequest.fetchRequestIfNeeded(currentVideoId, requestHeader, dataSyncId);
CreatePlaylistRequest.fetchRequestIfNeeded(currentVideoId, requestHeader);
runOnMainThreadDelayed(() -> {
CreatePlaylistRequest request = CreatePlaylistRequest.getRequestForVideoId(currentVideoId);
if (request != null) {
@ -218,7 +231,7 @@ public class PlaylistPatch extends AuthUtils {
showToast(fetchSucceededCreate);
Logger.printDebug(() -> "Queue successfully created, playlistId: " + createdPlaylistId + ", setVideoId: " + setVideoId);
if (openPlaylist) {
openQueue(currentVideoId, openVideo, reload);
openQueue(currentVideoId, openVideo);
}
return;
}
@ -228,7 +241,7 @@ public class PlaylistPatch extends AuthUtils {
}, 1000);
} else { // Queue is not empty, add or remove video.
String setVideoId = lastVideoIds.get(currentVideoId);
EditPlaylistRequest.fetchRequestIfNeeded(currentVideoId, currentPlaylistId, setVideoId, requestHeader, dataSyncId);
EditPlaylistRequest.fetchRequestIfNeeded(currentVideoId, currentPlaylistId, setVideoId, requestHeader);
runOnMainThreadDelayed(() -> {
EditPlaylistRequest request = EditPlaylistRequest.getRequestForVideoId(currentVideoId);
@ -236,24 +249,22 @@ public class PlaylistPatch extends AuthUtils {
String fetchedSetVideoId = request.getResult();
Logger.printDebug(() -> "fetchedSetVideoId: " + fetchedSetVideoId);
if (remove) { // Remove from queue.
if ("".equals(fetchedSetVideoId)) {
if (StringUtils.isEmpty(fetchedSetVideoId)) {
lastVideoIds.remove(currentVideoId, setVideoId);
EditPlaylistRequest.clearVideoId(currentVideoId);
showToast(fetchSucceededRemove);
if (openPlaylist) {
openQueue(currentVideoId, openVideo, reload);
openQueue(currentVideoId, openVideo);
}
return;
}
showToast(fetchFailedRemove);
} else { // Add to queue.
if (fetchedSetVideoId != null && !fetchedSetVideoId.isEmpty()) {
if (StringUtils.isNotEmpty(fetchedSetVideoId)) {
lastVideoIds.putIfAbsent(currentVideoId, fetchedSetVideoId);
EditPlaylistRequest.clearVideoId(currentVideoId);
showToast(fetchSucceededAdd);
Logger.printDebug(() -> "Video successfully added, setVideoId: " + fetchedSetVideoId);
if (openPlaylist) {
openQueue(currentVideoId, openVideo, reload);
openQueue(currentVideoId, openVideo);
}
return;
}
@ -275,7 +286,7 @@ public class PlaylistPatch extends AuthUtils {
return;
}
try {
GetPlaylistsRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader, dataSyncId);
GetPlaylistsRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader);
runOnMainThreadDelayed(() -> {
GetPlaylistsRequest request = GetPlaylistsRequest.getRequestForPlaylistId(currentPlaylistId);
if (request != null) {
@ -317,7 +328,7 @@ public class PlaylistPatch extends AuthUtils {
handleCheckError(checkFailedPlaylistId);
return;
}
SavePlaylistRequest.fetchRequestIfNeeded(playlistId, libraryId, requestHeader, dataSyncId);
SavePlaylistRequest.fetchRequestIfNeeded(playlistId, libraryId, requestHeader);
runOnMainThreadDelayed(() -> {
SavePlaylistRequest request = SavePlaylistRequest.getRequestForLibraryId(libraryId);
@ -343,7 +354,7 @@ public class PlaylistPatch extends AuthUtils {
return;
}
try {
DeletePlaylistRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader, dataSyncId);
DeletePlaylistRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader);
runOnMainThreadDelayed(() -> {
DeletePlaylistRequest request = DeletePlaylistRequest.getRequestForPlaylistId(currentPlaylistId);
if (request != null) {
@ -375,10 +386,10 @@ public class PlaylistPatch extends AuthUtils {
}
private static void openQueue() {
openQueue("", false, false);
openQueue("", false);
}
private static void openQueue(String currentVideoId, boolean openVideo, boolean reload) {
private static void openQueue(String currentVideoId, boolean openVideo) {
String currentPlaylistId = playlistId;
if (currentPlaylistId.isEmpty()) {
handleCheckError(checkFailedQueue);
@ -390,15 +401,7 @@ public class PlaylistPatch extends AuthUtils {
return;
}
// Open a video from a playlist
if (reload) {
// Since the Queue is not automatically synced, a 'reload' action has been added as a workaround.
// The 'reload' action simply closes the video and reopens it.
// It is important to close the video, otherwise the Queue will not be updated.
dismissPlayer();
openPlaylist(currentPlaylistId, VideoInformation.getVideoId(), true);
} else {
openPlaylist(currentPlaylistId, currentVideoId);
}
openPlaylist(currentPlaylistId, currentVideoId);
} else {
// Open a playlist
openPlaylist(currentPlaylistId);
@ -417,37 +420,27 @@ public class PlaylistPatch extends AuthUtils {
ADD_TO_QUEUE(
"revanced_queue_manager_add_to_queue",
"yt_outline_list_add_black_24",
() -> fetchQueue(false, false, false, false)
() -> fetchQueue(false, false, false)
),
ADD_TO_QUEUE_AND_OPEN_QUEUE(
"revanced_queue_manager_add_to_queue_and_open_queue",
"yt_outline_list_add_black_24",
() -> fetchQueue(false, true, false, false)
() -> fetchQueue(false, true, false)
),
ADD_TO_QUEUE_AND_PLAY_VIDEO(
"revanced_queue_manager_add_to_queue_and_play_video",
"yt_outline_list_play_arrow_black_24",
() -> fetchQueue(false, true, true, false)
),
ADD_TO_QUEUE_AND_RELOAD_VIDEO(
"revanced_queue_manager_add_to_queue_and_reload_video",
"yt_outline_arrow_circle_black_24",
() -> fetchQueue(false, true, true, true)
() -> fetchQueue(false, true, true)
),
REMOVE_FROM_QUEUE(
"revanced_queue_manager_remove_from_queue",
"yt_outline_trash_can_black_24",
() -> fetchQueue(true, false, false, false)
() -> fetchQueue(true, false, false)
),
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE(
"revanced_queue_manager_remove_from_queue_and_open_queue",
"yt_outline_trash_can_black_24",
() -> fetchQueue(true, true, false, false)
),
REMOVE_FROM_QUEUE_AND_RELOAD_VIDEO(
"revanced_queue_manager_remove_from_queue_and_reload_video",
"yt_outline_arrow_circle_black_24",
() -> fetchQueue(true, true, true, true)
() -> fetchQueue(true, true, false)
),
OPEN_QUEUE(
"revanced_queue_manager_open_queue",
@ -495,17 +488,6 @@ public class PlaylistPatch extends AuthUtils {
SAVE_QUEUE,
};
public static final QueueManager[] addToQueueWithReloadEntries = {
ADD_TO_QUEUE,
ADD_TO_QUEUE_AND_OPEN_QUEUE,
ADD_TO_QUEUE_AND_PLAY_VIDEO,
ADD_TO_QUEUE_AND_RELOAD_VIDEO,
OPEN_QUEUE,
//REMOVE_QUEUE,
EXTERNAL_DOWNLOADER,
SAVE_QUEUE,
};
public static final QueueManager[] removeFromQueueEntries = {
REMOVE_FROM_QUEUE,
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE,
@ -515,16 +497,6 @@ public class PlaylistPatch extends AuthUtils {
SAVE_QUEUE,
};
public static final QueueManager[] removeFromQueueWithReloadEntries = {
REMOVE_FROM_QUEUE,
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE,
REMOVE_FROM_QUEUE_AND_RELOAD_VIDEO,
OPEN_QUEUE,
//REMOVE_QUEUE,
EXTERNAL_DOWNLOADER,
SAVE_QUEUE,
};
public static final QueueManager[] noVideoIdQueueEntries = {
OPEN_QUEUE,
//REMOVE_QUEUE,

View File

@ -24,14 +24,9 @@ import java.util.concurrent.TimeoutException
class CreatePlaylistRequest private constructor(
private val videoId: String,
private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) {
private val future: Future<Pair<String, String>> = Utils.submitOnBackgroundThread {
fetch(
videoId,
requestHeader,
dataSyncId,
)
fetch(videoId, requestHeader)
}
val playlistId: Pair<String, String>?
@ -80,19 +75,11 @@ class CreatePlaylistRequest private constructor(
}
@JvmStatic
fun fetchRequestIfNeeded(
videoId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
) {
fun fetchRequestIfNeeded(videoId: String, requestHeader: Map<String, String>) {
Objects.requireNonNull(videoId)
synchronized(cache) {
if (!cache.containsKey(videoId)) {
cache[videoId] = CreatePlaylistRequest(
videoId,
requestHeader,
dataSyncId,
)
cache[videoId] = CreatePlaylistRequest(videoId, requestHeader)
}
}
}
@ -110,8 +97,7 @@ class CreatePlaylistRequest private constructor(
private fun sendCreatePlaylistRequest(
videoId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): JSONObject? {
Objects.requireNonNull(videoId)
@ -126,7 +112,6 @@ class CreatePlaylistRequest private constructor(
CREATE_PLAYLIST,
clientType,
requestHeader,
dataSyncId,
)
val requestBody = createPlaylistRequestBody(videoId = videoId)
@ -158,8 +143,7 @@ class CreatePlaylistRequest private constructor(
private fun sendSetVideoIdRequest(
videoId: String,
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): JSONObject? {
Objects.requireNonNull(playlistId)
@ -173,8 +157,7 @@ class CreatePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute(
GET_SET_VIDEO_ID,
clientType,
requestHeader,
dataSyncId,
requestHeader
)
val requestBody = createApplicationRequestBody(
@ -249,23 +232,13 @@ class CreatePlaylistRequest private constructor(
private fun fetch(
videoId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): Pair<String, String>? {
val createPlaylistJson = sendCreatePlaylistRequest(
videoId,
requestHeader,
dataSyncId
)
val createPlaylistJson = sendCreatePlaylistRequest(videoId, requestHeader)
if (createPlaylistJson != null) {
val playlistId = parseCreatePlaylistResponse(createPlaylistJson)
if (playlistId != null) {
val setVideoIdJson = sendSetVideoIdRequest(
videoId,
playlistId,
requestHeader,
dataSyncId
)
val setVideoIdJson = sendSetVideoIdRequest(videoId, playlistId, requestHeader)
if (setVideoIdJson != null) {
val setVideoId = parseSetVideoIdResponse(setVideoIdJson)
if (setVideoId != null) {

View File

@ -22,13 +22,11 @@ import java.util.concurrent.TimeoutException
class DeletePlaylistRequest private constructor(
private val playlistId: String,
private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) {
private val future: Future<Boolean> = Utils.submitOnBackgroundThread {
fetch(
playlistId,
requestHeader,
dataSyncId,
)
}
@ -80,16 +78,14 @@ class DeletePlaylistRequest private constructor(
@JvmStatic
fun fetchRequestIfNeeded(
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
) {
Objects.requireNonNull(playlistId)
synchronized(cache) {
if (!cache.containsKey(playlistId)) {
cache[playlistId] = DeletePlaylistRequest(
playlistId,
requestHeader,
dataSyncId,
requestHeader
)
}
}
@ -108,8 +104,7 @@ class DeletePlaylistRequest private constructor(
private fun sendRequest(
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): JSONObject? {
Objects.requireNonNull(playlistId)
@ -123,8 +118,7 @@ class DeletePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute(
DELETE_PLAYLIST,
clientType,
requestHeader,
dataSyncId
requestHeader
)
val requestBody = deletePlaylistRequestBody(playlistId)
@ -169,14 +163,9 @@ class DeletePlaylistRequest private constructor(
private fun fetch(
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): Boolean? {
val json = sendRequest(
playlistId,
requestHeader,
dataSyncId,
)
val json = sendRequest(playlistId, requestHeader)
if (json != null) {
return parseResponse(json)
}

View File

@ -8,6 +8,7 @@ import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.EDIT_PLA
import app.revanced.extension.shared.requests.Requester
import app.revanced.extension.shared.utils.Logger
import app.revanced.extension.shared.utils.Utils
import org.apache.commons.lang3.StringUtils
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
@ -24,7 +25,6 @@ class EditPlaylistRequest private constructor(
private val playlistId: String,
private val setVideoId: String?,
private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) {
private val future: Future<String> = Utils.submitOnBackgroundThread {
fetch(
@ -32,7 +32,6 @@ class EditPlaylistRequest private constructor(
playlistId,
setVideoId,
requestHeader,
dataSyncId,
)
}
@ -93,8 +92,7 @@ class EditPlaylistRequest private constructor(
videoId: String,
playlistId: String,
setVideoId: String?,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
) {
Objects.requireNonNull(videoId)
synchronized(cache) {
@ -103,8 +101,7 @@ class EditPlaylistRequest private constructor(
videoId,
playlistId,
setVideoId,
requestHeader,
dataSyncId,
requestHeader
)
}
}
@ -125,8 +122,7 @@ class EditPlaylistRequest private constructor(
videoId: String,
playlistId: String,
setVideoId: String?,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): JSONObject? {
Objects.requireNonNull(videoId)
@ -140,8 +136,7 @@ class EditPlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute(
EDIT_PLAYLIST,
clientType,
requestHeader,
dataSyncId
requestHeader
)
val requestBody = editPlaylistRequestBody(
@ -204,18 +199,11 @@ class EditPlaylistRequest private constructor(
videoId: String,
playlistId: String,
setVideoId: String?,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): String? {
val json = sendRequest(
videoId,
playlistId,
setVideoId,
requestHeader,
dataSyncId,
)
val json = sendRequest(videoId, playlistId, setVideoId, requestHeader)
if (json != null) {
return parseResponse(json, setVideoId != null && setVideoId.isNotEmpty())
return parseResponse(json, StringUtils.isNotEmpty(setVideoId))
}
return null

View File

@ -22,13 +22,11 @@ import java.util.concurrent.TimeoutException
class GetPlaylistsRequest private constructor(
private val playlistId: String,
private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) {
private val future: Future<Array<Pair<String, String>>> = Utils.submitOnBackgroundThread {
fetch(
playlistId,
requestHeader,
dataSyncId,
)
}
@ -80,16 +78,14 @@ class GetPlaylistsRequest private constructor(
@JvmStatic
fun fetchRequestIfNeeded(
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
) {
Objects.requireNonNull(playlistId)
synchronized(cache) {
if (!cache.containsKey(playlistId)) {
cache[playlistId] = GetPlaylistsRequest(
playlistId,
requestHeader,
dataSyncId,
requestHeader
)
}
}
@ -108,8 +104,7 @@ class GetPlaylistsRequest private constructor(
private fun sendRequest(
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): JSONObject? {
Objects.requireNonNull(playlistId)
@ -123,8 +118,7 @@ class GetPlaylistsRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute(
GET_PLAYLISTS,
clientType,
requestHeader,
dataSyncId
requestHeader
)
val requestBody = getPlaylistsRequestBody(playlistId)
@ -208,10 +202,9 @@ class GetPlaylistsRequest private constructor(
private fun fetch(
playlistId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): Array<Pair<String, String>>? {
val json = sendRequest(playlistId, requestHeader, dataSyncId)
val json = sendRequest(playlistId, requestHeader)
if (json != null) {
return parseResponse(json)
}

View File

@ -23,14 +23,12 @@ class SavePlaylistRequest private constructor(
private val playlistId: String,
private val libraryId: String,
private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) {
private val future: Future<Boolean> = Utils.submitOnBackgroundThread {
fetch(
playlistId,
libraryId,
requestHeader,
dataSyncId,
)
}
@ -83,16 +81,14 @@ class SavePlaylistRequest private constructor(
fun fetchRequestIfNeeded(
playlistId: String,
libraryId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
) {
Objects.requireNonNull(playlistId)
synchronized(cache) {
cache[libraryId] = SavePlaylistRequest(
playlistId,
libraryId,
requestHeader,
dataSyncId,
requestHeader
)
}
}
@ -111,8 +107,7 @@ class SavePlaylistRequest private constructor(
private fun sendRequest(
playlistId: String,
libraryId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): JSONObject? {
Objects.requireNonNull(playlistId)
Objects.requireNonNull(libraryId)
@ -127,8 +122,7 @@ class SavePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute(
EDIT_PLAYLIST,
clientType,
requestHeader,
dataSyncId
requestHeader
)
val requestBody = savePlaylistRequestBody(libraryId, playlistId)
@ -174,15 +168,9 @@ class SavePlaylistRequest private constructor(
private fun fetch(
playlistId: String,
libraryId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
requestHeader: Map<String, String>
): Boolean? {
val json = sendRequest(
playlistId,
libraryId,
requestHeader,
dataSyncId,
)
val json = sendRequest(playlistId, libraryId, requestHeader)
if (json != null) {
return parseResponse(json)
}

View File

@ -3,14 +3,10 @@ package app.revanced.extension.youtube.patches.video;
import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.youtube.shared.RootView.isShortsActive;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import org.apache.commons.lang3.BooleanUtils;
import java.util.LinkedHashMap;
import java.util.Map;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.utils.Logger;
@ -32,61 +28,48 @@ public class PlaybackSpeedPatch {
Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get();
private static final long TOAST_DELAY_MILLISECONDS = 750;
private static long lastTimeSpeedChanged;
/**
* The last used playback speed.
* This value is used when the default playback speed is 'Auto'.
*/
private static float lastSelectedPlaybackSpeed = 1.0f;
private static float lastSelectedShortsPlaybackSpeed = 1.0f;
/**
* The last regular video id.
*/
private static String videoId = "";
private static volatile String channelId = "";
private static volatile String videoId = "";
private static boolean isLiveStream;
@GuardedBy("itself")
private static final Map<String, Float> ignoredPlaybackSpeedVideoIds = new LinkedHashMap<>() {
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 3;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
}
};
private static volatile String channelIdShorts = "";
private static volatile String videoIdShorts = "";
private static boolean isLiveStreamShorts;
/**
* Injection point.
* This method is used to reset the playback speed to 1.0 when a general video is started, whether it is a live stream, music, or whitelist.
*/
public static void newVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
@NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle,
final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) {
if (isShortsActive()) {
return;
channelIdShorts = newlyLoadedChannelId;
videoIdShorts = newlyLoadedVideoId;
isLiveStreamShorts = newlyLoadedLiveStreamValue;
Logger.printDebug(() -> "newVideoStarted: " + newlyLoadedVideoId);
} else {
channelId = newlyLoadedChannelId;
videoId = newlyLoadedVideoId;
isLiveStream = newlyLoadedLiveStreamValue;
Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId);
}
if (videoId.equals(newlyLoadedVideoId)) {
return;
}
videoId = newlyLoadedVideoId;
}
boolean isMusic = isMusic(newlyLoadedVideoId);
boolean isWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(newlyLoadedVideoId);
/**
* Injection point.
*/
public static void newShortsVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
@NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle,
final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) {
channelIdShorts = newlyLoadedChannelId;
videoIdShorts = newlyLoadedVideoId;
isLiveStreamShorts = newlyLoadedLiveStreamValue;
if (newlyLoadedLiveStreamValue || isMusic || isWhitelisted) {
synchronized(ignoredPlaybackSpeedVideoIds) {
if (!ignoredPlaybackSpeedVideoIds.containsKey(newlyLoadedVideoId)) {
lastSelectedPlaybackSpeed = 1.0f;
ignoredPlaybackSpeedVideoIds.put(newlyLoadedVideoId, lastSelectedPlaybackSpeed);
VideoInformation.setPlaybackSpeed(lastSelectedPlaybackSpeed);
VideoInformation.overridePlaybackSpeed(lastSelectedPlaybackSpeed);
Logger.printDebug(() -> "changing playback speed to: 1.0, isLiveStream: " + newlyLoadedLiveStreamValue +
", isMusic: " + isMusic + ", isWhitelisted: " + isWhitelisted);
}
}
}
Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId);
}
/**
@ -115,29 +98,32 @@ public class PlaybackSpeedPatch {
/**
* Injection point.
* This method is called every second for regular videos and Shorts.
*/
public static float getPlaybackSpeed(float playbackSpeed) {
boolean isShorts = isShortsActive();
String currentChannelId = isShorts ? channelIdShorts : channelId;
String currentVideoId = isShorts ? videoIdShorts : videoId;
boolean currentVideoIsLiveStream = isShorts ? isLiveStreamShorts : isLiveStream;
boolean currentVideoIsWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(currentChannelId);
boolean currentVideoIsMusic = !isShorts && isMusic();
if (currentVideoIsLiveStream || currentVideoIsWhitelisted || currentVideoIsMusic) {
Logger.printDebug(() -> "changing playback speed to: 1.0");
VideoInformation.setPlaybackSpeed(1.0f);
return 1.0f;
}
float defaultPlaybackSpeed = isShorts ? DEFAULT_PLAYBACK_SPEED_SHORTS.get() : DEFAULT_PLAYBACK_SPEED.get();
if (defaultPlaybackSpeed < 0) { // If the default playback speed is 'Auto', it will be overridden to the last used playback speed.
float finalPlaybackSpeed = isShorts ? lastSelectedShortsPlaybackSpeed : lastSelectedPlaybackSpeed;
if (defaultPlaybackSpeed < 0) {
float finalPlaybackSpeed = isShorts ? playbackSpeed : lastSelectedPlaybackSpeed;
VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed);
Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed);
return finalPlaybackSpeed;
} else { // Otherwise the default playback speed is used.
synchronized (ignoredPlaybackSpeedVideoIds) {
if (isShorts) {
// For Shorts, the VideoInformation.overridePlaybackSpeed() method is not used, so manually save the playback speed in VideoInformation.
VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed);
} else if (ignoredPlaybackSpeedVideoIds.containsKey(videoId)) {
// For general videos, check whether the default video playback speed should not be applied.
Logger.printDebug(() -> "changing playback speed to: 1.0");
return 1.0f;
}
} else {
if (isShorts) {
VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed);
}
Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed);
return defaultPlaybackSpeed;
}
@ -152,19 +138,6 @@ public class PlaybackSpeedPatch {
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
try {
boolean isShorts = isShortsActive();
// Saves the user-selected playback speed in the method.
if (isShorts) {
lastSelectedShortsPlaybackSpeed = playbackSpeed;
} else {
lastSelectedPlaybackSpeed = playbackSpeed;
// If the user has manually changed the playback speed, the whitelist has already been applied.
// If there is a videoId on the map, it will be removed.
synchronized (ignoredPlaybackSpeedVideoIds) {
ignoredPlaybackSpeedVideoIds.remove(videoId);
}
}
if (PatchStatus.RememberPlaybackSpeed()) {
BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts
? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED
@ -205,23 +178,15 @@ public class PlaybackSpeedPatch {
}
}, TOAST_DELAY_MILLISECONDS);
}
} else if (!isShorts){
lastSelectedPlaybackSpeed = playbackSpeed;
}
} catch (Exception ex) {
Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex);
}
}
/**
* Injection point.
*/
public static void onDismiss() {
synchronized (ignoredPlaybackSpeedVideoIds) {
ignoredPlaybackSpeedVideoIds.remove(videoId);
videoId = "";
}
}
private static boolean isMusic(String videoId) {
private static boolean isMusic() {
if (DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC && !videoId.isEmpty()) {
try {
MusicRequest request = MusicRequest.getRequestForVideoId(videoId);

View File

@ -115,7 +115,7 @@ public class ReturnYouTubeDislike {
private static final Rect middleSeparatorBounds;
/**
* Horizontal padding between the left and middle separator.
* Left separator horizontal padding for Rolling Number layout.
*/
public static final int leftSeparatorShapePaddingPixels;
private static final ShapeDrawable leftSeparatorShape;
@ -131,7 +131,7 @@ public class ReturnYouTubeDislike {
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp);
middleSeparatorBounds = new Rect(0, 0, middleSeparatorSize, middleSeparatorSize);
leftSeparatorShapePaddingPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8.4f, dp);
leftSeparatorShapePaddingPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10.0f, dp);
leftSeparatorShape = new ShapeDrawable(new RectShape());
leftSeparatorShape.setBounds(leftSeparatorBounds);

View File

@ -637,31 +637,31 @@ public class Settings extends BaseSettings {
public static final FloatSetting SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00");
public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900");
public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
// SB Setting not exported
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);

View File

@ -377,7 +377,6 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
@Override
public void onDestroy() {
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
Utils.resetLocalizedContext();
super.onDestroy();
}

View File

@ -6,8 +6,6 @@ import static app.revanced.extension.shared.utils.Utils.isSDKAbove;
import android.preference.Preference;
import android.preference.SwitchPreference;
import java.util.Date;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch;
import app.revanced.extension.youtube.patches.utils.PatchStatus;
@ -46,7 +44,6 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment {
AmbientModePreferenceLinks();
FullScreenPanelPreferenceLinks();
NavigationPreferenceLinks();
PatchInformationPreferenceLinks();
RYDPreferenceLinks();
SeekBarPreferenceLinks();
ShortsPreferenceLinks();
@ -146,26 +143,6 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment {
);
}
/**
* Set patch information preference summary
*/
private static void PatchInformationPreferenceLinks() {
Preference appNamePreference = mPreferenceManager.findPreference("revanced_app_name");
if (appNamePreference != null) {
appNamePreference.setSummary(ExtendedUtils.getAppLabel());
}
Preference appVersionPreference = mPreferenceManager.findPreference("revanced_app_version");
if (appVersionPreference != null) {
appVersionPreference.setSummary(ExtendedUtils.getAppVersionName());
}
Preference patchedDatePreference = mPreferenceManager.findPreference("revanced_patched_date");
if (patchedDatePreference != null) {
long patchedTime = PatchStatus.PatchedTime();
Date date = new Date(patchedTime);
patchedDatePreference.setSummary(date.toLocaleString());
}
}
/**
* Enable/Disable Preference related to RYD settings
*/

View File

@ -37,6 +37,7 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockViewController
/**
* Not thread safe. All fields/methods must be accessed from the main thread.
*
*/
public class SponsorBlockUtils {
private static final int LOCKED_COLOR = Color.parseColor("#FFC83D");

View File

@ -1,36 +1,7 @@
package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.shared.utils.StringRef.sf;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED_COLOR;
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED_OPACITY;
import static app.revanced.extension.youtube.settings.Settings.*;
import android.graphics.Color;
import android.graphics.Paint;
@ -85,6 +56,7 @@ public enum SegmentCategory {
SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR, SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY),
UNSUBMITTED("unsubmitted", StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"),
SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR, SB_CATEGORY_UNSUBMITTED_OPACITY);
;
private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");

View File

@ -145,10 +145,8 @@ class SwipeControlsOverlayLayout(
addView(feedbackTextView)
// get icons scaled, assuming square icons
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
autoBrightnessIcon =
getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
manualBrightnessIcon =
getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight)
}
@ -188,18 +186,11 @@ class SwipeControlsOverlayLayout(
/**
* Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
*/
private fun showFeedbackView(
value: String,
progress: Int,
max: Int,
icon: Drawable,
isBrightness: Boolean
) {
private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
val viewToShow =
if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
viewToShow.apply {
setProgress(progress, max, value, isBrightness)
this.icon = icon
@ -250,13 +241,7 @@ class SwipeControlsOverlayLayout(
brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon
}
showFeedbackView(
"$brightnessValue%",
brightnessValue,
100,
icon,
isBrightness = true
)
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
} else {
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
}
@ -289,12 +274,7 @@ abstract class AbstractProgressView(
) : View(context, attrs, defStyleAttr) {
// Combined paint creation function for both fill and stroke styles
private fun createPaint(
color: Int,
style: Paint.Style = Paint.Style.FILL,
strokeCap: Paint.Cap = Paint.Cap.BUTT,
strokeWidth: Float = 0f
) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
private fun createPaint(color: Int, style: Paint.Style = Paint.Style.FILL, strokeCap: Paint.Cap = Paint.Cap.BUTT, strokeWidth: Float = 0f) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
this.style = style
this.color = color
this.strokeCap = strokeCap
@ -302,18 +282,13 @@ abstract class AbstractProgressView(
}
// Initialize paints
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
val progressPaint = createPaint(
overlayProgressColor,
style = Paint.Style.STROKE,
strokeCap = Paint.Cap.ROUND,
strokeWidth = 20f
)
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
textAlign = Paint.Align.CENTER
textSize = 40f // Can adjust based on need
textSize = 40f // Can adjust based on need
}
protected var progress = 0
@ -362,11 +337,11 @@ class CircularProgressView(
init {
textPaint.textSize = 40f // Override default text size for circular view
progressPaint.strokeWidth = 20f
progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND
progressPaint.strokeCap = Paint.Cap.ROUND
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.STROKE
progressPaint.style = Paint.Style.STROKE
fillBackgroundPaint.style = Paint.Style.STROKE
}
@ -377,12 +352,7 @@ class CircularProgressView(
rectF.set(20f, 20f, size - 20f, size - 20f)
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
canvas.drawCircle(
width / 2f,
height / 2f,
size / 3,
backgroundPaint
) // Draw the inner circle.
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
// Select the paint for drawing based on whether it's brightness or volume.
val sweepAngle = (progress.toFloat() / maxProgress) * 360
@ -429,13 +399,13 @@ class HorizontalProgressView(
) {
private val iconSize = 60f
private val padding = 40f
private val padding = 40f
init {
textPaint.textSize = 36f // Override default text size for horizontal view
textPaint.textSize = 36f // Override default text size for horizontal view
progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL
}
@ -458,15 +428,7 @@ class HorizontalProgressView(
if (!overlayShowOverlayMinimalStyle) {
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
} else {
canvas.drawRoundRect(
minimalStartX,
0f,
minimalStartX + minimalElementWidth,
height,
cornerRadius,
cornerRadius,
backgroundPaint
)
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
}
if (!overlayShowOverlayMinimalStyle) {
@ -504,12 +466,7 @@ class HorizontalProgressView(
padding + minimalStartX
}
val iconY = height / 2 - iconSize / 2
it.setBounds(
iconX.toInt(),
iconY.toInt(),
(iconX + iconSize).toInt(),
(iconY + iconSize).toInt()
)
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
it.draw(canvas)
}

View File

@ -1,40 +0,0 @@
package app.revanced.extension.youtube.utils;
import java.util.Map;
import app.revanced.extension.shared.utils.Logger;
@SuppressWarnings("unused")
public class AuthUtils {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String[] REQUEST_HEADER_KEYS = {
AUTHORIZATION_HEADER,
"X-GOOG-API-FORMAT-VERSION",
"X-Goog-Visitor-Id"
};
public static volatile String authorization = "";
public static volatile String dataSyncId = "";
public static volatile boolean isIncognito = false;
public static volatile Map<String, String> requestHeader;
public static volatile String playlistId = "";
public static volatile String videoId = "";
public static void setRequestHeaders(String url, Map<String, String> requestHeaders) {
try {
// Save requestHeaders whenever an account is switched.
String auth = requestHeaders.get(AUTHORIZATION_HEADER);
if (auth == null || authorization.equals(auth)) {
return;
}
for (String key : REQUEST_HEADER_KEYS) {
if (requestHeaders.get(key) == null) {
return;
}
}
authorization = auth;
requestHeader = requestHeaders;
} catch (Exception ex) {
Logger.initializationException(AuthUtils.class, "setRequestHeaders failure", ex);
}
}
}

View File

@ -31,20 +31,15 @@ import app.revanced.extension.shared.utils.PackageUtils;
import app.revanced.extension.youtube.settings.Settings;
public class ExtendedUtils extends PackageUtils {
private static boolean isVersionOrGreater(String version) {
return getAppVersionName().compareTo(version) >= 0;
}
@SuppressWarnings("unused")
public static final boolean IS_19_17_OR_GREATER = isVersionOrGreater("19.17.00");
public static final boolean IS_19_20_OR_GREATER = isVersionOrGreater("19.20.00");
public static final boolean IS_19_21_OR_GREATER = isVersionOrGreater("19.21.00");
public static final boolean IS_19_26_OR_GREATER = isVersionOrGreater("19.26.00");
public static final boolean IS_19_28_OR_GREATER = isVersionOrGreater("19.28.00");
public static final boolean IS_19_29_OR_GREATER = isVersionOrGreater("19.29.00");
public static final boolean IS_19_34_OR_GREATER = isVersionOrGreater("19.34.00");
public static final boolean IS_20_09_OR_GREATER = isVersionOrGreater("20.09.00");
public static final boolean IS_19_17_OR_GREATER = getAppVersionName().compareTo("19.17.00") >= 0;
public static final boolean IS_19_20_OR_GREATER = getAppVersionName().compareTo("19.20.00") >= 0;
public static final boolean IS_19_21_OR_GREATER = getAppVersionName().compareTo("19.21.00") >= 0;
public static final boolean IS_19_26_OR_GREATER = getAppVersionName().compareTo("19.26.00") >= 0;
public static final boolean IS_19_28_OR_GREATER = getAppVersionName().compareTo("19.28.00") >= 0;
public static final boolean IS_19_29_OR_GREATER = getAppVersionName().compareTo("19.29.00") >= 0;
public static final boolean IS_19_34_OR_GREATER = getAppVersionName().compareTo("19.34.00") >= 0;
public static final boolean IS_20_09_OR_GREATER = getAppVersionName().compareTo("20.09.00") >= 0;
public static int validateValue(IntegerSetting settings, int min, int max, String message) {
int value = settings.get();

View File

@ -133,25 +133,13 @@ public class VideoUtils extends IntentUtils {
}
public static void openPlaylist(@NonNull String playlistId, @NonNull String videoId) {
openPlaylist(playlistId, videoId, false);
}
public static void openPlaylist(@NonNull String playlistId, @NonNull String videoId, boolean withTimestamp) {
final StringBuilder sb = new StringBuilder();
if (videoId.isEmpty()) {
sb.append(getPlaylistUrl(playlistId));
} else {
sb.append(VIDEO_URL);
sb.append(videoId);
sb.append("?list=");
sb.append(getVideoScheme(videoId, false));
sb.append("&list=");
sb.append(playlistId);
if (withTimestamp) {
final long currentVideoTimeInSeconds = VideoInformation.getVideoTimeInSeconds();
if (currentVideoTimeInSeconds > 0) {
sb.append("&t=");
sb.append(currentVideoTimeInSeconds);
}
}
}
launchView(sb.toString(), getContext().getPackageName());
}
@ -281,13 +269,6 @@ public class VideoUtils extends IntentUtils {
return !isExternalDownloaderLaunched.get() && original;
}
/**
* Rest of the implementation added by patch.
*/
public static void dismissPlayer() {
Logger.printDebug(() -> "Dismiss player");
}
/**
* Rest of the implementation added by patch.
*/

View File

@ -1,9 +1,7 @@
package com.google.android.apps.youtube.app.settings.videoquality;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
@ -27,8 +25,8 @@ import app.revanced.extension.youtube.utils.ThemeUtils;
@SuppressWarnings("deprecation")
public class VideoQualitySettingsActivity extends Activity {
private static String rvxSettingsLabel;
private static String searchLabel;
private static final String rvxSettingsLabel = ResourceUtils.getString("revanced_extended_settings_title");
private static final String searchLabel = ResourceUtils.getString("revanced_extended_settings_search_title");
private static WeakReference<SearchView> searchViewRef = new WeakReference<>(null);
private static WeakReference<ImageView> closeButtonRef = new WeakReference<>(null);
private ReVancedPreferenceFragment fragment;
@ -73,10 +71,6 @@ public class VideoQualitySettingsActivity extends Activity {
return;
}
// Set label
rvxSettingsLabel = getString("revanced_extended_settings_title");
searchLabel = getString("revanced_extended_settings_search_title");
// Set toolbar
setToolbar();
@ -91,14 +85,6 @@ public class VideoQualitySettingsActivity extends Activity {
}
}
@SuppressLint("DiscouragedApi")
private String getString(String str) {
Context baseContext = getBaseContext();
Resources resources = baseContext.getResources();
int identifier = resources.getIdentifier(str, "string", baseContext.getPackageName());
return resources.getString(identifier);
}
private void filterPreferences(String query) {
if (fragment == null) return;
fragment.filterPreferences(query);

View File

@ -4,5 +4,5 @@ org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
kotlin.jvm.target.validation.mode = IGNORE
version = 5.6.2
version = 5.6.1-dev.4

View File

@ -15,7 +15,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -34,7 +35,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -73,7 +75,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -114,7 +117,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -153,7 +157,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -174,7 +179,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -188,7 +194,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": [
@ -220,7 +226,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -263,7 +270,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -303,7 +311,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -343,7 +352,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -379,7 +389,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -485,7 +496,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": [
@ -516,7 +527,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -599,7 +611,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -627,7 +640,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -746,7 +760,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -829,7 +844,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -877,7 +893,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -918,7 +935,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -936,7 +954,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -954,7 +973,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -995,7 +1015,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1014,7 +1035,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1024,13 +1046,14 @@
"description": "Adds an option to disable the popup that appears when taking a screenshot.",
"use": true,
"dependencies": [
"Settings for Reddit"
"Settings for Reddit",
"ResourcePatch"
],
"compatiblePackages": {
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -1049,7 +1072,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1090,7 +1114,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1129,7 +1154,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1147,7 +1173,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1215,7 +1242,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1240,7 +1268,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1309,15 +1338,6 @@
"Clone": "com.rvx.android.apps.youtube.music",
"Default": "app.rvx.android.apps.youtube.music"
}
},
{
"key": "patchAllManifest",
"title": "Patch all manifest components",
"description": "Patch all permissions, intents and content provider authorities supported by GmsCore.",
"required": true,
"type": "kotlin.Boolean",
"default": true,
"values": null
}
]
},
@ -1335,7 +1355,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -1382,15 +1403,6 @@
"Clone": "com.rvx.android.apps.youtube.music",
"Default": "app.rvx.android.apps.youtube.music"
}
},
{
"key": "patchAllManifest",
"title": "Patch all manifest components",
"description": "Patch all permissions, intents and content provider authorities supported by GmsCore.",
"required": true,
"type": "kotlin.Boolean",
"default": true,
"values": null
}
]
},
@ -1405,7 +1417,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -1423,7 +1435,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1441,7 +1454,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1510,7 +1524,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1554,7 +1569,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -1577,7 +1592,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1598,7 +1614,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1623,7 +1640,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1642,7 +1660,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1690,7 +1709,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1706,7 +1726,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -1750,7 +1770,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1772,7 +1793,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1807,7 +1829,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -1826,7 +1848,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -1881,7 +1904,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1902,7 +1926,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1921,7 +1946,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1941,7 +1967,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -1988,7 +2015,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2005,7 +2033,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -2022,7 +2050,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -2041,7 +2069,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2065,7 +2094,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -2164,7 +2194,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2178,7 +2209,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -2219,7 +2250,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2229,14 +2261,13 @@
"description": "Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically.",
"use": true,
"dependencies": [
"Settings for Reddit",
"ResourcePatch"
"Settings for Reddit"
],
"compatiblePackages": {
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -2277,7 +2308,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2342,7 +2374,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2384,7 +2417,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2422,7 +2456,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": []
@ -2441,7 +2475,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2465,7 +2500,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2482,7 +2518,7 @@
"com.reddit.frontpage": [
"2024.17.0",
"2025.05.1",
"2025.12.1"
"2025.12.0"
]
},
"options": [
@ -2517,7 +2553,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -2527,7 +2564,7 @@
"description": "The settings menu name that the RVX settings menu should be above.",
"required": true,
"type": "kotlin.String",
"default": "@string/parent_tools_key",
"default": "@string/about_key",
"values": {
"Parent settings": "@string/parent_tools_key",
"General": "@string/general_key",
@ -2628,7 +2665,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2647,7 +2685,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -2759,7 +2798,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -2820,7 +2860,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2865,7 +2906,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -2898,7 +2940,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2917,7 +2960,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -2977,7 +3021,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -2995,7 +3040,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -3114,7 +3160,6 @@
"BytecodePatch",
"BytecodePatch",
"BytecodePatch",
"BytecodePatch",
"ResourcePatch"
],
"compatiblePackages": {
@ -3123,7 +3168,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []
@ -3141,7 +3187,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": [
@ -3245,7 +3292,8 @@
"19.16.39",
"19.43.41",
"19.44.39",
"19.47.53"
"19.47.53",
"20.03.43"
]
},
"options": []

View File

@ -459,7 +459,7 @@ public final class app/revanced/patches/reddit/utils/extension/SharedExtensionPa
}
public final class app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatchKt {
public static final fun getNsfwDialogTitle ()J
public static final fun getScreenShotShareBanner ()J
}
public final class app/revanced/patches/reddit/utils/settings/SettingsPatchKt {
@ -875,7 +875,6 @@ public final class app/revanced/patches/youtube/player/seekbar/SeekbarComponents
public final class app/revanced/patches/youtube/shorts/components/FingerprintsKt {
public static final fun indexOfAddLiveHeaderElementsContainerInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I
public static final fun indexOfDismissInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I
}
public final class app/revanced/patches/youtube/shorts/components/ShortsComponentPatchKt {
@ -895,10 +894,6 @@ public final class app/revanced/patches/youtube/utils/FingerprintsKt {
public static final fun indexOfSpannedCharSequenceInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I
}
public final class app/revanced/patches/youtube/utils/auth/AuthHookPatchKt {
public static final fun getAuthHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/utils/bottomsheet/BottomSheetHookPatchKt {
public static final fun getBottomSheetHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@ -911,10 +906,6 @@ public final class app/revanced/patches/youtube/utils/controlsoverlay/ControlsOv
public static final fun getControlsOverlayConfigPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatchKt {
public static final fun getDismissPlayerHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatchKt {
public static final fun getEngagementPanelHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@ -1047,7 +1038,6 @@ public final class app/revanced/patches/youtube/utils/playservice/VersionCheckPa
public static final fun is_19_04_or_greater ()Z
public static final fun is_19_05_or_greater ()Z
public static final fun is_19_09_or_greater ()Z
public static final fun is_19_11_or_greater ()Z
public static final fun is_19_15_or_greater ()Z
public static final fun is_19_16_or_greater ()Z
public static final fun is_19_17_or_greater ()Z
@ -1062,12 +1052,10 @@ public final class app/revanced/patches/youtube/utils/playservice/VersionCheckPa
public static final fun is_19_34_or_greater ()Z
public static final fun is_19_36_or_greater ()Z
public static final fun is_19_41_or_greater ()Z
public static final fun is_19_42_or_greater ()Z
public static final fun is_19_43_or_greater ()Z
public static final fun is_19_44_or_greater ()Z
public static final fun is_19_46_or_greater ()Z
public static final fun is_19_49_or_greater ()Z
public static final fun is_19_50_or_greater ()Z
public static final fun is_20_02_or_greater ()Z
public static final fun is_20_03_or_greater ()Z
public static final fun is_20_05_or_greater ()Z

View File

@ -152,10 +152,7 @@ private enum class MethodCall(
RegisterNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"registerNetworkCallback",
arrayOf(
"Landroid/net/NetworkRequest;",
"Landroid/net/ConnectivityManager\$NetworkCallback;"
),
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RegisterNetworkCallback2(
@ -177,20 +174,13 @@ private enum class MethodCall(
RequestNetwork1(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf(
"Landroid/net/NetworkRequest;",
"Landroid/net/ConnectivityManager\$NetworkCallback;"
),
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RequestNetwork2(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf(
"Landroid/net/NetworkRequest;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"I"
),
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
"V",
),
RequestNetwork3(

View File

@ -19,8 +19,7 @@ val edgeToEdgeDisplayPatch = resourcePatch(
// Instead, it checks compileSdkVersion and prints a warning.
try {
val manifestElement = document.getNode("manifest") as Element
val compileSdkVersion =
Integer.parseInt(manifestElement.getAttribute("android:compileSdkVersion"))
val compileSdkVersion = Integer.parseInt(manifestElement.getAttribute("android:compileSdkVersion"))
if (compileSdkVersion < 35) {
printWarn("This app may not be forcing edge to edge display (compileSdkVersion: $compileSdkVersion)")
}

View File

@ -88,7 +88,7 @@ val accountComponentsPatch = bytecodePatch(
}
// account switcher
val textViewField = with(
val textViewField = with (
channelHandleFingerprint
.methodOrThrow(namesInactiveAccountThumbnailSizeFingerprint)
) {
@ -117,8 +117,7 @@ val accountComponentsPatch = bytecodePatch(
.forEach { index ->
val insertIndex = index - 1
if (!hook && getInstruction(insertIndex).opcode == Opcode.IF_NEZ) {
val insertRegister =
getInstruction<OneRegisterInstruction>(insertIndex).registerA
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex, """

View File

@ -115,8 +115,7 @@ val adsPatch = bytecodePatch(
.methodOrThrow(getPremiumDialogParentFingerprint)
.apply {
val setContentViewIndex = indexOfSetContentViewInstruction(this)
val dialogInstruction =
getInstruction<FiveRegisterInstruction>(setContentViewIndex)
val dialogInstruction = getInstruction<FiveRegisterInstruction>(setContentViewIndex)
val dialogRegister = dialogInstruction.registerC
val viewRegister = dialogInstruction.registerD

View File

@ -206,8 +206,7 @@ val changeHeaderPatch = resourcePatch(
printWarn(warnings)
}
val isLegacyLogoExists =
get("res").resolve("drawable-xxhdpi").resolve("ytm_logo.png").exists()
val isLegacyLogoExists = get("res").resolve("drawable-xxhdpi").resolve("ytm_logo.png").exists()
if (is_7_27_or_greater && isLegacyLogoExists) {
document("res/layout/signin_fragment.xml").use { document ->
document.doRecursively node@{ node ->

View File

@ -1,13 +1,13 @@
package app.revanced.patches.music.misc.watchhistory
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.trackingurlhook.hookWatchHistory
import app.revanced.patches.shared.trackingurlhook.trackingUrlHookPatch
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.music.utils.patch.PatchList.WATCH_HISTORY
import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.shared.trackingurlhook.hookWatchHistory
import app.revanced.patches.shared.trackingurlhook.trackingUrlHookPatch
@Suppress("unused")
val watchHistoryPatch = bytecodePatch(

View File

@ -124,17 +124,14 @@ val navigationBarComponentsPatch = bytecodePatch(
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;"
}
val browseIdReference =
getInstruction<ReferenceInstruction>(browseIdIndex).reference as FieldReference
val browseIdReference = getInstruction<ReferenceInstruction>(browseIdIndex).reference as FieldReference
val fieldName = browseIdReference.name
val componentIndex = indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.toString() == browseIdReference.toString()
}
val browseIdRegister =
getInstruction<TwoRegisterInstruction>(componentIndex).registerA
val componentRegister =
getInstruction<TwoRegisterInstruction>(componentIndex).registerB
val browseIdRegister = getInstruction<TwoRegisterInstruction>(componentIndex).registerA
val componentRegister = getInstruction<TwoRegisterInstruction>(componentIndex).registerB
val enumIndex = it.patternMatch!!.startIndex + 3
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA

View File

@ -54,7 +54,7 @@ internal val engagementPanelHeightFingerprint = legacyFingerprint(
parameters = emptyList(),
customFingerprint = { method, _ ->
AccessFlags.FINAL.isSet(method.accessFlags) &&
method.containsLiteralInstruction(1) &&
method.containsLiteralInstruction(1) &&
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "booleanValue"

View File

@ -747,8 +747,7 @@ val playerComponentsPatch = bytecodePatch(
getInstruction<FiveRegisterInstruction>(bottomSheetBehaviorIndex).registerD
val getFieldIndex = bottomSheetBehaviorIndex - 2
val getFieldReference =
getInstruction<ReferenceInstruction>(getFieldIndex).reference
val getFieldReference = getInstruction<ReferenceInstruction>(getFieldIndex).reference
val getFieldInstruction = getInstruction<TwoRegisterInstruction>(getFieldIndex)
addInstructionsWithLabels(

View File

@ -1,8 +1,14 @@
package app.revanced.patches.music.utils.extension
import app.revanced.patches.music.utils.extension.hooks.applicationInitHook
import app.revanced.patches.music.utils.extension.hooks.mainActivityBaseContextHook
import app.revanced.patches.shared.extension.hooks.cronetEngineContextHook
import app.revanced.patches.shared.extension.hooks.firebaseInitProviderContextHook
import app.revanced.patches.shared.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch(
applicationInitHook,
cronetEngineContextHook,
firebaseInitProviderContextHook,
mainActivityBaseContextHook,
)

View File

@ -0,0 +1,32 @@
package app.revanced.patches.music.utils.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var attachBaseContextIndex = -1
internal val mainActivityBaseContextHook = extensionHook(
insertIndexResolver = { method ->
attachBaseContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "attachBaseContext"
}
attachBaseContextIndex + 1
},
contextRegisterResolver = { method ->
val overrideInstruction =
method.implementation!!.instructions.elementAt(attachBaseContextIndex)
as FiveRegisterInstruction
"v${overrideInstruction.registerD}"
},
) {
returns("V")
parameters("Landroid/content/Context;")
custom { method, classDef ->
classDef.type == "Lcom/google/android/apps/youtube/music/activities/MusicActivity;" &&
method.name == "attachBaseContext"
}
}

View File

@ -22,6 +22,7 @@ import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.shared.spoof.blockrequest.blockRequestPatch
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
@ -30,7 +31,6 @@ import app.revanced.patches.shared.indexOfBrandInstruction
import app.revanced.patches.shared.indexOfManufacturerInstruction
import app.revanced.patches.shared.indexOfModelInstruction
import app.revanced.patches.shared.indexOfReleaseInstruction
import app.revanced.patches.shared.spoof.blockrequest.blockRequestPatch
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow

View File

@ -142,8 +142,7 @@ internal val sharedResourceIdPatch = resourcePatch(
endButtonsContainer = getResourceId(ID, "end_buttons_container")
floatingLayout = getResourceId(ID, "floating_layout")
historyMenuItem = getResourceId(ID, "history_menu_item")
inlineTimeBarAdBreakMarkerColor =
getResourceId(COLOR, "inline_time_bar_ad_break_marker_color")
inlineTimeBarAdBreakMarkerColor = getResourceId(COLOR, "inline_time_bar_ad_break_marker_color")
inlineTimeBarProgressColor = getResourceId(COLOR, "inline_time_bar_progress_color")
interstitialsContainer = getResourceId(ID, "interstitials_container")
isTablet = getResourceId(BOOL, "is_tablet")
@ -157,8 +156,7 @@ internal val sharedResourceIdPatch = resourcePatch(
modernDialogBackground = getResourceId(DRAWABLE, "modern_dialog_background")
musicNotifierShelf = getResourceId(LAYOUT, "music_notifier_shelf")
musicTasteBuilderShelf = getResourceId(LAYOUT, "music_tastebuilder_shelf")
namesInactiveAccountThumbnailSize =
getResourceId(DIMEN, "names_inactive_account_thumbnail_size")
namesInactiveAccountThumbnailSize = getResourceId(DIMEN, "names_inactive_account_thumbnail_size")
offlineSettingsMenuItem = getResourceId(ID, "offline_settings_menu_item")
playerOverlayChip = getResourceId(ID, "player_overlay_chip")
playerViewPager = getResourceId(ID, "player_view_pager")

View File

@ -25,8 +25,7 @@ val videoTypeHookPatch = bytecodePatch(
videoTypeFingerprint.methodOrThrow(videoTypeParentFingerprint).apply {
val getEnumIndex = indexOfGetEnumInstruction(this)
val enumClass =
(getInstruction<ReferenceInstruction>(getEnumIndex).reference as MethodReference).definingClass
val enumClass = (getInstruction<ReferenceInstruction>(getEnumIndex).reference as MethodReference).definingClass
val referenceIndex = indexOfFirstInstructionOrThrow(getEnumIndex) {
opcode == Opcode.SGET_OBJECT &&
getReference<FieldReference>()?.type == enumClass

View File

@ -71,8 +71,7 @@ val playerResponseMethodHookPatch = bytecodePatch(
val beforeVideoIdHooks =
hooks.filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed()
val videoIdHooks = hooks.filterIsInstance<Hook.VideoId>().asReversed()
val videoIdAndPlaylistIdHooks =
hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
val videoIdAndPlaylistIdHooks = hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
val afterVideoIdHooks = hooks.filterIsInstance<Hook.PlayerParameter>().asReversed()
// Add the hooks in this specific order as they insert instructions at the beginning of the method.

View File

@ -56,8 +56,7 @@ val navigationButtonsPatch = bytecodePatch(
if (bottomNavScreenFingerprint.resolvable()) {
val bottomNavScreenMutableClass = with(bottomNavScreenFingerprint.methodOrThrow()) {
val startIndex = indexOfGetDimensionPixelSizeInstruction(this)
val targetIndex =
indexOfFirstInstructionOrThrow(startIndex, Opcode.NEW_INSTANCE)
val targetIndex = indexOfFirstInstructionOrThrow(startIndex, Opcode.NEW_INSTANCE)
val targetReference =
getInstruction<ReferenceInstruction>(targetIndex).reference.toString()
@ -66,9 +65,7 @@ val navigationButtonsPatch = bytecodePatch(
?: throw ClassNotFoundException("Failed to find class $targetReference")
}
bottomNavScreenOnGlobalLayoutFingerprint.second.matchOrNull(
bottomNavScreenMutableClass
)
bottomNavScreenOnGlobalLayoutFingerprint.second.matchOrNull(bottomNavScreenMutableClass)
?.let {
it.method.apply {
val startIndex = it.patternMatch!!.startIndex
@ -85,8 +82,7 @@ val navigationButtonsPatch = bytecodePatch(
// Legacy method.
bottomNavScreenHandlerFingerprint.methodOrThrow().apply {
val targetIndex = indexOfGetItemsInstruction(this) + 1
val targetRegister =
getInstruction<OneRegisterInstruction>(targetIndex).registerA
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1, """

View File

@ -0,0 +1,36 @@
package app.revanced.patches.reddit.layout.screenshotpopup
import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.fingerprint.legacyFingerprint
import com.android.tools.smali.dexlib2.Opcode
/**
* Reddit 2025.06.0 ~
*/
internal val screenshotTakenBannerFingerprint = legacyFingerprint(
name = "screenshotTakenBannerFingerprint",
returnType = "L",
opcodes = listOf(
Opcode.CONST_4,
Opcode.IF_NE,
),
customFingerprint = { method, classDef ->
method.containsLiteralInstruction(screenShotShareBanner) &&
classDef.type.startsWith("Lcom/reddit/sharing/screenshot/composables/") &&
method.name == "invoke"
}
)
/**
* ~ Reddit 2025.05.1
*/
internal val screenshotTakenBannerLegacyFingerprint = legacyFingerprint(
name = "screenshotTakenBannerLegacyFingerprint",
returnType = "V",
parameters = listOf("Landroidx/compose/runtime/", "I"),
customFingerprint = { method, classDef ->
classDef.type.endsWith("\$ScreenshotTakenBannerKt\$lambda-1\$1;") &&
method.name == "invoke"
}
)

View File

@ -1,22 +1,26 @@
package app.revanced.patches.reddit.layout.screenshotpopup
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
import app.revanced.patches.reddit.utils.patch.PatchList.DISABLE_SCREENSHOT_POPUP
import app.revanced.patches.reddit.utils.resourceid.screenShotShareBanner
import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater
import app.revanced.patches.reddit.utils.settings.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.findMutableMethodOf
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_METHOD_DESCRIPTOR =
"$PATCHES_PATH/ScreenshotPopupPatch;->disableScreenshotPopup()Z"
@Suppress("unused")
val screenshotPopupPatch = bytecodePatch(
@ -25,67 +29,39 @@ val screenshotPopupPatch = bytecodePatch(
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch)
dependsOn(
settingsPatch,
sharedResourceIdPatch,
)
execute {
fun indexOfShowBannerInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<FieldReference>()
opcode == Opcode.IGET_OBJECT &&
reference?.name?.contains("shouldShowBanner") == true &&
reference.definingClass.startsWith("Lcom/reddit/sharing/screenshot/") == true
if (is_2025_06_or_greater) {
screenshotTakenBannerFingerprint.methodOrThrow().apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(screenShotShareBanner)
val insertIndex = indexOfFirstInstructionReversedOrThrow(literalIndex, Opcode.CONST_4)
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.SGET_OBJECT)
addInstructionsWithLabels(
insertIndex, """
invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR
move-result v$insertRegister
if-nez v$insertRegister, :hidden
""", ExternalLabel("hidden", getInstruction(jumpIndex))
)
}
fun indexOfSetValueInstruction(method: Method) =
method.indexOfFirstInstruction {
getReference<MethodReference>()?.name == "setValue"
} else {
screenshotTakenBannerLegacyFingerprint.methodOrThrow().apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR
move-result v0
if-eqz v0, :dismiss
return-void
""", ExternalLabel("dismiss", getInstruction(0))
)
}
fun indexOfBooleanInstruction(method: Method, startIndex: Int = 0) =
method.indexOfFirstInstruction(startIndex) {
val reference = getReference<FieldReference>()
opcode == Opcode.SGET_OBJECT &&
reference?.definingClass == "Ljava/lang/Boolean;" &&
reference.type == "Ljava/lang/Boolean;"
}
val isScreenShotMethod: Method.() -> Boolean = {
definingClass.startsWith("Lcom/reddit/sharing/screenshot/") &&
name == "invokeSuspend" &&
indexOfShowBannerInstruction(this) >= 0 &&
indexOfBooleanInstruction(this) >= 0 &&
indexOfSetValueInstruction(this) >= 0
}
var hookCount = 0
classes.forEach { classDef ->
classDef.methods.forEach { method ->
if (method.isScreenShotMethod()) {
proxy(classDef)
.mutableClass
.findMutableMethodOf(method)
.apply {
val showBannerIndex = indexOfShowBannerInstruction(this)
val booleanIndex = indexOfBooleanInstruction(this, showBannerIndex)
val booleanRegister =
getInstruction<OneRegisterInstruction>(booleanIndex).registerA
addInstructions(
booleanIndex + 1, """
invoke-static {v$booleanRegister}, $PATCHES_PATH/ScreenshotPopupPatch;->disableScreenshotPopup(Ljava/lang/Boolean;)Ljava/lang/Boolean;
move-result-object v$booleanRegister
"""
)
hookCount++
}
}
}
}
if (hookCount == 0) {
throw PatchException("Failed to find hook method")
}
updatePatchStatus(

View File

@ -1,6 +1,5 @@
package app.revanced.patches.reddit.layout.subredditdialog
import app.revanced.patches.reddit.utils.resourceid.nsfwDialogTitle
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
@ -72,14 +71,6 @@ fun indexOfHasBeenVisitedInstruction(method: Method) =
reference.returnType == "Z"
}
internal val nsfwAlertBuilderFingerprint = legacyFingerprint(
name = "nsfwAlertBuilderFingerprint",
literals = listOf(nsfwDialogTitle),
customFingerprint = { method, _ ->
method.definingClass.startsWith("Lcom/reddit/screen/nsfw")
}
)
internal val redditAlertDialogsFingerprint = legacyFingerprint(
name = "redditAlertDialogsFingerprint",
returnType = "V",

View File

@ -3,12 +3,10 @@ package app.revanced.patches.reddit.layout.subredditdialog
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.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
import app.revanced.patches.reddit.utils.patch.PatchList.REMOVE_SUBREDDIT_DIALOG
import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.reddit.utils.settings.is_2024_41_or_greater
import app.revanced.patches.reddit.utils.settings.is_2025_01_or_greater
import app.revanced.patches.reddit.utils.settings.is_2025_05_or_greater
@ -16,9 +14,7 @@ import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater
import app.revanced.patches.reddit.utils.settings.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
@ -36,10 +32,7 @@ val subRedditDialogPatch = bytecodePatch(
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(
settingsPatch,
sharedResourceIdPatch,
)
dependsOn(settingsPatch)
execute {
@ -49,8 +42,7 @@ val subRedditDialogPatch = bytecodePatch(
.apply {
listOfIsLoggedInInstruction(this)
.forEach { index ->
val register =
getInstruction<OneRegisterInstruction>(index + 1).registerA
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructions(
index + 2, """
@ -89,32 +81,6 @@ val subRedditDialogPatch = bytecodePatch(
"""
)
}
var hookCount = 0
nsfwAlertBuilderFingerprint.mutableClassOrThrow().let {
it.methods.forEach { method ->
method.apply {
val showIndex = indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "show"
}
if (showIndex >= 0) {
val dialogRegister = getInstruction<OneRegisterInstruction>(showIndex + 1).registerA
addInstruction(
showIndex + 2,
"invoke-static {v$dialogRegister}, $EXTENSION_CLASS_DESCRIPTOR->dismissNSFWDialog(Ljava/lang/Object;)V"
)
hookCount++
}
}
}
}
if (hookCount == 0) {
throw PatchException("Failed to find hook method")
}
}
// Not used in latest Reddit client.

View File

@ -11,7 +11,7 @@ internal object Constants {
setOf(
"2024.17.0", // This is the last version that can be patched without anti-split.
"2025.05.1", // This was the latest version supported by the previous RVX patch.
"2025.12.1", // This is the latest version supported by the RVX patch.
"2025.12.0", // This is the latest version supported by the RVX patch.
)
)
}

View File

@ -5,7 +5,7 @@ import app.revanced.patches.shared.mapping.ResourceType.STRING
import app.revanced.patches.shared.mapping.getResourceId
import app.revanced.patches.shared.mapping.resourceMappingPatch
var nsfwDialogTitle = -1L
var screenShotShareBanner = -1L
private set
internal val sharedResourceIdPatch = resourcePatch(
@ -14,6 +14,6 @@ internal val sharedResourceIdPatch = resourcePatch(
dependsOn(resourceMappingPatch)
execute {
nsfwDialogTitle = getResourceId(STRING, "nsfw_dialog_title")
screenShotShareBanner = getResourceId(STRING, "screenshot_share_banner_title")
}
}

View File

@ -62,7 +62,7 @@ fun baseAdsPatch(
)
}
val getAdvertisingIdMethod = with(advertisingIdFingerprint.methodOrThrow()) {
val getAdvertisingIdMethod = with (advertisingIdFingerprint.methodOrThrow()) {
val getAdvertisingIdIndex = indexOfGetAdvertisingIdInstruction(this)
getWalkerMethod(getAdvertisingIdIndex)
}

View File

@ -0,0 +1,48 @@
package app.revanced.patches.shared.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var initIndex = -1
private var isRange = true
internal val cronetEngineContextHook = extensionHook(
insertIndexResolver = { method ->
initIndex = method.indexOfFirstInstruction(Opcode.INVOKE_DIRECT_RANGE)
if (initIndex < 0) {
initIndex = method.indexOfFirstInstructionOrThrow(Opcode.INVOKE_DIRECT)
isRange = false
}
initIndex
},
contextRegisterResolver = { method ->
val initInstruction =
method.implementation!!.instructions.elementAt(initIndex)
if (isRange) {
val overrideInstruction = initInstruction as Instruction3rc
"v${overrideInstruction.startRegister + 1}"
} else {
val overrideInstruction = initInstruction as FiveRegisterInstruction
"v${overrideInstruction.registerD}"
}
},
) {
returns("Lorg/chromium/net/CronetEngine;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
strings("Could not create CronetEngine")
custom { method, classDef ->
method.indexOfFirstInstruction {
(opcode == Opcode.INVOKE_DIRECT || opcode == Opcode.INVOKE_DIRECT_RANGE) &&
getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Landroid/content/Context;"
} >= 0
}
}

View File

@ -0,0 +1,37 @@
package app.revanced.patches.shared.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var getResourcesIndex = -1
internal val firebaseInitProviderContextHook = extensionHook(
insertIndexResolver = { method ->
getResourcesIndex = indexOfGerResourcesInstruction(method)
getResourcesIndex + 2
},
contextRegisterResolver = { method ->
val overrideInstruction =
method.implementation!!.instructions.elementAt(getResourcesIndex)
as FiveRegisterInstruction
"v${overrideInstruction.registerC}"
},
) {
strings("firebase_database_url")
custom { method, _ ->
indexOfGerResourcesInstruction(method) >= 0
}
}
private fun indexOfGerResourcesInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.toString() =="Landroid/content/Context;->getResources()Landroid/content/res/Resources;"
}

View File

@ -6,6 +6,8 @@ import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@ -106,8 +108,3 @@ internal val primesLifecycleEventFingerprint = legacyFingerprint(
} >= 0
}
)
internal val primeMethodFingerprint = legacyFingerprint(
name = "primesLifecycleEventFingerprint",
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
)

View File

@ -4,7 +4,6 @@ import app.revanced.patcher.Fingerprint
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.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
@ -19,13 +18,9 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
import app.revanced.patches.shared.gms.Constants.ACTIONS
import app.revanced.patches.shared.gms.Constants.ACTIONS_LEGACY
import app.revanced.patches.shared.gms.Constants.AUTHORITIES
import app.revanced.patches.shared.gms.Constants.AUTHORITIES_LEGACY
import app.revanced.patches.shared.gms.Constants.PERMISSIONS
import app.revanced.patches.shared.gms.Constants.PERMISSIONS_LEGACY
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.fingerprint.methodOrNull
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference
@ -88,9 +83,9 @@ fun gmsCoreSupportPatch(
key = "gmsCoreVendorGroupId",
default = "app.revanced",
values =
mapOf(
"ReVanced" to "app.revanced",
),
mapOf(
"ReVanced" to "app.revanced",
),
title = "GmsCore vendor group ID",
description = "The vendor's group ID for GmsCore.",
required = true,
@ -132,14 +127,6 @@ fun gmsCoreSupportPatch(
required = true
) { it!!.matches(Regex(PACKAGE_NAME_REGEX_PATTERN)) && it != ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC }
val patchAllManifest by booleanOption(
key = "patchAllManifest",
default = true,
title = "Patch all manifest components",
description = "Patch all permissions, intents and content provider authorities supported by GmsCore.",
required = true,
)
dependsOn(
gmsCoreSupportResourcePatchFactory(
gmsCoreVendorGroupIdOption,
@ -152,20 +139,6 @@ fun gmsCoreSupportPatch(
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
execute {
val patchAllManifestEnabled = patchAllManifest == true
val permissions = if (patchAllManifestEnabled)
PERMISSIONS
else
PERMISSIONS_LEGACY
val actions = if (patchAllManifestEnabled)
ACTIONS
else
ACTIONS_LEGACY
val authorities = if (patchAllManifestEnabled)
AUTHORITIES
else
AUTHORITIES_LEGACY
fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
val mutableClass by lazy {
proxy(it).mutableClass
@ -209,9 +182,9 @@ fun gmsCoreSupportPatch(
when (referencedString) {
"com.google",
"com.google.android.gms",
in permissions,
in actions,
in authorities,
in PERMISSIONS,
in ACTIONS,
in AUTHORITIES,
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
// TODO: Add this permission when bumping GmsCore
@ -223,7 +196,7 @@ fun gmsCoreSupportPatch(
// only when content:// uri
if (str.startsWith("content://")) {
// check if matches any authority
for (authority in authorities) {
for (authority in AUTHORITIES) {
val uriPrefix = "content://$authority"
if (str.startsWith(uriPrefix)) {
return str.replace(
@ -250,56 +223,40 @@ fun gmsCoreSupportPatch(
}
}
fun transformPrimeMethod(packageName: String) {
if (patchAllManifestEnabled) {
primeMethodFingerprint.methodOrNull()?.apply {
var register = 2
val index = instructions.indexOfFirst {
if (it.getReference<StringReference>()?.string != fromPackageName) return@indexOfFirst false
register = (it as OneRegisterInstruction).registerA
return@indexOfFirst true
fun transformPrimeMethod() {
setOf(
primesBackgroundInitializationFingerprint,
primesLifecycleEventFingerprint
).forEach { fingerprint ->
fingerprint.methodOrThrow().apply {
val exceptionIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.NEW_INSTANCE &&
(this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;"
}
replaceInstruction(index, "const-string v$register, \"$packageName\"")
val index =
indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(
index,
"const/4 v$register, 0x1"
)
}
} else {
setOf(
primesBackgroundInitializationFingerprint,
primesLifecycleEventFingerprint
).forEach { fingerprint ->
fingerprint.methodOrThrow().apply {
val exceptionIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.NEW_INSTANCE &&
(this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;"
}
val index =
indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(
index,
"const/4 v$register, 0x1"
)
}
}
primesApiFingerprint.mutableClassOrThrow().methods.filter { method ->
method.name != "<clinit>" &&
method.returnType == "V"
}.forEach { method ->
method.apply {
val index = if (MethodUtil.isConstructor(method))
indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.name == "<init>"
} + 1
else 0
addInstruction(
index,
"return-void"
)
}
}
primesApiFingerprint.mutableClassOrThrow().methods.filter { method ->
method.name != "<clinit>" &&
method.returnType == "V"
}.forEach { method ->
method.apply {
val index = if (MethodUtil.isConstructor(method))
indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.name == "<init>"
} + 1
else 0
addInstruction(
index,
"return-void"
)
}
}
}
@ -323,40 +280,37 @@ fun gmsCoreSupportPatch(
return@transform null
}
val earlyReturnFingerprints = mutableListOf(
// Return these methods early to prevent the app from crashing.
setOf(
castContextFetchFingerprint,
castDynamiteModuleFingerprint,
castDynamiteModuleV2Fingerprint,
googlePlayUtilityFingerprint,
serviceCheckFingerprint
)
serviceCheckFingerprint,
sslGuardFingerprint,
).forEach { it.methodOrThrow().returnEarly() }
if (patchAllManifestEnabled) {
earlyReturnFingerprints += listOf(sslGuardFingerprint)
// Prevent spam logs.
eCatcherFingerprint.methodOrThrow().apply {
val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY)
addInstruction(index, "return-void")
}
// Prevent spam logs.
eCatcherFingerprint.methodOrThrow().apply {
val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY)
addInstruction(index, "return-void")
}
// Return these methods early to prevent the app from crashing.
earlyReturnFingerprints.forEach { it.methodOrThrow().returnEarly() }
// Specific method that needs to be patched.
transformPrimeMethod(packageName)
transformPrimeMethod()
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
if (checkGmsCore == true) {
mainActivityOnCreateFingerprint.method.apply {
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
val setContextIndex = indexOfFirstInstruction {
val reference =
getReference<MethodReference>() ?: return@indexOfFirstInstruction false
mainActivityOnCreateFingerprint.method.apply {
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
val setContextIndex = indexOfFirstInstruction {
val reference =
getReference<MethodReference>() ?: return@indexOfFirstInstruction false
reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V"
}
reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V"
}
// Add after setContext call, because this patch needs the context.
// Add after setContext call, because this patch needs the context.
if (checkGmsCore == true) {
addInstructions(
if (setContextIndex < 0) 0 else setContextIndex + 1,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
@ -381,30 +335,7 @@ fun gmsCoreSupportPatch(
* that are present in GmsCore which need to be transformed.
*/
private object Constants {
/**
* All permissions.
*/
val PERMISSIONS = setOf(
"com.google.android.c2dm.permission.RECEIVE",
"com.google.android.c2dm.permission.SEND",
"com.google.android.gms.auth.api.phone.permission.SEND",
"com.google.android.gms.permission.AD_ID",
"com.google.android.gms.permission.AD_ID_NOTIFICATION",
"com.google.android.gms.permission.CAR_FUEL",
"com.google.android.gms.permission.CAR_INFORMATION",
"com.google.android.gms.permission.CAR_MILEAGE",
"com.google.android.gms.permission.CAR_SPEED",
"com.google.android.gms.permission.CAR_VENDOR_EXTENSION",
"com.google.android.googleapps.permission.GOOGLE_AUTH",
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
"com.google.android.providers.gsf.permission.READ_GSERVICES",
)
val PERMISSIONS_LEGACY = setOf(
// C2DM / GCM
"com.google.android.c2dm.permission.RECEIVE",
"com.google.android.c2dm.permission.SEND",
@ -418,234 +349,7 @@ private object Constants {
// "com.google.android.gms.permission.ACTIVITY_RECOGNITION",
)
/**
* All intent actions.
*/
val ACTIONS = setOf(
"com.google.android.c2dm.intent.RECEIVE",
"com.google.android.c2dm.intent.REGISTER",
"com.google.android.c2dm.intent.REGISTRATION",
"com.google.android.c2dm.intent.UNREGISTER",
"com.google.android.contextmanager.service.ContextManagerService.START",
"com.google.android.gcm.intent.SEND",
"com.google.android.gms.accounts.ACCOUNT_SERVICE",
"com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS",
"com.google.android.gms.accountsettings.action.BROWSE_SETTINGS",
"com.google.android.gms.accountsettings.action.VIEW_SETTINGS",
"com.google.android.gms.accountsettings.MY_ACCOUNT",
"com.google.android.gms.accountsettings.PRIVACY_SETTINGS",
"com.google.android.gms.accountsettings.SECURITY_SETTINGS",
"com.google.android.gms.ads.gservice.START",
"com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION",
"com.google.android.gms.ads.service.CACHE",
"com.google.android.gms.ads.service.CONSENT_LOOKUP",
"com.google.android.gms.ads.service.HTTP",
"com.google.android.gms.analytics.service.START",
"com.google.android.gms.app.settings.GoogleSettingsLink",
"com.google.android.gms.appstate.service.START",
"com.google.android.gms.appusage.service.START",
"com.google.android.gms.asterism.service.START",
"com.google.android.gms.audiomodem.service.AudioModemService.START",
"com.google.android.gms.audit.service.START",
"com.google.android.gms.auth.account.authapi.START",
"com.google.android.gms.auth.account.authenticator.auto.service.START",
"com.google.android.gms.auth.account.authenticator.chromeos.START",
"com.google.android.gms.auth.account.authenticator.tv.service.START",
"com.google.android.gms.auth.account.data.service.START",
"com.google.android.gms.auth.api.credentials.PICKER",
"com.google.android.gms.auth.api.credentials.service.START",
"com.google.android.gms.auth.api.identity.service.authorization.START",
"com.google.android.gms.auth.api.identity.service.credentialsaving.START",
"com.google.android.gms.auth.api.identity.service.signin.START",
"com.google.android.gms.auth.api.phone.service.InternalService.START",
"com.google.android.gms.auth.api.signin.service.START",
"com.google.android.gms.auth.be.appcert.AppCertService",
"com.google.android.gms.auth.blockstore.service.START",
"com.google.android.gms.auth.config.service.START",
"com.google.android.gms.auth.cryptauth.cryptauthservice.START",
"com.google.android.gms.auth.GOOGLE_SIGN_IN",
"com.google.android.gms.auth.login.LOGIN",
"com.google.android.gms.auth.proximity.devicesyncservice.START",
"com.google.android.gms.auth.proximity.securechannelservice.START",
"com.google.android.gms.auth.proximity.START",
"com.google.android.gms.auth.service.START",
"com.google.android.gms.backup.ACTION_BACKUP_SETTINGS",
"com.google.android.gms.backup.G1_BACKUP",
"com.google.android.gms.backup.G1_RESTORE",
"com.google.android.gms.backup.GMS_MODULE_RESTORE",
"com.google.android.gms.beacon.internal.IBleService.START",
"com.google.android.gms.car.service.START",
"com.google.android.gms.carrierauth.service.START",
"com.google.android.gms.cast.firstparty.START",
"com.google.android.gms.cast.remote_display.service.START",
"com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE",
"com.google.android.gms.cast_mirroring.service.START",
"com.google.android.gms.checkin.BIND_TO_SERVICE",
"com.google.android.gms.chromesync.service.START",
"com.google.android.gms.clearcut.service.START",
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
"com.google.android.gms.common.download.START",
"com.google.android.gms.common.service.START",
"com.google.android.gms.common.telemetry.service.START",
"com.google.android.gms.config.START",
"com.google.android.gms.constellation.service.START",
"com.google.android.gms.credential.manager.service.firstparty.START",
"com.google.android.gms.deviceconnection.service.START",
"com.google.android.gms.drive.ApiService.RESET_AFTER_BOOT",
"com.google.android.gms.drive.ApiService.START",
"com.google.android.gms.drive.ApiService.STOP",
"com.google.android.gms.droidguard.service.INIT",
"com.google.android.gms.droidguard.service.PING",
"com.google.android.gms.droidguard.service.START",
"com.google.android.gms.enterprise.loader.service.START",
"com.google.android.gms.facs.cache.service.START",
"com.google.android.gms.facs.internal.service.START",
"com.google.android.gms.feedback.internal.IFeedbackService",
"com.google.android.gms.fido.credentialstore.internal_service.START",
"com.google.android.gms.fido.fido2.privileged.START",
"com.google.android.gms.fido.fido2.regular.START",
"com.google.android.gms.fido.fido2.zeroparty.START",
"com.google.android.gms.fido.sourcedevice.service.START",
"com.google.android.gms.fido.targetdevice.internal_service.START",
"com.google.android.gms.fido.u2f.privileged.START",
"com.google.android.gms.fido.u2f.thirdparty.START",
"com.google.android.gms.fido.u2f.zeroparty.START",
"com.google.android.gms.fitness.BleApi",
"com.google.android.gms.fitness.ConfigApi",
"com.google.android.gms.fitness.GoalsApi",
"com.google.android.gms.fitness.GoogleFitnessService.START",
"com.google.android.gms.fitness.HistoryApi",
"com.google.android.gms.fitness.InternalApi",
"com.google.android.gms.fitness.RecordingApi",
"com.google.android.gms.fitness.SensorsApi",
"com.google.android.gms.fitness.SessionsApi",
"com.google.android.gms.fonts.service.START",
"com.google.android.gms.freighter.service.START",
"com.google.android.gms.games.internal.connect.service.START",
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
"com.google.android.gms.games.service.START",
"com.google.android.gms.gass.START",
"com.google.android.gms.gmscompliance.service.START",
"com.google.android.gms.googlehelp.HELP",
"com.google.android.gms.googlehelp.service.GoogleHelpService.START",
"com.google.android.gms.growth.service.START",
"com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START",
"com.google.android.gms.icing.INDEX_SERVICE",
"com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE",
"com.google.android.gms.identity.service.BIND",
"com.google.android.gms.inappreach.service.START",
"com.google.android.gms.instantapps.START",
"com.google.android.gms.kids.service.START",
"com.google.android.gms.languageprofile.service.START",
"com.google.android.gms.learning.internal.dynamitesupport.START",
"com.google.android.gms.learning.intservice.START",
"com.google.android.gms.learning.predictor.START",
"com.google.android.gms.learning.trainer.START",
"com.google.android.gms.learning.training.background.START",
"com.google.android.gms.location.places.GeoDataApi",
"com.google.android.gms.location.places.PlaceDetectionApi",
"com.google.android.gms.location.places.PlacesApi",
"com.google.android.gms.location.reporting.service.START",
"com.google.android.gms.location.settings.LOCATION_HISTORY",
"com.google.android.gms.location.settings.LOCATION_REPORTING_SETTINGS",
"com.google.android.gms.locationsharing.api.START",
"com.google.android.gms.locationsharingreporter.service.START",
"com.google.android.gms.lockbox.service.START",
"com.google.android.gms.matchstick.lighter.service.START",
"com.google.android.gms.mdm.services.DeviceManagerApiService.START",
"com.google.android.gms.mdm.services.START",
"com.google.android.gms.mdns.service.START",
"com.google.android.gms.measurement.START",
"com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START",
"com.google.android.gms.nearby.connection.service.START",
"com.google.android.gms.nearby.fastpair.START",
"com.google.android.gms.nearby.messages.service.NearbyMessagesService.START",
"com.google.android.gms.nearby.sharing.service.NearbySharingService.START",
"com.google.android.gms.nearby.sharing.START_SERVICE",
"com.google.android.gms.notifications.service.START",
"com.google.android.gms.ocr.service.internal.START",
"com.google.android.gms.ocr.service.START",
"com.google.android.gms.oss.licenses.service.START",
"com.google.android.gms.payse.service.BIND",
"com.google.android.gms.people.contactssync.service.START",
"com.google.android.gms.people.service.START",
"com.google.android.gms.phenotype.service.START",
"com.google.android.gms.photos.autobackup.service.START",
"com.google.android.gms.playlog.service.START",
"com.google.android.gms.plus.service.default.INTENT",
"com.google.android.gms.plus.service.image.INTENT",
"com.google.android.gms.plus.service.internal.START",
"com.google.android.gms.plus.service.START",
"com.google.android.gms.potokens.service.START",
"com.google.android.gms.pseudonymous.service.START",
"com.google.android.gms.rcs.START",
"com.google.android.gms.reminders.service.START",
"com.google.android.gms.romanesco.MODULE_BACKUP_AGENT",
"com.google.android.gms.romanesco.service.START",
"com.google.android.gms.safetynet.service.START",
"com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE",
"com.google.android.gms.search.service.SEARCH_AUTH_START",
"com.google.android.gms.semanticlocation.service.START_ODLH",
"com.google.android.gms.sesame.service.BIND",
"com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS",
"com.google.android.gms.setup.auth.SecondDeviceAuth.START",
"com.google.android.gms.signin.service.START",
"com.google.android.gms.smartdevice.d2d.SourceDeviceService.START",
"com.google.android.gms.smartdevice.d2d.TargetDeviceService.START",
"com.google.android.gms.smartdevice.directtransfer.SourceDirectTransferService.START",
"com.google.android.gms.smartdevice.directtransfer.TargetDirectTransferService.START",
"com.google.android.gms.smartdevice.postsetup.PostSetupService.START",
"com.google.android.gms.smartdevice.setup.accounts.AccountsService.START",
"com.google.android.gms.smartdevice.wifi.START_WIFI_HELPER_SERVICE",
"com.google.android.gms.social.location.activity.service.START",
"com.google.android.gms.speech.service.START",
"com.google.android.gms.statementservice.EXECUTE",
"com.google.android.gms.stats.ACTION_UPLOAD_DROPBOX_ENTRIES",
"com.google.android.gms.tapandpay.service.BIND",
"com.google.android.gms.telephonyspam.service.START",
"com.google.android.gms.testsupport.service.START",
"com.google.android.gms.thunderbird.service.START",
"com.google.android.gms.trustagent.BridgeApi.START",
"com.google.android.gms.trustagent.StateApi.START",
"com.google.android.gms.trustagent.trustlet.trustletmanagerservice.BIND",
"com.google.android.gms.trustlet.bluetooth.service.BIND",
"com.google.android.gms.trustlet.connectionlessble.service.BIND",
"com.google.android.gms.trustlet.face.service.BIND",
"com.google.android.gms.trustlet.nfc.service.BIND",
"com.google.android.gms.trustlet.onbody.service.BIND",
"com.google.android.gms.trustlet.place.service.BIND",
"com.google.android.gms.trustlet.voiceunlock.service.BIND",
"com.google.android.gms.udc.service.START",
"com.google.android.gms.update.START_API_SERVICE",
"com.google.android.gms.update.START_SERVICE",
"com.google.android.gms.update.START_SINGLE_USER_API_SERVICE",
"com.google.android.gms.update.START_TV_API_SERVICE",
"com.google.android.gms.usagereporting.service.START",
"com.google.android.gms.userlocation.service.START",
"com.google.android.gms.vehicle.cabin.service.START",
"com.google.android.gms.vehicle.climate.service.START",
"com.google.android.gms.vehicle.info.service.START",
"com.google.android.gms.wallet.service.BIND",
"com.google.android.gms.walletp2p.service.firstparty.BIND",
"com.google.android.gms.walletp2p.service.zeroparty.BIND",
"com.google.android.gms.wearable.BIND",
"com.google.android.gms.wearable.BIND_LISTENER",
"com.google.android.gms.wearable.DATA_CHANGED",
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
"com.google.android.gms.wearable.NODE_CHANGED",
"com.google.android.gsf.action.GET_GLS",
"com.google.android.location.settings.LOCATION_REPORTING_SETTINGS",
"com.google.android.mdd.service.START",
"com.google.android.mdh.service.listener.START",
"com.google.android.mdh.service.START",
"com.google.android.mobstore.service.START",
"com.google.firebase.auth.api.gms.service.START",
"com.google.firebase.dynamiclinks.service.START",
"com.google.iid.TOKEN_REQUEST",
"com.google.android.gms.location.places.ui.PICK_PLACE",
)
val ACTIONS_LEGACY = setOf(
// C2DM / GCM
"com.google.android.c2dm.intent.REGISTER",
"com.google.android.c2dm.intent.REGISTRATION",
@ -703,19 +407,7 @@ private object Constants {
"com.google.android.gms.droidguard.service.START",
)
/**
* All content provider authorities.
*/
val AUTHORITIES = setOf(
"com.google.android.gms.auth.accounts",
"com.google.android.gms.chimera",
"com.google.android.gms.fonts",
"com.google.android.gms.phenotype",
"com.google.android.gsf.gservices",
"com.google.settings",
)
val AUTHORITIES_LEGACY = setOf(
// gsf
"com.google.android.gsf.gservices",

View File

@ -1,5 +1,6 @@
package app.revanced.patches.shared.mapping
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.resourcePatch
import org.w3c.dom.Element

View File

@ -147,8 +147,7 @@ fun ResourcePatchContext.baseTranslationsPatch(
val length = text.length
if (!text.endsWith("DEFAULT") &&
length >= 2 &&
text.subSequence(length - 2, length) !in filteredAppLanguages
) {
text.subSequence(length - 2, length) !in filteredAppLanguages) {
nodesToRemove.add(item)
}
}

View File

@ -262,10 +262,8 @@ val feedComponentsPatch = bytecodePatch(
val insertIndex = indexOfBufferParserInstruction(this)
if (is_19_46_or_greater) {
val objectIndex =
indexOfFirstInstructionReversedOrThrow(insertIndex, Opcode.IGET_OBJECT)
val objectRegister =
getInstruction<TwoRegisterInstruction>(objectIndex).registerA
val objectIndex = indexOfFirstInstructionReversedOrThrow(insertIndex, Opcode.IGET_OBJECT)
val objectRegister = getInstruction<TwoRegisterInstruction>(objectIndex).registerA
addInstructionsWithLabels(
insertIndex, """
@ -277,8 +275,7 @@ val feedComponentsPatch = bytecodePatch(
)
} else {
val objectIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT)
val objectRegister =
getInstruction<TwoRegisterInstruction>(objectIndex).registerA
val objectRegister = getInstruction<TwoRegisterInstruction>(objectIndex).registerA
val jumpIndex = it.patternMatch!!.startIndex
addInstructionsWithLabels(

View File

@ -90,8 +90,7 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
)
val playbackStartIndex = indexOfPlaybackStartDescriptorInstruction(this) + 1
val playbackStartRegister =
getInstruction<OneRegisterInstruction>(playbackStartIndex).registerA
val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex).registerA
val mapIndex = indexOfFirstInstructionOrThrow(playbackStartIndex) {
val reference = getReference<MethodReference>()
@ -170,24 +169,15 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
val playbackStartIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
}
val mapIndex =
indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
val mapIndex = indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
val mapRegister = getInstruction<TwoRegisterInstruction>(mapIndex).registerA
val playbackStartRegister =
getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA
val videoIdRegister =
getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA
val videoIdRegister = getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
addInstructionsWithLabels(
playbackStartIndex + 2, """
move-object/from16 v$mapRegister, p2
${
fetchChannelIdInstructions(
playbackStartRegister,
mapRegister,
videoIdRegister
)
}
${fetchChannelIdInstructions(playbackStartRegister, mapRegister, videoIdRegister)}
"""
)
}

View File

@ -80,8 +80,7 @@ private val snackBarComponentsBytecodePatch = bytecodePatch(
bottomUiContainerThemeFingerprint.matchOrThrow().let {
it.method.apply {
val darkThemeIndex = it.patternMatch!!.startIndex + 2
val darkThemeReference =
getInstruction<ReferenceInstruction>(darkThemeIndex).reference.toString()
val darkThemeReference = getInstruction<ReferenceInstruction>(darkThemeIndex).reference.toString()
implementation!!.instructions
.withIndex()
@ -92,8 +91,7 @@ private val snackBarComponentsBytecodePatch = bytecodePatch(
.map { (index, _) -> index }
.reversed()
.forEach { index ->
val appThemeIndex =
indexOfFirstInstructionReversedOrThrow(index, Opcode.MOVE_RESULT_OBJECT)
val appThemeIndex = indexOfFirstInstructionReversedOrThrow(index, Opcode.MOVE_RESULT_OBJECT)
val appThemeRegister =
getInstruction<OneRegisterInstruction>(appThemeIndex).registerA
val darkThemeRegister =

View File

@ -86,11 +86,7 @@ val shortsActionButtonsPatch = resourcePatch(
// Some directory is missing in the bundles.
if (inputStreamForLegacy != null && fromFileResolved.exists()) {
Files.copy(
inputStreamForLegacy,
fromFileResolved.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
Files.copy(inputStreamForLegacy, fromFileResolved.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
if (is_19_36_or_greater) {
@ -99,11 +95,7 @@ val shortsActionButtonsPatch = resourcePatch(
// Some directory is missing in the bundles.
if (inputStreamForNew != null && toFileResolved.exists()) {
Files.copy(
inputStreamForNew,
toFileResolved.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
Files.copy(inputStreamForNew, toFileResolved.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
}

View File

@ -217,23 +217,15 @@ val customBrandingIconPatch = resourcePatch(
}
}
val styleList = mutableListOf(
Pair(
"Base.Theme.YouTube.Launcher",
"@style/Theme.AppCompat.DayNight.NoActionBar"
),
)
val styleMap = mutableMapOf<String, String>()
styleMap["Base.Theme.YouTube.Launcher"] =
"@style/Theme.AppCompat.DayNight.NoActionBar"
if (is_19_32_or_greater) {
styleList += listOf(
Pair(
"Theme.YouTube.Home",
"@style/Base.V27.Theme.YouTube.Home"
),
)
styleMap["Theme.YouTube.Home"] = "@style/Base.V27.Theme.YouTube.Home"
}
styleList.forEach { (nodeAttributeName, nodeAttributeParent) ->
styleMap.forEach { (nodeAttributeName, nodeAttributeParent) ->
document("res/values-v31/styles.xml").use { document ->
val resourcesNode =
document.getElementsByTagName("resources").item(0) as Element
@ -242,27 +234,21 @@ val customBrandingIconPatch = resourcePatch(
style.setAttribute("name", nodeAttributeName)
style.setAttribute("parent", nodeAttributeParent)
val splashScreenAnimatedIcon = document.createElement("item")
splashScreenAnimatedIcon.setAttribute(
"name",
"android:windowSplashScreenAnimatedIcon"
)
splashScreenAnimatedIcon.textContent = "@drawable/avd_anim"
// Deprecated in Android 13+
val splashScreenAnimationDuration = document.createElement("item")
splashScreenAnimationDuration.setAttribute(
val primaryItem = document.createElement("item")
primaryItem.setAttribute("name", "android:windowSplashScreenAnimatedIcon")
primaryItem.textContent = "@drawable/avd_anim"
val secondaryItem = document.createElement("item")
secondaryItem.setAttribute(
"name",
"android:windowSplashScreenAnimationDuration"
)
splashScreenAnimationDuration.textContent =
if (appIcon.startsWith("revancify"))
"1500"
else
"1000"
secondaryItem.textContent = if (appIcon.startsWith("revancify"))
"1500"
else
"1000"
style.appendChild(splashScreenAnimatedIcon)
style.appendChild(splashScreenAnimationDuration)
style.appendChild(primaryItem)
style.appendChild(secondaryItem)
resourcesNode.appendChild(style)
}

View File

@ -4,6 +4,7 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_NAME_FOR_YOUTUBE
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusLabel
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.removeStringsElements
import app.revanced.util.valueOrThrow
@ -52,5 +53,7 @@ val customBrandingNamePatch = resourcePatch(
.appendChild(stringElement)
}
updatePatchStatusLabel(appName)
}
}

View File

@ -61,10 +61,8 @@ val sharedThemePatch = resourcePatch(
0 -> when (nodeAttributeName) {
"Base.Theme.YouTube.Launcher.Dark",
"Base.Theme.YouTube.Launcher.Cairo.Dark" -> "@color/yt_black1"
"Base.Theme.YouTube.Launcher.Light",
"Base.Theme.YouTube.Launcher.Cairo.Light" -> "@color/yt_white1"
else -> "null"
}

View File

@ -32,10 +32,8 @@ val accessibilityPatch = bytecodePatch(
.methods
.first { method -> method.name == "<init>" }
.apply {
val lifecycleObserverIndex =
indexOfFirstInstructionReversedOrThrow(Opcode.NEW_INSTANCE)
val lifecycleObserverClass =
getInstruction<ReferenceInstruction>(lifecycleObserverIndex).reference.toString()
val lifecycleObserverIndex = indexOfFirstInstructionReversedOrThrow(Opcode.NEW_INSTANCE)
val lifecycleObserverClass = getInstruction<ReferenceInstruction>(lifecycleObserverIndex).reference.toString()
findMethodOrThrow(lifecycleObserverClass) {
accessFlags == AccessFlags.PUBLIC or AccessFlags.FINAL &&

View File

@ -85,19 +85,17 @@ val backgroundPlaybackPatch = bytecodePatch(
backgroundPlaybackManagerCairoFragmentPrimaryFingerprint,
backgroundPlaybackManagerCairoFragmentSecondaryFingerprint
).forEach { fingerprint ->
fingerprint.matchOrThrow(backgroundPlaybackManagerCairoFragmentParentFingerprint)
.let {
it.method.apply {
val insertIndex = it.patternMatch!!.startIndex + 4
val insertRegister =
getInstruction<OneRegisterInstruction>(insertIndex).registerA
fingerprint.matchOrThrow(backgroundPlaybackManagerCairoFragmentParentFingerprint).let {
it.method.apply {
val insertIndex = it.patternMatch!!.startIndex + 4
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstruction(
insertIndex,
"const/4 v$insertRegister, 0x0"
)
}
addInstruction(
insertIndex,
"const/4 v$insertRegister, 0x0"
)
}
}
}
pipInputConsumerFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(

View File

@ -57,26 +57,20 @@ val actionButtonsPatch = bytecodePatch(
findMethodOrThrow(parameters[1].type) {
name == "toString"
}
val identifierReference = with(conversionContextToStringMethod) {
val identifierReference = with (conversionContextToStringMethod) {
val identifierStringIndex =
indexOfFirstStringInstructionOrThrow(", identifierProperty=")
val identifierStringAppendIndex =
indexOfFirstInstructionOrThrow(identifierStringIndex, Opcode.INVOKE_VIRTUAL)
val identifierStringAppendIndexRegister =
getInstruction<FiveRegisterInstruction>(identifierStringAppendIndex).registerD
val identifierStringAppendIndexRegister = getInstruction<FiveRegisterInstruction>(identifierStringAppendIndex).registerD
val identifierAppendIndex =
indexOfFirstInstructionOrThrow(
identifierStringAppendIndex + 1,
Opcode.INVOKE_VIRTUAL
)
val identifierRegister =
getInstruction<FiveRegisterInstruction>(identifierAppendIndex).registerD
val identifierIndex =
indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;" &&
(this as? TwoRegisterInstruction)?.registerA == identifierRegister
}
indexOfFirstInstructionOrThrow(identifierStringAppendIndex + 1, Opcode.INVOKE_VIRTUAL)
val identifierRegister = getInstruction<FiveRegisterInstruction>(identifierAppendIndex).registerD
val identifierIndex = indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;" &&
(this as? TwoRegisterInstruction)?.registerA == identifierRegister
}
getInstruction<ReferenceInstruction>(identifierIndex).reference
}

View File

@ -10,11 +10,11 @@ import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutCircl
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutIcon
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo
import app.revanced.patches.youtube.utils.resourceid.offlineActionsVideoDeletedUndoSnackbarText
import app.revanced.patches.youtube.utils.resourceid.verticalTouchOffsetToEnterFineScrubbing
import app.revanced.patches.youtube.utils.resourceid.seekEasyHorizontalTouchOffsetToStartScrubbing
import app.revanced.patches.youtube.utils.resourceid.suggestedAction
import app.revanced.patches.youtube.utils.resourceid.tapBloomView
import app.revanced.patches.youtube.utils.resourceid.touchArea
import app.revanced.patches.youtube.utils.resourceid.verticalTouchOffsetToEnterFineScrubbing
import app.revanced.patches.youtube.utils.resourceid.verticalTouchOffsetToStartFineScrubbing
import app.revanced.patches.youtube.utils.resourceid.videoZoomSnapIndicator
import app.revanced.util.fingerprint.legacyFingerprint

View File

@ -129,11 +129,9 @@ private val speedOverlayPatch = bytecodePatch(
// region patch for Custom speed overlay float value
val speedFieldReference = with(speedOverlayFloatValueFingerprint.methodOrThrow()) {
val literalIndex =
indexOfFirstLiteralInstructionOrThrow(SPEED_OVERLAY_LEGACY_FEATURE_FLAG)
val floatIndex =
indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
val speedFieldReference = with (speedOverlayFloatValueFingerprint.methodOrThrow()) {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(SPEED_OVERLAY_LEGACY_FEATURE_FLAG)
val floatIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
val floatRegister = getInstruction<TwoRegisterInstruction>(floatIndex).registerA
addInstructions(
@ -606,9 +604,7 @@ val playerComponentsPatch = bytecodePatch(
)
if (is_20_12_or_greater) {
filmStripOverlayMotionEventPrimaryFingerprint.matchOrThrow(
filmStripOverlayStartParentFingerprint
).let {
filmStripOverlayMotionEventPrimaryFingerprint.matchOrThrow(filmStripOverlayStartParentFingerprint).let {
it.method.apply {
val index = it.patternMatch!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(index).registerA
@ -617,9 +613,7 @@ val playerComponentsPatch = bytecodePatch(
}
}
filmStripOverlayMotionEventSecondaryFingerprint.matchOrThrow(
filmStripOverlayStartParentFingerprint
).let {
filmStripOverlayMotionEventSecondaryFingerprint.matchOrThrow(filmStripOverlayStartParentFingerprint).let {
it.method.apply {
val index = it.patternMatch!!.startIndex + 2
val register = getInstruction<OneRegisterInstruction>(index).registerA

View File

@ -7,6 +7,7 @@ import app.revanced.patches.youtube.utils.resourceid.quickActionsElementContaine
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.util.MethodUtil
internal val broadcastReceiverFingerprint = legacyFingerprint(
name = "broadcastReceiverFingerprint",

View File

@ -37,6 +37,7 @@ import app.revanced.patches.youtube.utils.youtubeControlsOverlayFingerprint
import app.revanced.patches.youtube.video.information.hookBackgroundPlayVideoInformation
import app.revanced.patches.youtube.video.information.videoEndMethod
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.Utils.printWarn
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow
@ -312,6 +313,8 @@ val fullscreenComponentsPatch = bytecodePatch(
}
settingArray += "SETTINGS: KEEP_LANDSCAPE_MODE"
} else {
printWarn("\"Keep landscape mode\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
}
// endregion

View File

@ -63,7 +63,6 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
// region modern miniplayer
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
// In later targets this feature flag does nothing and is dead code.
internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L

View File

@ -239,8 +239,7 @@ val miniplayerPatch = bytecodePatch(
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
targetIndex + 1, """
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getMiniplayerDefaultSize(I)I
move-result v$register
""",

View File

@ -256,8 +256,7 @@ val overlayButtonsPatch = resourcePatch(
width != "0.0dip",
)
val isButton =
id.endsWith("_button") && id != "@id/multiview_button" || id == "@id/youtube_controls_fullscreen_button_stub"
val isButton = id.endsWith("_button") && id != "@id/multiview_button" || id == "@id/youtube_controls_fullscreen_button_stub"
// Adjust TimeBar and Chapter bottom padding
val timBarItem = mutableMapOf(
@ -287,10 +286,7 @@ val overlayButtonsPatch = resourcePatch(
if (id.equals("@+id/bottom_margin")) {
node.setAttribute("android:layout_height", marginBottom)
} else if (id.equals("@id/time_bar_reference_view")) {
node.setAttribute(
"yt:layout_constraintBottom_toTopOf",
"@id/quick_actions_container"
)
node.setAttribute("yt:layout_constraintBottom_toTopOf", "@id/quick_actions_container")
}
}
}

View File

@ -14,6 +14,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import kotlin.collections.listOf
internal val shortsSeekbarColorFingerprint = legacyFingerprint(
name = "shortsSeekbarColorFingerprint",

View File

@ -139,8 +139,7 @@ val seekbarComponentsPatch = bytecodePatch(
reference?.returnType == "V" &&
reference.parameterTypes.isEmpty()
}
val thisInstanceRegister =
getInstruction<FiveRegisterInstruction>(tapSeekIndex).registerC
val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(tapSeekIndex).registerC
val tapSeekClass = getInstruction(tapSeekIndex)
.getReference<MethodReference>()!!
@ -275,10 +274,7 @@ val seekbarComponentsPatch = bytecodePatch(
playerSeekbarHandleColorPrimaryFingerprint,
playerSeekbarHandleColorSecondaryFingerprint
).forEach {
it.methodOrThrow().addColorChangeInstructions(
ytStaticBrandRed,
"getVideoPlayerSeekbarColorAccent"
)
it.methodOrThrow().addColorChangeInstructions(ytStaticBrandRed, "getVideoPlayerSeekbarColorAccent")
}
// If hiding feed seekbar thumbnails, then turn off the cairo gradient
// of the watch history menu items as they use the same gradient as the

View File

@ -22,34 +22,7 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val bottomSheetMenuDismissFingerprint = legacyFingerprint(
name = "bottomSheetMenuDismissFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
customFingerprint = { method, _ ->
indexOfDismissInstruction(method) >= 0
}
)
fun indexOfDismissInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.name == "dismiss" &&
reference.returnType == "V" &&
reference.parameterTypes.isEmpty()
}
internal val bottomSheetMenuItemClickFingerprint = legacyFingerprint(
name = "bottomSheetMenuItemClickFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("Landroid/widget/AdapterView;", "Landroid/view/View;", "I", "J"),
customFingerprint = { method, _ ->
method.name == "onItemClick"
}
)
import kotlin.collections.listOf
internal val bottomSheetMenuListBuilderFingerprint = legacyFingerprint(
name = "bottomSheetMenuListBuilderFingerprint",

View File

@ -35,7 +35,6 @@ import app.revanced.patches.youtube.utils.playservice.is_18_31_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_02_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_11_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_28_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
@ -75,6 +74,7 @@ import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
import app.revanced.patches.youtube.video.videoid.videoIdPatch
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.ResourceGroup
import app.revanced.util.addEntryValues
import app.revanced.util.cloneMutable
import app.revanced.util.copyResources
import app.revanced.util.findMethodOrThrow
@ -339,35 +339,7 @@ private val shortsCustomActionsPatch = bytecodePatch(
}
}
if (is_19_11_or_greater) {
// The type of the Shorts flyout menu is RecyclerView.
recyclerViewTreeObserverHook("$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
} else {
// The type of the Shorts flyout menu is ListView.
val dismissReference = with(
bottomSheetMenuDismissFingerprint.methodOrThrow(
bottomSheetMenuListBuilderFingerprint
)
) {
val dismissIndex = indexOfDismissInstruction(this)
getInstruction<ReferenceInstruction>(dismissIndex).reference
}
bottomSheetMenuItemClickFingerprint
.methodOrThrow(bottomSheetMenuListBuilderFingerprint)
.addInstructionsWithLabels(
0,
"""
invoke-static/range {p2 .. p2}, $EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onBottomSheetMenuItemClick(Landroid/view/View;)Z
move-result v0
if-eqz v0, :ignore
invoke-virtual {p0}, $dismissReference
return-void
:ignore
nop
""",
)
}
recyclerViewTreeObserverHook("$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
// endregion
@ -435,7 +407,7 @@ private val shortsRepeatPatch = bytecodePatch(
"setMainActivity"
)
val endScreenReference = with(reelEnumConstructorFingerprint.methodOrThrow()) {
val endScreenReference = with (reelEnumConstructorFingerprint.methodOrThrow()) {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID)
addInstructions(
@ -515,11 +487,7 @@ private val shortsRepeatPatch = bytecodePatch(
// Manually add the 'Autoplay' code that Google removed.
// Tested on YouTube 20.10.
if (is_20_09_or_greater) {
val (directReference, virtualReference) = with(
reelPlaybackFingerprint.methodOrThrow(
videoIdFingerprintShorts
)
) {
val (directReference, virtualReference) = with (reelPlaybackFingerprint.methodOrThrow(videoIdFingerprintShorts)) {
val directIndex = indexOfInitializationInstruction(this)
val virtualIndex = indexOfFirstInstructionOrThrow(directIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
@ -537,8 +505,7 @@ private val shortsRepeatPatch = bytecodePatch(
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.definingClass == EXTENSION_REPEAT_STATE_CLASS_DESCRIPTOR
}
val enumRegister =
getInstruction<OneRegisterInstruction>(extensionIndex + 1).registerA
val enumRegister = getInstruction<OneRegisterInstruction>(extensionIndex + 1).registerA
val freeIndex = indexOfFirstInstructionOrThrow(extensionIndex) {
opcode == Opcode.SGET_OBJECT &&
getReference<FieldReference>()?.name != "a"
@ -1024,8 +991,7 @@ val shortsComponentPatch = bytecodePatch(
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
}
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
val playbackStartRegister =
getInstruction<OneRegisterInstruction>(index + 1).registerA
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructionsWithLabels(
index + 2,

View File

@ -1,17 +1,41 @@
package app.revanced.patches.youtube.shorts.startupshortsreset
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
/**
* YouTube v18.15.40+
* YouTube v18.15.40 ~ YouTube 19.46.42
*/
internal val userWasInShortsConfigFingerprint = legacyFingerprint(
internal val userWasInShortsABConfigFingerprint = legacyFingerprint(
name = "userWasInShortsABConfigFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
literals = listOf(45358360L)
returnType = "V",
strings = listOf("Failed to get offline response: "),
customFingerprint = { method, _ ->
indexOfOptionalInstruction(method) >= 0
}
)
internal fun indexOfOptionalInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>().toString() == "Lj${'$'}/util/Optional;->of(Ljava/lang/Object;)Lj${'$'}/util/Optional;"
}
/**
* YouTube 19.47.53 ~
*/
internal val userWasInShortsABConfigAlternativeFingerprint = legacyFingerprint(
name = "userWasInShortsABConfigAlternativeFingerprint",
returnType = "V",
parameters = listOf("I"),
opcodes = listOf(Opcode.OR_INT_LIT8),
strings = listOf("alias", "null"),
)
/**

View File

@ -4,10 +4,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.patch.PatchList.DISABLE_RESUMING_SHORTS_ON_STARTUP
import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
@ -15,8 +19,10 @@ import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.fingerprint.matchOrThrow
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.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstStringInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -36,19 +42,50 @@ val resumingShortsOnStartupPatch = bytecodePatch(
execute {
userWasInShortsConfigFingerprint
.methodOrThrow()
.addInstructionsWithLabels(
0, """
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
move-result v0
if-eqz v0, :show
const/4 v0, 0x0
return v0
:show
nop
"""
)
fun MutableMethod.hookUserWasInShortsABConfig(startIndex: Int) {
val walkerIndex = implementation!!.instructions.let {
val subListIndex =
it.subList(startIndex, startIndex + 20).indexOfFirst { instruction ->
val reference = instruction.getReference<MethodReference>()
instruction.opcode == Opcode.INVOKE_VIRTUAL &&
reference?.returnType == "Z" &&
reference.definingClass != "Lj${'$'}/util/Optional;" &&
reference.parameterTypes.isEmpty()
}
if (subListIndex < 0)
throw PatchException("subListIndex not found")
startIndex + subListIndex
}
val walkerMethod = getWalkerMethod(walkerIndex)
// This method will only be called for the user being A/B tested.
// Presumably a method that processes the ProtoDataStore value (boolean) for the 'user_was_in_shorts' key.
walkerMethod.apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
move-result v0
if-eqz v0, :show
const/4 v0, 0x0
return v0
""", ExternalLabel("show", getInstruction(0))
)
}
}
if (is_19_46_or_greater) {
userWasInShortsABConfigAlternativeFingerprint.methodOrThrow().apply {
val stringIndex = indexOfFirstStringInstructionOrThrow("null")
val startIndex = indexOfFirstInstructionOrThrow(stringIndex, Opcode.OR_INT_LIT8)
hookUserWasInShortsABConfig(startIndex)
}
} else {
userWasInShortsABConfigFingerprint.methodOrThrow().apply {
val startIndex = indexOfOptionalInstruction(this)
hookUserWasInShortsABConfig(startIndex)
}
}
if (is_20_02_or_greater) {
userWasInShortsAlternativeFingerprint.matchOrThrow().let {

View File

@ -10,7 +10,6 @@ import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val fullScreenEngagementOverlayFingerprint = legacyFingerprint(
@ -114,17 +113,11 @@ internal val playerGestureConfigSyntheticFingerprint = legacyFingerprint(
// This method is always called "a" because this kind of class always has a single method.
method.name == "a" &&
classDef.methods.count() == 2 &&
indexOfPlayerConfigModelBooleanInstruction(method) >= 0
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
reference.parameterTypes.isEmpty() &&
reference.returnType == "Z"
} >= 0
},
)
internal fun indexOfPlayerConfigModelBooleanInstruction(
method: Method,
startIndex: Int = 0
) = method.indexOfFirstInstruction(startIndex) {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
reference.parameterTypes.isEmpty() &&
reference.returnType == "Z"
}
)

View File

@ -30,6 +30,7 @@ import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference
@ -211,17 +212,14 @@ val swipeControlsPatch = bytecodePatch(
// region patch for disable swipe to enter fullscreen mode (in the player) and disable swipe to exit fullscreen mode
playerGestureConfigSyntheticFingerprint.methodOrThrow().apply {
val disableSwipeToExitFullscreenModeIndex =
indexOfPlayerConfigModelBooleanInstruction(this)
val disableSwipeToEnterFullscreenModeInThePlayerIndex =
indexOfPlayerConfigModelBooleanInstruction(this, disableSwipeToExitFullscreenModeIndex + 1)
playerGestureConfigSyntheticFingerprint.matchOrThrow().let {
val endIndex = it.patternMatch!!.endIndex
mapOf(
disableSwipeToExitFullscreenModeIndex to "disableSwipeToExitFullscreenMode",
disableSwipeToEnterFullscreenModeInThePlayerIndex to "disableSwipeToEnterFullscreenModeInThePlayer"
).forEach { (walkerIndex, methodName) ->
getWalkerMethod(walkerIndex).apply {
3 to "disableSwipeToEnterFullscreenModeInThePlayer",
9 to "disableSwipeToExitFullscreenMode"
).forEach { (offSet, methodName) ->
it.getWalkerMethod(endIndex - offSet).apply {
val index = implementation!!.instructions.lastIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA

View File

@ -1,34 +0,0 @@
package app.revanced.patches.youtube.utils.auth
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.request.buildRequestPatch
import app.revanced.patches.youtube.utils.request.hookBuildRequest
import app.revanced.util.fingerprint.methodOrThrow
private const val EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/AuthUtils;"
val authHookPatch = bytecodePatch(
description = "authHookPatch"
) {
dependsOn(
sharedExtensionPatch,
buildRequestPatch,
)
execute {
// Get incognito status and data sync id.
accountIdentityFingerprint.methodOrThrow().addInstructions(
1, """
sput-object p3, $EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String;
sput-boolean p4, $EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->isIncognito:Z
"""
)
// Get the header to use the auth token.
hookBuildRequest("$EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->setRequestHeaders(Ljava/lang/String;Ljava/util/Map;)V")
}
}

View File

@ -1,14 +0,0 @@
package app.revanced.patches.youtube.utils.auth
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
internal val accountIdentityFingerprint = legacyFingerprint(
name = "accountIdentityFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { method, _ ->
method.definingClass.endsWith("${'$'}AutoValue_AccountIdentity;")
}
)

View File

@ -13,7 +13,8 @@ internal object Constants {
"19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
"19.43.41", // This is the latest version where edge-to-edge display is not enforced on Android 15+.
"19.44.39", // This is the only version that has experimental shortcut icons.
"19.47.53", // This is the latest version supported by the RVX patch.
"19.47.53", // This was the latest version supported by the previous RVX patch.
"20.03.43", // This is the latest version supported by the RVX patch.
)
)
}

View File

@ -1,111 +0,0 @@
package app.revanced.patches.youtube.utils.dismiss
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.util.addStaticFieldToExtension
import app.revanced.util.findMethodOrThrow
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.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/VideoUtils;"
private lateinit var dismissMethod: MutableMethod
val dismissPlayerHookPatch = bytecodePatch(
description = "dismissPlayerHookPatch"
) {
dependsOn(sharedExtensionPatch)
execute {
dismissPlayerOnClickListenerFingerprint.methodOrThrow().apply {
val literalIndex =
indexOfFirstLiteralInstructionOrThrow(DISMISS_PLAYER_LITERAL)
val dismissPlayerIndex = indexOfFirstInstructionOrThrow(literalIndex) {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.returnType == "V" &&
reference.parameterTypes.isEmpty()
}
getWalkerMethod(dismissPlayerIndex).apply {
val jumpIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.returnType == "V"
}
getWalkerMethod(jumpIndex).apply {
val jumpIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.returnType == "V"
}
dismissMethod = getWalkerMethod(jumpIndex)
}
}
val dismissPlayerReference =
getInstruction<ReferenceInstruction>(dismissPlayerIndex).reference as MethodReference
val dismissPlayerClass = dismissPlayerReference.definingClass
val fieldIndex =
indexOfFirstInstructionReversedOrThrow(dismissPlayerIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == dismissPlayerClass
}
val fieldReference =
getInstruction<ReferenceInstruction>(fieldIndex).reference as FieldReference
findMethodOrThrow(fieldReference.definingClass).apply {
val insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>() == fieldReference
}
val insertRegister =
getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstruction(
insertIndex,
"sput-object v$insertRegister, $EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR->dismissPlayerClass:$dismissPlayerClass"
)
val smaliInstructions =
"""
if-eqz v0, :ignore
invoke-virtual {v0}, $dismissPlayerReference
:ignore
return-void
"""
addStaticFieldToExtension(
EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR,
"dismissPlayer",
"dismissPlayerClass",
dismissPlayerClass,
smaliInstructions,
false
)
}
}
}
}
/**
* This method is called when the video is closed.
*/
internal fun hookDismissObserver(descriptor: String) =
dismissMethod.addInstruction(
0,
"invoke-static {}, $descriptor"
)

View File

@ -1,17 +0,0 @@
package app.revanced.patches.youtube.utils.dismiss
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
internal const val DISMISS_PLAYER_LITERAL = 34699L
internal val dismissPlayerOnClickListenerFingerprint = legacyFingerprint(
name = "dismissPlayerOnClickListenerFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
literals = listOf(DISMISS_PLAYER_LITERAL),
customFingerprint = { method, _ ->
method.name == "onClick"
}
)

View File

@ -1,9 +1,17 @@
package app.revanced.patches.youtube.utils.extension
import app.revanced.patches.shared.extension.hooks.cronetEngineContextHook
import app.revanced.patches.shared.extension.hooks.firebaseInitProviderContextHook
import app.revanced.patches.shared.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.extension.hooks.applicationInitHook
import app.revanced.patches.youtube.utils.extension.hooks.mainActivityBaseContextHook
import app.revanced.patches.youtube.utils.extension.hooks.urlActivityBaseContextHook
// TODO: Move this to a "Hook.kt" file. Same for other extension hook patches.
val sharedExtensionPatch = sharedExtensionPatch(
applicationInitHook,
cronetEngineContextHook,
firebaseInitProviderContextHook,
mainActivityBaseContextHook,
urlActivityBaseContextHook,
)

View File

@ -0,0 +1,36 @@
package app.revanced.patches.youtube.utils.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var attachBaseContextIndex = -1
internal val mainActivityBaseContextHook = extensionHook(
insertIndexResolver = { method ->
attachBaseContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "attachBaseContext"
}
attachBaseContextIndex + 1
},
contextRegisterResolver = { method ->
val overrideInstruction =
method.implementation!!.instructions.elementAt(attachBaseContextIndex)
as FiveRegisterInstruction
"v${overrideInstruction.registerD}"
},
) {
returns("V")
parameters("Landroid/content/Context;")
custom { method, classDef ->
method.name == "attachBaseContext" &&
(
classDef.endsWith("/MainActivity;") ||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
classDef.endsWith("/WatchWhileActivity;")
)
}
}

View File

@ -0,0 +1,32 @@
package app.revanced.patches.youtube.utils.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var attachBaseContextIndex = -1
internal val urlActivityBaseContextHook = extensionHook(
insertIndexResolver = { method ->
attachBaseContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "attachBaseContext"
}
attachBaseContextIndex + 1
},
contextRegisterResolver = { method ->
val overrideInstruction =
method.implementation!!.instructions.elementAt(attachBaseContextIndex)
as FiveRegisterInstruction
"v${overrideInstruction.registerD}"
},
) {
returns("V")
parameters("Landroid/content/Context;")
custom { method, classDef ->
classDef.endsWith("/Shell_UrlActivity;") &&
method.name == "attachBaseContext"
}
}

View File

@ -116,7 +116,7 @@ val cairoFragmentPatch = resourcePatch(
?.let { node ->
node.insertNode("Preference", node) {
for (index in 0 until node.attributes.length) {
with(node.attributes.item(index)) {
with (node.attributes.item(index)) {
setAttribute(nodeName, nodeValue)
}
}

View File

@ -1,10 +1,10 @@
package app.revanced.patches.youtube.utils.fix.cairo
import app.revanced.patches.youtube.utils.resourceid.settingsFragment
import app.revanced.patches.youtube.utils.resourceid.settingsFragmentCairo
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
/**

View File

@ -1,9 +1,12 @@
package app.revanced.patches.youtube.utils.fix.splash
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.utils.playservice.is_19_32_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.restoreOldSplashAnimationIncluded
import app.revanced.patches.youtube.utils.settings.ResourceUtils.youtubePackageName
import app.revanced.util.findElementByAttributeValueOrThrow
import org.w3c.dom.Element
/**
@ -25,6 +28,18 @@ val darkModeSplashScreenPatch = resourcePatch(
return@finalize
}
// GmsCore support included
if (youtubePackageName != YOUTUBE_PACKAGE_NAME) {
document("AndroidManifest.xml").use { document ->
val mainActivityElement = document.childNodes.findElementByAttributeValueOrThrow(
"android:name",
"com.google.android.apps.youtube.app.watchwhile.MainActivity",
)
mainActivityElement.setAttribute("android:launchMode", "singleTask")
}
}
if (restoreOldSplashAnimationIncluded) {
document("res/values-night/styles.xml").use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element

View File

@ -153,18 +153,3 @@ internal val onesieEncryptionAlternativeFeatureFlagFingerprint = legacyFingerpri
name = "onesieEncryptionAlternativeFeatureFlagFingerprint",
literals = listOf(ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG),
)
// Feature flag that enables different code for parsing and starting video playback,
// but it's exact purpose is not known. If this flag is enabled while stream spoofing
// then videos will never start playback and load forever.
// Flag does not seem to affect playback if spoofing is off.
// YouTube 19.50 ~
internal const val PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG = 45665455L
internal val playbackStartDescriptorFeatureFlagFingerprint = legacyFingerprint(
name = "playbackStartDescriptorFeatureFlagFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
returnType = ("Z"),
literals = listOf(PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG)
)

View File

@ -20,7 +20,6 @@ import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PAC
import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_50_or_greater
import app.revanced.patches.youtube.utils.playservice.is_20_10_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.request.buildRequestPatch
@ -346,18 +345,11 @@ val spoofStreamingDataPatch = bytecodePatch(
"$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z"
)
if (is_19_50_or_greater) {
playbackStartDescriptorFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z"
if (is_20_10_or_greater) {
onesieEncryptionAlternativeFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z"
)
if (is_20_10_or_greater) {
onesieEncryptionAlternativeFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z"
)
}
}
settingArray += "SETTINGS: SKIP_RESPONSE_ENCRYPTION"

View File

@ -22,6 +22,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import kotlin.collections.mutableListOf
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/VideoUtils;"
@ -58,10 +59,7 @@ val fullscreenButtonHookPatch = bytecodePatch(
getReference<MethodReference>()?.name == "addListener"
}
val animatorListenerAdapterClass = getInstruction<ReferenceInstruction>(
indexOfFirstInstructionReversedOrThrow(
addListenerIndex,
Opcode.NEW_INSTANCE
)
indexOfFirstInstructionReversedOrThrow(addListenerIndex, Opcode.NEW_INSTANCE)
).reference.toString()
return Pair(
findMethodOrThrow(animatorListenerAdapterClass) { parameters.isEmpty() },

Some files were not shown because too many files have changed in this diff Show More