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 | | 💊 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 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `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 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 ~ 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 ~ 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 ~ 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 ~ 20.03.43 |
| `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 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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 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 ~ 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 ~ 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 ~ 20.03.43 |
| `Disable splash animation` | Adds an option to disable the splash animation on app startup. | 19.05.36 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `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 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 ~ 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 ~ 20.03.43 |
| `Hide action buttons` | Adds options to hide action buttons under videos. | 19.05.36 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 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 ~ 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 ~ 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 ~ 20.03.43 |
| `MaterialYou` | Applies the MaterialYou theme for Android 12+ devices. | 19.05.36 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 19.05.36 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `Overlay buttons` | Adds options to display useful overlay buttons in the video player. | 19.05.36 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 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 ~ 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 ~ 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 ~ 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 ~ 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 ~ 20.03.43 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 19.05.36 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 19.05.36 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `Theme` | Changes the app's themes to the values specified in patch options. | 19.05.36 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `Translations for YouTube` | Add translations or remove string resources. | 19.05.36 ~ 19.47.53 | | `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 ~ 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 ~ 20.03.43 |
| `Visual preferences icons for YouTube` | Adds icons to specific preferences in the settings. | 19.05.36 ~ 19.47.53 | | `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 ~ 19.47.53 | | `Watch history` | Adds an option to change the domain of the watch history or check its status. | 19.05.36 ~ 20.03.43 |
</details> </details>
### [📦 `com.google.android.apps.youtube.music`](https://play.google.com/store/apps/details?id=com.google.android.apps.youtube.music) ### [📦 `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 | | 💊 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 | | `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.1 | | `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.1 | | `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.1 | | `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.1 | | `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.1 | | `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.1 | | `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.1 | | `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.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.0 |
| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.1 | | `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.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.0 |
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 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.0 |
| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 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.0 |
</details> </details>
@ -169,7 +169,8 @@ Example:
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -200,7 +201,7 @@ Example:
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []

View File

@ -2,10 +2,7 @@ package app.revanced.extension.reddit.patches;
import static app.revanced.extension.shared.utils.StringRef.str; import static app.revanced.extension.shared.utils.StringRef.str;
import android.app.Dialog;
import android.view.View; import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -37,35 +34,6 @@ public class RemoveSubRedditDialogPatch {
return Settings.REMOVE_NSFW_DIALOG.get() || hasBeenVisited; 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) { public static boolean spoofLoggedInStatus(boolean isLoggedIn) {
return !Settings.REMOVE_NOTIFICATION_DIALOG.get() && isLoggedIn; return !Settings.REMOVE_NOTIFICATION_DIALOG.get() && isLoggedIn;
} }

View File

@ -5,7 +5,7 @@ import app.revanced.extension.reddit.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ScreenshotPopupPatch { public class ScreenshotPopupPatch {
public static Boolean disableScreenshotPopup(Boolean original) { public static boolean disableScreenshotPopup() {
return Settings.DISABLE_SCREENSHOT_POPUP.get() ? Boolean.FALSE : original; 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); public static final BooleanSetting HIDE_NEW_POST_ADS = new BooleanSetting("revanced_hide_new_post_ads", TRUE, true);
// Layout // 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_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_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); 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, route: CompiledRoute,
clientType: YouTubeAppClient.ClientType, clientType: YouTubeAppClient.ClientType,
requestHeader: Map<String, String>? = null, requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
) = getInnerTubeResponseConnectionFromRoute( ) = getInnerTubeResponseConnectionFromRoute(
@ -289,7 +288,6 @@ object InnerTubeRequestBody {
clientVersion = clientType.clientVersion, clientVersion = clientType.clientVersion,
supportsCookies = clientType.supportsCookies, supportsCookies = clientType.supportsCookies,
requestHeader = requestHeader, requestHeader = requestHeader,
dataSyncId = dataSyncId,
connectTimeout = connectTimeout, connectTimeout = connectTimeout,
readTimeout = readTimeout, readTimeout = readTimeout,
) )
@ -299,7 +297,6 @@ object InnerTubeRequestBody {
route: CompiledRoute, route: CompiledRoute,
clientType: YouTubeWebClient.ClientType, clientType: YouTubeWebClient.ClientType,
requestHeader: Map<String, String>? = null, requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
) = getInnerTubeResponseConnectionFromRoute( ) = getInnerTubeResponseConnectionFromRoute(
@ -308,7 +305,6 @@ object InnerTubeRequestBody {
clientId = clientType.id.toString(), clientId = clientType.id.toString(),
clientVersion = clientType.clientVersion, clientVersion = clientType.clientVersion,
requestHeader = requestHeader, requestHeader = requestHeader,
dataSyncId = dataSyncId,
connectTimeout = connectTimeout, connectTimeout = connectTimeout,
readTimeout = readTimeout, readTimeout = readTimeout,
) )
@ -321,7 +317,6 @@ object InnerTubeRequestBody {
clientVersion: String, clientVersion: String,
supportsCookies: Boolean = true, supportsCookies: Boolean = true,
requestHeader: Map<String, String>? = null, requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
): HttpURLConnection { ): 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 return connection
} }

View File

@ -69,21 +69,11 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
* Skip response encryption in OnesiePlayerRequest. * Skip response encryption in OnesiePlayerRequest.
*/ */
public static boolean skipResponseEncryption(boolean original) { public static boolean skipResponseEncryption(boolean original) {
if (!SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION) { if (SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION) {
return original; return false;
} }
return false;
}
/** return original;
* 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;
} }
/** /**

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"), null, true)
handleConnectionError( handleConnectionError(str("revanced_spoof_streaming_data_failed_forbidden_suggestion"), null, true)
str("revanced_spoof_streaming_data_failed_forbidden_suggestion"),
null,
true
)
return null return null
} }
} }

View File

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

View File

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

View File

@ -155,34 +155,6 @@ public final class CustomActionsPatch {
Logger.printInfo(() -> customAction.name() + bottomSheetMenuClass + bottomSheetMenuList + bottomSheetMenuObject); 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. * Injection point.
*/ */
@ -207,9 +179,8 @@ public final class CustomActionsPatch {
if (recyclerView.getChildAt(childCount - i - 1) instanceof ViewGroup parentViewGroup) { if (recyclerView.getChildAt(childCount - i - 1) instanceof ViewGroup parentViewGroup) {
childCount = recyclerView.getChildCount(); childCount = recyclerView.getChildCount();
if (childCount > 3 && parentViewGroup.getChildAt(1) instanceof TextView textView) { if (childCount > 3 && parentViewGroup.getChildAt(1) instanceof TextView textView) {
String menuTitle = textView.getText().toString();
for (CustomAction customAction : CustomAction.values()) { for (CustomAction customAction : CustomAction.values()) {
if (customAction.getLabel().equals(menuTitle)) { if (customAction.getLabel().equals(textView.getText().toString())) {
View.OnClickListener onClick = customAction.getOnClickListener(); View.OnClickListener onClick = customAction.getOnClickListener();
View.OnLongClickListener onLongClick = customAction.getOnLongClickListener(); View.OnLongClickListener onLongClick = customAction.getOnLongClickListener();
recyclerViewRef = new WeakReference<>(recyclerView); recyclerViewRef = new WeakReference<>(recyclerView);

View File

@ -9,11 +9,6 @@ public class PatchStatus {
return false; return false;
} }
// Modified by a patch. Do not touch.
public static boolean OldSeekbarThumbnailsDefaultBoolean() {
return false;
}
public static boolean OldSplashAnimation() { public static boolean OldSplashAnimation() {
// Replace this with true if the Restore old splash animation (Custom branding icon) succeeds // Replace this with true if the Restore old splash animation (Custom branding icon) succeeds
return false; return false;
@ -45,22 +40,23 @@ public class PatchStatus {
return false; return false;
} }
public static String SpoofAppVersionDefaultString() {
return "18.17.43";
}
public static boolean ToolBarComponents() { public static boolean ToolBarComponents() {
// Replace this with true if the Toolbar components patch succeeds // Replace this with true if the Toolbar components patch succeeds
return false; return false;
} }
public static long PatchedTime() {
return 0L;
}
public static String SpoofAppVersionDefaultString() {
return "18.17.43";
}
// Modified by a patch. Do not touch. // Modified by a patch. Do not touch.
public static String RVXMusicPackageName() { public static String RVXMusicPackageName() {
return "com.google.android.apps.youtube.music"; 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; 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.content.Context;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.widget.LinearLayout; 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.GetPlaylistsRequest;
import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest; import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest;
import app.revanced.extension.youtube.settings.Settings; 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.ExtendedUtils;
import app.revanced.extension.youtube.utils.VideoUtils;
import kotlin.Pair; import kotlin.Pair;
// TODO: Implement sync queue and clean up code. // TODO: Implement sync queue and clean up code.
@SuppressWarnings({"unused", "StaticFieldLeak"}) @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 = private static final boolean QUEUE_MANAGER =
Settings.OVERLAY_BUTTON_EXTERNAL_DOWNLOADER_QUEUE_MANAGER.get() Settings.OVERLAY_BUTTON_EXTERNAL_DOWNLOADER_QUEUE_MANAGER.get()
|| Settings.OVERRIDE_VIDEO_DOWNLOAD_BUTTON_QUEUE_MANAGER.get(); || Settings.OVERRIDE_VIDEO_DOWNLOAD_BUTTON_QUEUE_MANAGER.get();
private static Context mContext; 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 final String checkFailedAuth =
private static String checkFailedPlaylistId; ResourceUtils.getString("revanced_queue_manager_check_failed_auth");
private static String checkFailedQueue; private static final String checkFailedPlaylistId =
private static String checkFailedVideoId; ResourceUtils.getString("revanced_queue_manager_check_failed_playlist_id");
private static String checkFailedGeneric; 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 final String fetchFailedAdd =
private static String fetchFailedCreate; ResourceUtils.getString("revanced_queue_manager_fetch_failed_add");
private static String fetchFailedDelete; private static final String fetchFailedCreate =
private static String fetchFailedRemove; ResourceUtils.getString("revanced_queue_manager_fetch_failed_create");
private static String fetchFailedSave; 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 final String fetchSucceededAdd =
private static String fetchSucceededCreate; ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_add");
private static String fetchSucceededDelete; private static final String fetchSucceededCreate =
private static String fetchSucceededRemove; ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_create");
private static String fetchSucceededSave; private static final String fetchSucceededDelete =
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_delete");
static { private static final String fetchSucceededRemove =
Context mContext = Utils.getContext(); ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_remove");
if (mContext != null && mContext.getResources() != null) { private static final String fetchSucceededSave =
checkFailedAuth = str("revanced_queue_manager_check_failed_auth"); ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_save");
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");
}
}
@GuardedBy("itself") @GuardedBy("itself")
private static final BidiMap<String, String> lastVideoIds = new DualHashBidiMap<>(); private static final BidiMap<String, String> lastVideoIds = new DualHashBidiMap<>();
@ -118,12 +113,20 @@ public class PlaylistPatch extends AuthUtils {
if (videoId != null) { if (videoId != null) {
lastVideoIds.remove(videoId, setVideoId); lastVideoIds.remove(videoId, setVideoId);
EditPlaylistRequest.clearVideoId(videoId); 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. * 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. * Invoked by extension.
*/ */
@ -144,7 +171,7 @@ public class PlaylistPatch extends AuthUtils {
* Invoked by extension. * Invoked by extension.
*/ */
public static void prepareDialogBuilder(@NonNull String currentVideoId) { public static void prepareDialogBuilder(@NonNull String currentVideoId) {
if (authorization.isEmpty() || (dataSyncId.isEmpty() && isIncognito)) { if (authorization.isEmpty() || isIncognito) {
handleCheckError(checkFailedAuth); handleCheckError(checkFailedAuth);
return; return;
} }
@ -153,22 +180,9 @@ public class PlaylistPatch extends AuthUtils {
} else { } else {
videoId = currentVideoId; videoId = currentVideoId;
synchronized (lastVideoIds) { synchronized (lastVideoIds) {
QueueManager[] customActionsEntries; QueueManager[] customActionsEntries = playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null
boolean canReload = PlayerType.getCurrent().isMaximizedOrFullscreen() && ? QueueManager.addToQueueEntries
lastVideoIds.get(VideoInformation.getVideoId()) != null; : QueueManager.removeFromQueueEntries;
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;
}
}
buildBottomSheetDialog(customActionsEntries); buildBottomSheetDialog(customActionsEntries);
} }
@ -197,14 +211,13 @@ public class PlaylistPatch extends AuthUtils {
ExtendedUtils.showBottomSheetDialog(mContext, mScrollView, actionsMap); ExtendedUtils.showBottomSheetDialog(mContext, mScrollView, actionsMap);
} }
private static void fetchQueue(boolean remove, boolean openPlaylist, private static void fetchQueue(boolean remove, boolean openPlaylist, boolean openVideo) {
boolean openVideo, boolean reload) {
try { try {
String currentPlaylistId = playlistId; String currentPlaylistId = playlistId;
String currentVideoId = videoId; String currentVideoId = videoId;
synchronized (lastVideoIds) { synchronized (lastVideoIds) {
if (currentPlaylistId.isEmpty()) { // Queue is empty, create new playlist. if (currentPlaylistId.isEmpty()) { // Queue is empty, create new playlist.
CreatePlaylistRequest.fetchRequestIfNeeded(currentVideoId, requestHeader, dataSyncId); CreatePlaylistRequest.fetchRequestIfNeeded(currentVideoId, requestHeader);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
CreatePlaylistRequest request = CreatePlaylistRequest.getRequestForVideoId(currentVideoId); CreatePlaylistRequest request = CreatePlaylistRequest.getRequestForVideoId(currentVideoId);
if (request != null) { if (request != null) {
@ -218,7 +231,7 @@ public class PlaylistPatch extends AuthUtils {
showToast(fetchSucceededCreate); showToast(fetchSucceededCreate);
Logger.printDebug(() -> "Queue successfully created, playlistId: " + createdPlaylistId + ", setVideoId: " + setVideoId); Logger.printDebug(() -> "Queue successfully created, playlistId: " + createdPlaylistId + ", setVideoId: " + setVideoId);
if (openPlaylist) { if (openPlaylist) {
openQueue(currentVideoId, openVideo, reload); openQueue(currentVideoId, openVideo);
} }
return; return;
} }
@ -228,7 +241,7 @@ public class PlaylistPatch extends AuthUtils {
}, 1000); }, 1000);
} else { // Queue is not empty, add or remove video. } else { // Queue is not empty, add or remove video.
String setVideoId = lastVideoIds.get(currentVideoId); String setVideoId = lastVideoIds.get(currentVideoId);
EditPlaylistRequest.fetchRequestIfNeeded(currentVideoId, currentPlaylistId, setVideoId, requestHeader, dataSyncId); EditPlaylistRequest.fetchRequestIfNeeded(currentVideoId, currentPlaylistId, setVideoId, requestHeader);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
EditPlaylistRequest request = EditPlaylistRequest.getRequestForVideoId(currentVideoId); EditPlaylistRequest request = EditPlaylistRequest.getRequestForVideoId(currentVideoId);
@ -236,24 +249,22 @@ public class PlaylistPatch extends AuthUtils {
String fetchedSetVideoId = request.getResult(); String fetchedSetVideoId = request.getResult();
Logger.printDebug(() -> "fetchedSetVideoId: " + fetchedSetVideoId); Logger.printDebug(() -> "fetchedSetVideoId: " + fetchedSetVideoId);
if (remove) { // Remove from queue. if (remove) { // Remove from queue.
if ("".equals(fetchedSetVideoId)) { if (StringUtils.isEmpty(fetchedSetVideoId)) {
lastVideoIds.remove(currentVideoId, setVideoId); lastVideoIds.remove(currentVideoId, setVideoId);
EditPlaylistRequest.clearVideoId(currentVideoId);
showToast(fetchSucceededRemove); showToast(fetchSucceededRemove);
if (openPlaylist) { if (openPlaylist) {
openQueue(currentVideoId, openVideo, reload); openQueue(currentVideoId, openVideo);
} }
return; return;
} }
showToast(fetchFailedRemove); showToast(fetchFailedRemove);
} else { // Add to queue. } else { // Add to queue.
if (fetchedSetVideoId != null && !fetchedSetVideoId.isEmpty()) { if (StringUtils.isNotEmpty(fetchedSetVideoId)) {
lastVideoIds.putIfAbsent(currentVideoId, fetchedSetVideoId); lastVideoIds.putIfAbsent(currentVideoId, fetchedSetVideoId);
EditPlaylistRequest.clearVideoId(currentVideoId);
showToast(fetchSucceededAdd); showToast(fetchSucceededAdd);
Logger.printDebug(() -> "Video successfully added, setVideoId: " + fetchedSetVideoId); Logger.printDebug(() -> "Video successfully added, setVideoId: " + fetchedSetVideoId);
if (openPlaylist) { if (openPlaylist) {
openQueue(currentVideoId, openVideo, reload); openQueue(currentVideoId, openVideo);
} }
return; return;
} }
@ -275,7 +286,7 @@ public class PlaylistPatch extends AuthUtils {
return; return;
} }
try { try {
GetPlaylistsRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader, dataSyncId); GetPlaylistsRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
GetPlaylistsRequest request = GetPlaylistsRequest.getRequestForPlaylistId(currentPlaylistId); GetPlaylistsRequest request = GetPlaylistsRequest.getRequestForPlaylistId(currentPlaylistId);
if (request != null) { if (request != null) {
@ -317,7 +328,7 @@ public class PlaylistPatch extends AuthUtils {
handleCheckError(checkFailedPlaylistId); handleCheckError(checkFailedPlaylistId);
return; return;
} }
SavePlaylistRequest.fetchRequestIfNeeded(playlistId, libraryId, requestHeader, dataSyncId); SavePlaylistRequest.fetchRequestIfNeeded(playlistId, libraryId, requestHeader);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
SavePlaylistRequest request = SavePlaylistRequest.getRequestForLibraryId(libraryId); SavePlaylistRequest request = SavePlaylistRequest.getRequestForLibraryId(libraryId);
@ -343,7 +354,7 @@ public class PlaylistPatch extends AuthUtils {
return; return;
} }
try { try {
DeletePlaylistRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader, dataSyncId); DeletePlaylistRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
DeletePlaylistRequest request = DeletePlaylistRequest.getRequestForPlaylistId(currentPlaylistId); DeletePlaylistRequest request = DeletePlaylistRequest.getRequestForPlaylistId(currentPlaylistId);
if (request != null) { if (request != null) {
@ -375,10 +386,10 @@ public class PlaylistPatch extends AuthUtils {
} }
private static void openQueue() { 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; String currentPlaylistId = playlistId;
if (currentPlaylistId.isEmpty()) { if (currentPlaylistId.isEmpty()) {
handleCheckError(checkFailedQueue); handleCheckError(checkFailedQueue);
@ -390,15 +401,7 @@ public class PlaylistPatch extends AuthUtils {
return; return;
} }
// Open a video from a playlist // Open a video from a playlist
if (reload) { openPlaylist(currentPlaylistId, currentVideoId);
// 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);
}
} else { } else {
// Open a playlist // Open a playlist
openPlaylist(currentPlaylistId); openPlaylist(currentPlaylistId);
@ -417,37 +420,27 @@ public class PlaylistPatch extends AuthUtils {
ADD_TO_QUEUE( ADD_TO_QUEUE(
"revanced_queue_manager_add_to_queue", "revanced_queue_manager_add_to_queue",
"yt_outline_list_add_black_24", "yt_outline_list_add_black_24",
() -> fetchQueue(false, false, false, false) () -> fetchQueue(false, false, false)
), ),
ADD_TO_QUEUE_AND_OPEN_QUEUE( ADD_TO_QUEUE_AND_OPEN_QUEUE(
"revanced_queue_manager_add_to_queue_and_open_queue", "revanced_queue_manager_add_to_queue_and_open_queue",
"yt_outline_list_add_black_24", "yt_outline_list_add_black_24",
() -> fetchQueue(false, true, false, false) () -> fetchQueue(false, true, false)
), ),
ADD_TO_QUEUE_AND_PLAY_VIDEO( ADD_TO_QUEUE_AND_PLAY_VIDEO(
"revanced_queue_manager_add_to_queue_and_play_video", "revanced_queue_manager_add_to_queue_and_play_video",
"yt_outline_list_play_arrow_black_24", "yt_outline_list_play_arrow_black_24",
() -> fetchQueue(false, true, true, false) () -> fetchQueue(false, true, true)
),
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)
), ),
REMOVE_FROM_QUEUE( REMOVE_FROM_QUEUE(
"revanced_queue_manager_remove_from_queue", "revanced_queue_manager_remove_from_queue",
"yt_outline_trash_can_black_24", "yt_outline_trash_can_black_24",
() -> fetchQueue(true, false, false, false) () -> fetchQueue(true, false, false)
), ),
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE( REMOVE_FROM_QUEUE_AND_OPEN_QUEUE(
"revanced_queue_manager_remove_from_queue_and_open_queue", "revanced_queue_manager_remove_from_queue_and_open_queue",
"yt_outline_trash_can_black_24", "yt_outline_trash_can_black_24",
() -> fetchQueue(true, true, false, false) () -> fetchQueue(true, true, 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)
), ),
OPEN_QUEUE( OPEN_QUEUE(
"revanced_queue_manager_open_queue", "revanced_queue_manager_open_queue",
@ -495,17 +488,6 @@ public class PlaylistPatch extends AuthUtils {
SAVE_QUEUE, 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 = { public static final QueueManager[] removeFromQueueEntries = {
REMOVE_FROM_QUEUE, REMOVE_FROM_QUEUE,
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE, REMOVE_FROM_QUEUE_AND_OPEN_QUEUE,
@ -515,16 +497,6 @@ public class PlaylistPatch extends AuthUtils {
SAVE_QUEUE, 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 = { public static final QueueManager[] noVideoIdQueueEntries = {
OPEN_QUEUE, OPEN_QUEUE,
//REMOVE_QUEUE, //REMOVE_QUEUE,

View File

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

View File

@ -22,13 +22,11 @@ import java.util.concurrent.TimeoutException
class DeletePlaylistRequest private constructor( class DeletePlaylistRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<Boolean> = Utils.submitOnBackgroundThread { private val future: Future<Boolean> = Utils.submitOnBackgroundThread {
fetch( fetch(
playlistId, playlistId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -80,16 +78,14 @@ class DeletePlaylistRequest private constructor(
@JvmStatic @JvmStatic
fun fetchRequestIfNeeded( fun fetchRequestIfNeeded(
playlistId: String, playlistId: String,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
) { ) {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
synchronized(cache) { synchronized(cache) {
if (!cache.containsKey(playlistId)) { if (!cache.containsKey(playlistId)) {
cache[playlistId] = DeletePlaylistRequest( cache[playlistId] = DeletePlaylistRequest(
playlistId, playlistId,
requestHeader, requestHeader
dataSyncId,
) )
} }
} }
@ -108,8 +104,7 @@ class DeletePlaylistRequest private constructor(
private fun sendRequest( private fun sendRequest(
playlistId: String, playlistId: String,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
@ -123,8 +118,7 @@ class DeletePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
DELETE_PLAYLIST, DELETE_PLAYLIST,
clientType, clientType,
requestHeader, requestHeader
dataSyncId
) )
val requestBody = deletePlaylistRequestBody(playlistId) val requestBody = deletePlaylistRequestBody(playlistId)
@ -169,14 +163,9 @@ class DeletePlaylistRequest private constructor(
private fun fetch( private fun fetch(
playlistId: String, playlistId: String,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
): Boolean? { ): Boolean? {
val json = sendRequest( val json = sendRequest(playlistId, requestHeader)
playlistId,
requestHeader,
dataSyncId,
)
if (json != null) { if (json != null) {
return parseResponse(json) 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.requests.Requester
import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Logger
import app.revanced.extension.shared.utils.Utils import app.revanced.extension.shared.utils.Utils
import org.apache.commons.lang3.StringUtils
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.io.IOException import java.io.IOException
@ -24,7 +25,6 @@ class EditPlaylistRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val setVideoId: String?, private val setVideoId: String?,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<String> = Utils.submitOnBackgroundThread { private val future: Future<String> = Utils.submitOnBackgroundThread {
fetch( fetch(
@ -32,7 +32,6 @@ class EditPlaylistRequest private constructor(
playlistId, playlistId,
setVideoId, setVideoId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -93,8 +92,7 @@ class EditPlaylistRequest private constructor(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
setVideoId: String?, setVideoId: String?,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
) { ) {
Objects.requireNonNull(videoId) Objects.requireNonNull(videoId)
synchronized(cache) { synchronized(cache) {
@ -103,8 +101,7 @@ class EditPlaylistRequest private constructor(
videoId, videoId,
playlistId, playlistId,
setVideoId, setVideoId,
requestHeader, requestHeader
dataSyncId,
) )
} }
} }
@ -125,8 +122,7 @@ class EditPlaylistRequest private constructor(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
setVideoId: String?, setVideoId: String?,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(videoId) Objects.requireNonNull(videoId)
@ -140,8 +136,7 @@ class EditPlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
EDIT_PLAYLIST, EDIT_PLAYLIST,
clientType, clientType,
requestHeader, requestHeader
dataSyncId
) )
val requestBody = editPlaylistRequestBody( val requestBody = editPlaylistRequestBody(
@ -204,18 +199,11 @@ class EditPlaylistRequest private constructor(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
setVideoId: String?, setVideoId: String?,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
): String? { ): String? {
val json = sendRequest( val json = sendRequest(videoId, playlistId, setVideoId, requestHeader)
videoId,
playlistId,
setVideoId,
requestHeader,
dataSyncId,
)
if (json != null) { if (json != null) {
return parseResponse(json, setVideoId != null && setVideoId.isNotEmpty()) return parseResponse(json, StringUtils.isNotEmpty(setVideoId))
} }
return null return null

View File

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

View File

@ -23,14 +23,12 @@ class SavePlaylistRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val libraryId: String, private val libraryId: String,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<Boolean> = Utils.submitOnBackgroundThread { private val future: Future<Boolean> = Utils.submitOnBackgroundThread {
fetch( fetch(
playlistId, playlistId,
libraryId, libraryId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -83,16 +81,14 @@ class SavePlaylistRequest private constructor(
fun fetchRequestIfNeeded( fun fetchRequestIfNeeded(
playlistId: String, playlistId: String,
libraryId: String, libraryId: String,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
) { ) {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
synchronized(cache) { synchronized(cache) {
cache[libraryId] = SavePlaylistRequest( cache[libraryId] = SavePlaylistRequest(
playlistId, playlistId,
libraryId, libraryId,
requestHeader, requestHeader
dataSyncId,
) )
} }
} }
@ -111,8 +107,7 @@ class SavePlaylistRequest private constructor(
private fun sendRequest( private fun sendRequest(
playlistId: String, playlistId: String,
libraryId: String, libraryId: String,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
Objects.requireNonNull(libraryId) Objects.requireNonNull(libraryId)
@ -127,8 +122,7 @@ class SavePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
EDIT_PLAYLIST, EDIT_PLAYLIST,
clientType, clientType,
requestHeader, requestHeader
dataSyncId
) )
val requestBody = savePlaylistRequestBody(libraryId, playlistId) val requestBody = savePlaylistRequestBody(libraryId, playlistId)
@ -174,15 +168,9 @@ class SavePlaylistRequest private constructor(
private fun fetch( private fun fetch(
playlistId: String, playlistId: String,
libraryId: String, libraryId: String,
requestHeader: Map<String, String>, requestHeader: Map<String, String>
dataSyncId: String,
): Boolean? { ): Boolean? {
val json = sendRequest( val json = sendRequest(playlistId, libraryId, requestHeader)
playlistId,
libraryId,
requestHeader,
dataSyncId,
)
if (json != null) { if (json != null) {
return parseResponse(json) 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.shared.utils.StringRef.str;
import static app.revanced.extension.youtube.shared.RootView.isShortsActive; import static app.revanced.extension.youtube.shared.RootView.isShortsActive;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.apache.commons.lang3.BooleanUtils; 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.BooleanSetting;
import app.revanced.extension.shared.settings.FloatSetting; import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.utils.Logger; import app.revanced.extension.shared.utils.Logger;
@ -32,61 +28,48 @@ public class PlaybackSpeedPatch {
Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get(); Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get();
private static final long TOAST_DELAY_MILLISECONDS = 750; private static final long TOAST_DELAY_MILLISECONDS = 750;
private static long lastTimeSpeedChanged; 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 lastSelectedPlaybackSpeed = 1.0f;
private static float lastSelectedShortsPlaybackSpeed = 1.0f;
/** private static volatile String channelId = "";
* The last regular video id. private static volatile String videoId = "";
*/ private static boolean isLiveStream;
private static String videoId = "";
@GuardedBy("itself") private static volatile String channelIdShorts = "";
private static final Map<String, Float> ignoredPlaybackSpeedVideoIds = new LinkedHashMap<>() { private static volatile String videoIdShorts = "";
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 3; private static boolean isLiveStreamShorts;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
}
};
/** /**
* Injection point. * 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, public static void newVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
@NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle, @NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle,
final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) { final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) {
if (isShortsActive()) { 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) { Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId);
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);
}
}
}
} }
/** /**
@ -115,29 +98,32 @@ public class PlaybackSpeedPatch {
/** /**
* Injection point. * Injection point.
* This method is called every second for regular videos and Shorts.
*/ */
public static float getPlaybackSpeed(float playbackSpeed) { public static float getPlaybackSpeed(float playbackSpeed) {
boolean isShorts = isShortsActive(); 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(); 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. if (defaultPlaybackSpeed < 0) {
float finalPlaybackSpeed = isShorts ? lastSelectedShortsPlaybackSpeed : lastSelectedPlaybackSpeed; float finalPlaybackSpeed = isShorts ? playbackSpeed : lastSelectedPlaybackSpeed;
VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed); VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed);
Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed); Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed);
return finalPlaybackSpeed; return finalPlaybackSpeed;
} else { // Otherwise the default playback speed is used. } else {
synchronized (ignoredPlaybackSpeedVideoIds) { if (isShorts) {
if (isShorts) { VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed);
// 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;
}
} }
Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed); Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed);
return defaultPlaybackSpeed; return defaultPlaybackSpeed;
} }
@ -152,19 +138,6 @@ public class PlaybackSpeedPatch {
public static void userSelectedPlaybackSpeed(float playbackSpeed) { public static void userSelectedPlaybackSpeed(float playbackSpeed) {
try { try {
boolean isShorts = isShortsActive(); 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()) { if (PatchStatus.RememberPlaybackSpeed()) {
BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts
? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED ? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED
@ -205,23 +178,15 @@ public class PlaybackSpeedPatch {
} }
}, TOAST_DELAY_MILLISECONDS); }, TOAST_DELAY_MILLISECONDS);
} }
} else if (!isShorts){
lastSelectedPlaybackSpeed = playbackSpeed;
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex); Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex);
} }
} }
/** private static boolean isMusic() {
* Injection point.
*/
public static void onDismiss() {
synchronized (ignoredPlaybackSpeedVideoIds) {
ignoredPlaybackSpeedVideoIds.remove(videoId);
videoId = "";
}
}
private static boolean isMusic(String videoId) {
if (DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC && !videoId.isEmpty()) { if (DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC && !videoId.isEmpty()) {
try { try {
MusicRequest request = MusicRequest.getRequestForVideoId(videoId); MusicRequest request = MusicRequest.getRequestForVideoId(videoId);

View File

@ -115,7 +115,7 @@ public class ReturnYouTubeDislike {
private static final Rect middleSeparatorBounds; 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; public static final int leftSeparatorShapePaddingPixels;
private static final ShapeDrawable leftSeparatorShape; private static final ShapeDrawable leftSeparatorShape;
@ -131,7 +131,7 @@ public class ReturnYouTubeDislike {
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp); (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp);
middleSeparatorBounds = new Rect(0, 0, middleSeparatorSize, middleSeparatorSize); 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 = new ShapeDrawable(new RectShape());
leftSeparatorShape.setBounds(leftSeparatorBounds); 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 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 = 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 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 = 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 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 = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684"); 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 = 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 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 = 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 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 = 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 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 = 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 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 = 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 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 = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF"); 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 // SB Setting not exported
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false); 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 @Override
public void onDestroy() { public void onDestroy() {
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener); mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
Utils.resetLocalizedContext();
super.onDestroy(); super.onDestroy();
} }

View File

@ -6,8 +6,6 @@ import static app.revanced.extension.shared.utils.Utils.isSDKAbove;
import android.preference.Preference; import android.preference.Preference;
import android.preference.SwitchPreference; import android.preference.SwitchPreference;
import java.util.Date;
import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch; import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch;
import app.revanced.extension.youtube.patches.utils.PatchStatus; import app.revanced.extension.youtube.patches.utils.PatchStatus;
@ -46,7 +44,6 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment {
AmbientModePreferenceLinks(); AmbientModePreferenceLinks();
FullScreenPanelPreferenceLinks(); FullScreenPanelPreferenceLinks();
NavigationPreferenceLinks(); NavigationPreferenceLinks();
PatchInformationPreferenceLinks();
RYDPreferenceLinks(); RYDPreferenceLinks();
SeekBarPreferenceLinks(); SeekBarPreferenceLinks();
ShortsPreferenceLinks(); 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 * 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. * Not thread safe. All fields/methods must be accessed from the main thread.
*
*/ */
public class SponsorBlockUtils { public class SponsorBlockUtils {
private static final int LOCKED_COLOR = Color.parseColor("#FFC83D"); private static final int LOCKED_COLOR = Color.parseColor("#FFC83D");

View File

@ -1,36 +1,7 @@
package app.revanced.extension.youtube.sponsorblock.objects; package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.shared.utils.StringRef.sf; 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.*;
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 android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; 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), 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"), 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); 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 skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight"); private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");

View File

@ -145,10 +145,8 @@ class SwipeControlsOverlayLayout(
addView(feedbackTextView) addView(feedbackTextView)
// get icons scaled, assuming square icons // get icons scaled, assuming square icons
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt() val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
autoBrightnessIcon = autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight) manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
manualBrightnessIcon =
getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight) mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", 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). * Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
*/ */
private fun showFeedbackView( private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
value: String,
progress: Int,
max: Int,
icon: Drawable,
isBrightness: Boolean
) {
feedbackHideHandler.removeCallbacks(feedbackHideCallback) feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis) feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
val viewToShow = val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
viewToShow.apply { viewToShow.apply {
setProgress(progress, max, value, isBrightness) setProgress(progress, max, value, isBrightness)
this.icon = icon this.icon = icon
@ -250,13 +241,7 @@ class SwipeControlsOverlayLayout(
brightnessValue < 75 -> highBrightnessIcon brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon else -> fullBrightnessIcon
} }
showFeedbackView( showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
"$brightnessValue%",
brightnessValue,
100,
icon,
isBrightness = true
)
} else { } else {
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon) showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
} }
@ -289,12 +274,7 @@ abstract class AbstractProgressView(
) : View(context, attrs, defStyleAttr) { ) : View(context, attrs, defStyleAttr) {
// Combined paint creation function for both fill and stroke styles // Combined paint creation function for both fill and stroke styles
private fun createPaint( 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 {
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.style = style
this.color = color this.color = color
this.strokeCap = strokeCap this.strokeCap = strokeCap
@ -302,18 +282,13 @@ abstract class AbstractProgressView(
} }
// Initialize paints // Initialize paints
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL) val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
val progressPaint = createPaint( val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
overlayProgressColor,
style = Paint.Style.STROKE,
strokeCap = Paint.Cap.ROUND,
strokeWidth = 20f
)
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL) val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor color = overlayTextColor
textAlign = Paint.Align.CENTER textAlign = Paint.Align.CENTER
textSize = 40f // Can adjust based on need textSize = 40f // Can adjust based on need
} }
protected var progress = 0 protected var progress = 0
@ -362,11 +337,11 @@ class CircularProgressView(
init { init {
textPaint.textSize = 40f // Override default text size for circular view textPaint.textSize = 40f // Override default text size for circular view
progressPaint.strokeWidth = 20f progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND progressPaint.strokeCap = Paint.Cap.ROUND
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.STROKE progressPaint.style = Paint.Style.STROKE
fillBackgroundPaint.style = Paint.Style.STROKE fillBackgroundPaint.style = Paint.Style.STROKE
} }
@ -377,12 +352,7 @@ class CircularProgressView(
rectF.set(20f, 20f, size - 20f, size - 20f) rectF.set(20f, 20f, size - 20f, size - 20f)
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring. canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
canvas.drawCircle( canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
width / 2f,
height / 2f,
size / 3,
backgroundPaint
) // Draw the inner circle.
// Select the paint for drawing based on whether it's brightness or volume. // Select the paint for drawing based on whether it's brightness or volume.
val sweepAngle = (progress.toFloat() / maxProgress) * 360 val sweepAngle = (progress.toFloat() / maxProgress) * 360
@ -429,13 +399,13 @@ class HorizontalProgressView(
) { ) {
private val iconSize = 60f private val iconSize = 60f
private val padding = 40f private val padding = 40f
init { 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.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL fillBackgroundPaint.style = Paint.Style.FILL
} }
@ -458,15 +428,7 @@ class HorizontalProgressView(
if (!overlayShowOverlayMinimalStyle) { if (!overlayShowOverlayMinimalStyle) {
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint) canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
} else { } else {
canvas.drawRoundRect( canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
minimalStartX,
0f,
minimalStartX + minimalElementWidth,
height,
cornerRadius,
cornerRadius,
backgroundPaint
)
} }
if (!overlayShowOverlayMinimalStyle) { if (!overlayShowOverlayMinimalStyle) {
@ -504,12 +466,7 @@ class HorizontalProgressView(
padding + minimalStartX padding + minimalStartX
} }
val iconY = height / 2 - iconSize / 2 val iconY = height / 2 - iconSize / 2
it.setBounds( it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
iconX.toInt(),
iconY.toInt(),
(iconX + iconSize).toInt(),
(iconY + iconSize).toInt()
)
it.draw(canvas) 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; import app.revanced.extension.youtube.settings.Settings;
public class ExtendedUtils extends PackageUtils { public class ExtendedUtils extends PackageUtils {
private static boolean isVersionOrGreater(String version) {
return getAppVersionName().compareTo(version) >= 0;
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static final boolean IS_19_17_OR_GREATER = isVersionOrGreater("19.17.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 = isVersionOrGreater("19.20.00"); public static final boolean IS_19_20_OR_GREATER = getAppVersionName().compareTo("19.20.00") >= 0;
public static final boolean IS_19_21_OR_GREATER = isVersionOrGreater("19.21.00"); public static final boolean IS_19_21_OR_GREATER = getAppVersionName().compareTo("19.21.00") >= 0;
public static final boolean IS_19_26_OR_GREATER = isVersionOrGreater("19.26.00"); public static final boolean IS_19_26_OR_GREATER = getAppVersionName().compareTo("19.26.00") >= 0;
public static final boolean IS_19_28_OR_GREATER = isVersionOrGreater("19.28.00"); public static final boolean IS_19_28_OR_GREATER = getAppVersionName().compareTo("19.28.00") >= 0;
public static final boolean IS_19_29_OR_GREATER = isVersionOrGreater("19.29.00"); public static final boolean IS_19_29_OR_GREATER = getAppVersionName().compareTo("19.29.00") >= 0;
public static final boolean IS_19_34_OR_GREATER = isVersionOrGreater("19.34.00"); public static final boolean IS_19_34_OR_GREATER = getAppVersionName().compareTo("19.34.00") >= 0;
public static final boolean IS_20_09_OR_GREATER = isVersionOrGreater("20.09.00"); 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) { public static int validateValue(IntegerSetting settings, int min, int max, String message) {
int value = settings.get(); 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) { 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(); final StringBuilder sb = new StringBuilder();
if (videoId.isEmpty()) { if (videoId.isEmpty()) {
sb.append(getPlaylistUrl(playlistId)); sb.append(getPlaylistUrl(playlistId));
} else { } else {
sb.append(VIDEO_URL); sb.append(getVideoScheme(videoId, false));
sb.append(videoId); sb.append("&list=");
sb.append("?list=");
sb.append(playlistId); sb.append(playlistId);
if (withTimestamp) {
final long currentVideoTimeInSeconds = VideoInformation.getVideoTimeInSeconds();
if (currentVideoTimeInSeconds > 0) {
sb.append("&t=");
sb.append(currentVideoTimeInSeconds);
}
}
} }
launchView(sb.toString(), getContext().getPackageName()); launchView(sb.toString(), getContext().getPackageName());
} }
@ -281,13 +269,6 @@ public class VideoUtils extends IntentUtils {
return !isExternalDownloaderLaunched.get() && original; 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. * Rest of the implementation added by patch.
*/ */

View File

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

View File

@ -4,5 +4,5 @@ org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
kotlin.jvm.target.validation.mode = IGNORE 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.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -34,7 +35,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -73,7 +75,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -114,7 +117,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -153,7 +157,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -174,7 +179,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -188,7 +194,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [ "options": [
@ -220,7 +226,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -263,7 +270,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -303,7 +311,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -343,7 +352,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -379,7 +389,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -485,7 +496,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [ "options": [
@ -516,7 +527,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -599,7 +611,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -627,7 +640,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -746,7 +760,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -829,7 +844,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -877,7 +893,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -918,7 +935,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -936,7 +954,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -954,7 +973,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -995,7 +1015,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1014,7 +1035,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1024,13 +1046,14 @@
"description": "Adds an option to disable the popup that appears when taking a screenshot.", "description": "Adds an option to disable the popup that appears when taking a screenshot.",
"use": true, "use": true,
"dependencies": [ "dependencies": [
"Settings for Reddit" "Settings for Reddit",
"ResourcePatch"
], ],
"compatiblePackages": { "compatiblePackages": {
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -1049,7 +1072,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1090,7 +1114,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1129,7 +1154,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1147,7 +1173,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1215,7 +1242,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1240,7 +1268,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1309,15 +1338,6 @@
"Clone": "com.rvx.android.apps.youtube.music", "Clone": "com.rvx.android.apps.youtube.music",
"Default": "app.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.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -1382,15 +1403,6 @@
"Clone": "com.rvx.android.apps.youtube.music", "Clone": "com.rvx.android.apps.youtube.music",
"Default": "app.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": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -1423,7 +1435,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1441,7 +1454,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1510,7 +1524,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1554,7 +1569,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -1577,7 +1592,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1598,7 +1614,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1623,7 +1640,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1642,7 +1660,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1690,7 +1709,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1706,7 +1726,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -1750,7 +1770,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1772,7 +1793,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1807,7 +1829,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -1826,7 +1848,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -1881,7 +1904,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1902,7 +1926,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1921,7 +1946,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1941,7 +1967,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -1988,7 +2015,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2005,7 +2033,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -2022,7 +2050,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -2041,7 +2069,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2065,7 +2094,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -2164,7 +2194,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2178,7 +2209,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -2219,7 +2250,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2229,14 +2261,13 @@
"description": "Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically.", "description": "Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically.",
"use": true, "use": true,
"dependencies": [ "dependencies": [
"Settings for Reddit", "Settings for Reddit"
"ResourcePatch"
], ],
"compatiblePackages": { "compatiblePackages": {
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -2277,7 +2308,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2342,7 +2374,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2384,7 +2417,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2422,7 +2456,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [] "options": []
@ -2441,7 +2475,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2465,7 +2500,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2482,7 +2518,7 @@
"com.reddit.frontpage": [ "com.reddit.frontpage": [
"2024.17.0", "2024.17.0",
"2025.05.1", "2025.05.1",
"2025.12.1" "2025.12.0"
] ]
}, },
"options": [ "options": [
@ -2517,7 +2553,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -2527,7 +2564,7 @@
"description": "The settings menu name that the RVX settings menu should be above.", "description": "The settings menu name that the RVX settings menu should be above.",
"required": true, "required": true,
"type": "kotlin.String", "type": "kotlin.String",
"default": "@string/parent_tools_key", "default": "@string/about_key",
"values": { "values": {
"Parent settings": "@string/parent_tools_key", "Parent settings": "@string/parent_tools_key",
"General": "@string/general_key", "General": "@string/general_key",
@ -2628,7 +2665,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2647,7 +2685,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -2759,7 +2798,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -2820,7 +2860,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2865,7 +2906,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -2898,7 +2940,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2917,7 +2960,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -2977,7 +3021,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -2995,7 +3040,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -3114,7 +3160,6 @@
"BytecodePatch", "BytecodePatch",
"BytecodePatch", "BytecodePatch",
"BytecodePatch", "BytecodePatch",
"BytecodePatch",
"ResourcePatch" "ResourcePatch"
], ],
"compatiblePackages": { "compatiblePackages": {
@ -3123,7 +3168,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "options": []
@ -3141,7 +3187,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [ "options": [
@ -3245,7 +3292,8 @@
"19.16.39", "19.16.39",
"19.43.41", "19.43.41",
"19.44.39", "19.44.39",
"19.47.53" "19.47.53",
"20.03.43"
] ]
}, },
"options": [] "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 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 { 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 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 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 { 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 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 final class app/revanced/patches/youtube/utils/bottomsheet/BottomSheetHookPatchKt {
public static final fun getBottomSheetHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; 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 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 final class app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatchKt {
public static final fun getEngagementPanelHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; 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_04_or_greater ()Z
public static final fun is_19_05_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_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_15_or_greater ()Z
public static final fun is_19_16_or_greater ()Z public static final fun is_19_16_or_greater ()Z
public static final fun is_19_17_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_34_or_greater ()Z
public static final fun is_19_36_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_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_43_or_greater ()Z
public static final fun is_19_44_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_46_or_greater ()Z
public static final fun is_19_49_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_02_or_greater ()Z
public static final fun is_20_03_or_greater ()Z public static final fun is_20_03_or_greater ()Z
public static final fun is_20_05_or_greater ()Z public static final fun is_20_05_or_greater ()Z

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,14 @@
package app.revanced.patches.music.utils.extension package app.revanced.patches.music.utils.extension
import app.revanced.patches.music.utils.extension.hooks.applicationInitHook 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 import app.revanced.patches.shared.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch( val sharedExtensionPatch = sharedExtensionPatch(
applicationInitHook, 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.addPreferenceWithIntent
import app.revanced.patches.music.utils.settings.addSwitchPreference import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch 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.createPlayerRequestBodyWithModelFingerprint
import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH 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.indexOfManufacturerInstruction
import app.revanced.patches.shared.indexOfModelInstruction import app.revanced.patches.shared.indexOfModelInstruction
import app.revanced.patches.shared.indexOfReleaseInstruction import app.revanced.patches.shared.indexOfReleaseInstruction
import app.revanced.patches.shared.spoof.blockrequest.blockRequestPatch
import app.revanced.util.findMethodOrThrow import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.matchOrThrow

View File

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

View File

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

View File

@ -71,8 +71,7 @@ val playerResponseMethodHookPatch = bytecodePatch(
val beforeVideoIdHooks = val beforeVideoIdHooks =
hooks.filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed() hooks.filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed()
val videoIdHooks = hooks.filterIsInstance<Hook.VideoId>().asReversed() val videoIdHooks = hooks.filterIsInstance<Hook.VideoId>().asReversed()
val videoIdAndPlaylistIdHooks = val videoIdAndPlaylistIdHooks = hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
val afterVideoIdHooks = hooks.filterIsInstance<Hook.PlayerParameter>().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. // 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()) { if (bottomNavScreenFingerprint.resolvable()) {
val bottomNavScreenMutableClass = with(bottomNavScreenFingerprint.methodOrThrow()) { val bottomNavScreenMutableClass = with(bottomNavScreenFingerprint.methodOrThrow()) {
val startIndex = indexOfGetDimensionPixelSizeInstruction(this) val startIndex = indexOfGetDimensionPixelSizeInstruction(this)
val targetIndex = val targetIndex = indexOfFirstInstructionOrThrow(startIndex, Opcode.NEW_INSTANCE)
indexOfFirstInstructionOrThrow(startIndex, Opcode.NEW_INSTANCE)
val targetReference = val targetReference =
getInstruction<ReferenceInstruction>(targetIndex).reference.toString() getInstruction<ReferenceInstruction>(targetIndex).reference.toString()
@ -66,9 +65,7 @@ val navigationButtonsPatch = bytecodePatch(
?: throw ClassNotFoundException("Failed to find class $targetReference") ?: throw ClassNotFoundException("Failed to find class $targetReference")
} }
bottomNavScreenOnGlobalLayoutFingerprint.second.matchOrNull( bottomNavScreenOnGlobalLayoutFingerprint.second.matchOrNull(bottomNavScreenMutableClass)
bottomNavScreenMutableClass
)
?.let { ?.let {
it.method.apply { it.method.apply {
val startIndex = it.patternMatch!!.startIndex val startIndex = it.patternMatch!!.startIndex
@ -85,8 +82,7 @@ val navigationButtonsPatch = bytecodePatch(
// Legacy method. // Legacy method.
bottomNavScreenHandlerFingerprint.methodOrThrow().apply { bottomNavScreenHandlerFingerprint.methodOrThrow().apply {
val targetIndex = indexOfGetItemsInstruction(this) + 1 val targetIndex = indexOfGetItemsInstruction(this) + 1
val targetRegister = val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions( addInstructions(
targetIndex + 1, """ 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 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.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch 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.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH 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.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.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.findMutableMethodOf import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode 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.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") @Suppress("unused")
val screenshotPopupPatch = bytecodePatch( val screenshotPopupPatch = bytecodePatch(
@ -25,67 +29,39 @@ val screenshotPopupPatch = bytecodePatch(
) { ) {
compatibleWith(COMPATIBLE_PACKAGE) compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch) dependsOn(
settingsPatch,
sharedResourceIdPatch,
)
execute { execute {
fun indexOfShowBannerInstruction(method: Method) = if (is_2025_06_or_greater) {
method.indexOfFirstInstruction { screenshotTakenBannerFingerprint.methodOrThrow().apply {
val reference = getReference<FieldReference>() val literalIndex = indexOfFirstLiteralInstructionOrThrow(screenShotShareBanner)
opcode == Opcode.IGET_OBJECT && val insertIndex = indexOfFirstInstructionReversedOrThrow(literalIndex, Opcode.CONST_4)
reference?.name?.contains("shouldShowBanner") == true && val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
reference.definingClass.startsWith("Lcom/reddit/sharing/screenshot/") == true 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))
)
} }
} else {
fun indexOfSetValueInstruction(method: Method) = screenshotTakenBannerLegacyFingerprint.methodOrThrow().apply {
method.indexOfFirstInstruction { addInstructionsWithLabels(
getReference<MethodReference>()?.name == "setValue" 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( updatePatchStatus(

View File

@ -1,6 +1,5 @@
package app.revanced.patches.reddit.layout.subredditdialog 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.fingerprint.legacyFingerprint
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstruction
@ -72,14 +71,6 @@ fun indexOfHasBeenVisitedInstruction(method: Method) =
reference.returnType == "Z" 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( internal val redditAlertDialogsFingerprint = legacyFingerprint(
name = "redditAlertDialogsFingerprint", name = "redditAlertDialogsFingerprint",
returnType = "V", 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.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE 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.extension.Constants.PATCHES_PATH
import app.revanced.patches.reddit.utils.patch.PatchList.REMOVE_SUBREDDIT_DIALOG 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_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_01_or_greater
import app.revanced.patches.reddit.utils.settings.is_2025_05_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.settingsPatch
import app.revanced.patches.reddit.utils.settings.updatePatchStatus import app.revanced.patches.reddit.utils.settings.updatePatchStatus
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@ -36,10 +32,7 @@ val subRedditDialogPatch = bytecodePatch(
) { ) {
compatibleWith(COMPATIBLE_PACKAGE) compatibleWith(COMPATIBLE_PACKAGE)
dependsOn( dependsOn(settingsPatch)
settingsPatch,
sharedResourceIdPatch,
)
execute { execute {
@ -49,8 +42,7 @@ val subRedditDialogPatch = bytecodePatch(
.apply { .apply {
listOfIsLoggedInInstruction(this) listOfIsLoggedInInstruction(this)
.forEach { index -> .forEach { index ->
val register = val register = getInstruction<OneRegisterInstruction>(index + 1).registerA
getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructions( addInstructions(
index + 2, """ 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. // Not used in latest Reddit client.

View File

@ -11,7 +11,7 @@ internal object Constants {
setOf( setOf(
"2024.17.0", // This is the last version that can be patched without anti-split. "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.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.getResourceId
import app.revanced.patches.shared.mapping.resourceMappingPatch import app.revanced.patches.shared.mapping.resourceMappingPatch
var nsfwDialogTitle = -1L var screenShotShareBanner = -1L
private set private set
internal val sharedResourceIdPatch = resourcePatch( internal val sharedResourceIdPatch = resourcePatch(
@ -14,6 +14,6 @@ internal val sharedResourceIdPatch = resourcePatch(
dependsOn(resourceMappingPatch) dependsOn(resourceMappingPatch)
execute { 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) val getAdvertisingIdIndex = indexOfGetAdvertisingIdInstruction(this)
getWalkerMethod(getAdvertisingIdIndex) 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 app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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.iface.reference.StringReference
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
@ -106,8 +108,3 @@ internal val primesLifecycleEventFingerprint = legacyFingerprint(
} >= 0 } >= 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.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction 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.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext 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.patcher.patch.stringOption
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH 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
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
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
import app.revanced.patches.shared.gms.Constants.PERMISSIONS_LEGACY
import app.revanced.util.Utils.trimIndentMultiline import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.fingerprint.methodOrNull
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
@ -88,9 +83,9 @@ fun gmsCoreSupportPatch(
key = "gmsCoreVendorGroupId", key = "gmsCoreVendorGroupId",
default = "app.revanced", default = "app.revanced",
values = values =
mapOf( mapOf(
"ReVanced" to "app.revanced", "ReVanced" to "app.revanced",
), ),
title = "GmsCore vendor group ID", title = "GmsCore vendor group ID",
description = "The vendor's group ID for GmsCore.", description = "The vendor's group ID for GmsCore.",
required = true, required = true,
@ -132,14 +127,6 @@ fun gmsCoreSupportPatch(
required = true required = true
) { it!!.matches(Regex(PACKAGE_NAME_REGEX_PATTERN)) && it != ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC } ) { 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( dependsOn(
gmsCoreSupportResourcePatchFactory( gmsCoreSupportResourcePatchFactory(
gmsCoreVendorGroupIdOption, gmsCoreVendorGroupIdOption,
@ -152,20 +139,6 @@ fun gmsCoreSupportPatch(
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
execute { 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 { fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
val mutableClass by lazy { val mutableClass by lazy {
proxy(it).mutableClass proxy(it).mutableClass
@ -209,9 +182,9 @@ fun gmsCoreSupportPatch(
when (referencedString) { when (referencedString) {
"com.google", "com.google",
"com.google.android.gms", "com.google.android.gms",
in permissions, in PERMISSIONS,
in actions, in ACTIONS,
in authorities, in AUTHORITIES,
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!) -> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
// TODO: Add this permission when bumping GmsCore // TODO: Add this permission when bumping GmsCore
@ -223,7 +196,7 @@ fun gmsCoreSupportPatch(
// only when content:// uri // only when content:// uri
if (str.startsWith("content://")) { if (str.startsWith("content://")) {
// check if matches any authority // check if matches any authority
for (authority in authorities) { for (authority in AUTHORITIES) {
val uriPrefix = "content://$authority" val uriPrefix = "content://$authority"
if (str.startsWith(uriPrefix)) { if (str.startsWith(uriPrefix)) {
return str.replace( return str.replace(
@ -250,56 +223,40 @@ fun gmsCoreSupportPatch(
} }
} }
fun transformPrimeMethod(packageName: String) { fun transformPrimeMethod() {
if (patchAllManifestEnabled) { setOf(
primeMethodFingerprint.methodOrNull()?.apply { primesBackgroundInitializationFingerprint,
var register = 2 primesLifecycleEventFingerprint
).forEach { fingerprint ->
val index = instructions.indexOfFirst { fingerprint.methodOrThrow().apply {
if (it.getReference<StringReference>()?.string != fromPackageName) return@indexOfFirst false val exceptionIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.NEW_INSTANCE &&
register = (it as OneRegisterInstruction).registerA (this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;"
return@indexOfFirst true
} }
val index =
replaceInstruction(index, "const-string v$register, \"$packageName\"") indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(
index,
"const/4 v$register, 0x1"
)
} }
} else { }
setOf( primesApiFingerprint.mutableClassOrThrow().methods.filter { method ->
primesBackgroundInitializationFingerprint, method.name != "<clinit>" &&
primesLifecycleEventFingerprint method.returnType == "V"
).forEach { fingerprint -> }.forEach { method ->
fingerprint.methodOrThrow().apply { method.apply {
val exceptionIndex = indexOfFirstInstructionReversedOrThrow { val index = if (MethodUtil.isConstructor(method))
opcode == Opcode.NEW_INSTANCE && indexOfFirstInstructionOrThrow {
(this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;" opcode == Opcode.INVOKE_DIRECT &&
} getReference<MethodReference>()?.name == "<init>"
val index = } + 1
indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ) else 0
val register = getInstruction<OneRegisterInstruction>(index).registerA addInstruction(
addInstruction( index,
index, "return-void"
"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"
)
}
} }
} }
} }
@ -323,40 +280,37 @@ fun gmsCoreSupportPatch(
return@transform null return@transform null
} }
val earlyReturnFingerprints = mutableListOf( // Return these methods early to prevent the app from crashing.
setOf(
castContextFetchFingerprint, castContextFetchFingerprint,
castDynamiteModuleFingerprint,
castDynamiteModuleV2Fingerprint,
googlePlayUtilityFingerprint, googlePlayUtilityFingerprint,
serviceCheckFingerprint serviceCheckFingerprint,
) sslGuardFingerprint,
).forEach { it.methodOrThrow().returnEarly() }
if (patchAllManifestEnabled) { // Prevent spam logs.
earlyReturnFingerprints += listOf(sslGuardFingerprint) eCatcherFingerprint.methodOrThrow().apply {
val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY)
// Prevent spam logs. addInstruction(index, "return-void")
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. // Specific method that needs to be patched.
transformPrimeMethod(packageName) transformPrimeMethod()
// Verify GmsCore is installed and whitelisted for power optimizations and background usage. // Verify GmsCore is installed and whitelisted for power optimizations and background usage.
if (checkGmsCore == true) { mainActivityOnCreateFingerprint.method.apply {
mainActivityOnCreateFingerprint.method.apply { // Temporary fix for patches with an extension patch that hook the onCreate method as well.
// Temporary fix for patches with an extension patch that hook the onCreate method as well. val setContextIndex = indexOfFirstInstruction {
val setContextIndex = indexOfFirstInstruction { val reference =
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
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( addInstructions(
if (setContextIndex < 0) 0 else setContextIndex + 1, if (setContextIndex < 0) 0 else setContextIndex + 1,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" + "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
@ -381,30 +335,7 @@ fun gmsCoreSupportPatch(
* that are present in GmsCore which need to be transformed. * that are present in GmsCore which need to be transformed.
*/ */
private object Constants { private object Constants {
/**
* All permissions.
*/
val PERMISSIONS = setOf( 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 // C2DM / GCM
"com.google.android.c2dm.permission.RECEIVE", "com.google.android.c2dm.permission.RECEIVE",
"com.google.android.c2dm.permission.SEND", "com.google.android.c2dm.permission.SEND",
@ -418,234 +349,7 @@ private object Constants {
// "com.google.android.gms.permission.ACTIVITY_RECOGNITION", // "com.google.android.gms.permission.ACTIVITY_RECOGNITION",
) )
/**
* All intent actions.
*/
val ACTIONS = setOf( 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 // C2DM / GCM
"com.google.android.c2dm.intent.REGISTER", "com.google.android.c2dm.intent.REGISTER",
"com.google.android.c2dm.intent.REGISTRATION", "com.google.android.c2dm.intent.REGISTRATION",
@ -703,19 +407,7 @@ private object Constants {
"com.google.android.gms.droidguard.service.START", "com.google.android.gms.droidguard.service.START",
) )
/**
* All content provider authorities.
*/
val AUTHORITIES = setOf( 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 // gsf
"com.google.android.gsf.gservices", "com.google.android.gsf.gservices",

View File

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

View File

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

View File

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

View File

@ -90,8 +90,7 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
) )
val playbackStartIndex = indexOfPlaybackStartDescriptorInstruction(this) + 1 val playbackStartIndex = indexOfPlaybackStartDescriptorInstruction(this) + 1
val playbackStartRegister = val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex).registerA
getInstruction<OneRegisterInstruction>(playbackStartIndex).registerA
val mapIndex = indexOfFirstInstructionOrThrow(playbackStartIndex) { val mapIndex = indexOfFirstInstructionOrThrow(playbackStartIndex) {
val reference = getReference<MethodReference>() val reference = getReference<MethodReference>()
@ -170,24 +169,15 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
val playbackStartIndex = indexOfFirstInstructionOrThrow { val playbackStartIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
} }
val mapIndex = val mapIndex = indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
val mapRegister = getInstruction<TwoRegisterInstruction>(mapIndex).registerA val mapRegister = getInstruction<TwoRegisterInstruction>(mapIndex).registerA
val playbackStartRegister = val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA
getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA val videoIdRegister = getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
val videoIdRegister =
getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
addInstructionsWithLabels( addInstructionsWithLabels(
playbackStartIndex + 2, """ playbackStartIndex + 2, """
move-object/from16 v$mapRegister, p2 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 { bottomUiContainerThemeFingerprint.matchOrThrow().let {
it.method.apply { it.method.apply {
val darkThemeIndex = it.patternMatch!!.startIndex + 2 val darkThemeIndex = it.patternMatch!!.startIndex + 2
val darkThemeReference = val darkThemeReference = getInstruction<ReferenceInstruction>(darkThemeIndex).reference.toString()
getInstruction<ReferenceInstruction>(darkThemeIndex).reference.toString()
implementation!!.instructions implementation!!.instructions
.withIndex() .withIndex()
@ -92,8 +91,7 @@ private val snackBarComponentsBytecodePatch = bytecodePatch(
.map { (index, _) -> index } .map { (index, _) -> index }
.reversed() .reversed()
.forEach { index -> .forEach { index ->
val appThemeIndex = val appThemeIndex = indexOfFirstInstructionReversedOrThrow(index, Opcode.MOVE_RESULT_OBJECT)
indexOfFirstInstructionReversedOrThrow(index, Opcode.MOVE_RESULT_OBJECT)
val appThemeRegister = val appThemeRegister =
getInstruction<OneRegisterInstruction>(appThemeIndex).registerA getInstruction<OneRegisterInstruction>(appThemeIndex).registerA
val darkThemeRegister = val darkThemeRegister =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -57,26 +57,20 @@ val actionButtonsPatch = bytecodePatch(
findMethodOrThrow(parameters[1].type) { findMethodOrThrow(parameters[1].type) {
name == "toString" name == "toString"
} }
val identifierReference = with(conversionContextToStringMethod) { val identifierReference = with (conversionContextToStringMethod) {
val identifierStringIndex = val identifierStringIndex =
indexOfFirstStringInstructionOrThrow(", identifierProperty=") indexOfFirstStringInstructionOrThrow(", identifierProperty=")
val identifierStringAppendIndex = val identifierStringAppendIndex =
indexOfFirstInstructionOrThrow(identifierStringIndex, Opcode.INVOKE_VIRTUAL) indexOfFirstInstructionOrThrow(identifierStringIndex, Opcode.INVOKE_VIRTUAL)
val identifierStringAppendIndexRegister = val identifierStringAppendIndexRegister = getInstruction<FiveRegisterInstruction>(identifierStringAppendIndex).registerD
getInstruction<FiveRegisterInstruction>(identifierStringAppendIndex).registerD
val identifierAppendIndex = val identifierAppendIndex =
indexOfFirstInstructionOrThrow( indexOfFirstInstructionOrThrow(identifierStringAppendIndex + 1, Opcode.INVOKE_VIRTUAL)
identifierStringAppendIndex + 1, val identifierRegister = getInstruction<FiveRegisterInstruction>(identifierAppendIndex).registerD
Opcode.INVOKE_VIRTUAL val identifierIndex = indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) {
) opcode == Opcode.IGET_OBJECT &&
val identifierRegister = getReference<FieldReference>()?.type == "Ljava/lang/String;" &&
getInstruction<FiveRegisterInstruction>(identifierAppendIndex).registerD (this as? TwoRegisterInstruction)?.registerA == identifierRegister
val identifierIndex = }
indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;" &&
(this as? TwoRegisterInstruction)?.registerA == identifierRegister
}
getInstruction<ReferenceInstruction>(identifierIndex).reference 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.endScreenElementLayoutIcon
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo
import app.revanced.patches.youtube.utils.resourceid.offlineActionsVideoDeletedUndoSnackbarText 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.seekEasyHorizontalTouchOffsetToStartScrubbing
import app.revanced.patches.youtube.utils.resourceid.suggestedAction import app.revanced.patches.youtube.utils.resourceid.suggestedAction
import app.revanced.patches.youtube.utils.resourceid.tapBloomView import app.revanced.patches.youtube.utils.resourceid.tapBloomView
import app.revanced.patches.youtube.utils.resourceid.touchArea 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.verticalTouchOffsetToStartFineScrubbing
import app.revanced.patches.youtube.utils.resourceid.videoZoomSnapIndicator import app.revanced.patches.youtube.utils.resourceid.videoZoomSnapIndicator
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint

View File

@ -129,11 +129,9 @@ private val speedOverlayPatch = bytecodePatch(
// region patch for Custom speed overlay float value // region patch for Custom speed overlay float value
val speedFieldReference = with(speedOverlayFloatValueFingerprint.methodOrThrow()) { val speedFieldReference = with (speedOverlayFloatValueFingerprint.methodOrThrow()) {
val literalIndex = val literalIndex = indexOfFirstLiteralInstructionOrThrow(SPEED_OVERLAY_LEGACY_FEATURE_FLAG)
indexOfFirstLiteralInstructionOrThrow(SPEED_OVERLAY_LEGACY_FEATURE_FLAG) val floatIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
val floatIndex =
indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
val floatRegister = getInstruction<TwoRegisterInstruction>(floatIndex).registerA val floatRegister = getInstruction<TwoRegisterInstruction>(floatIndex).registerA
addInstructions( addInstructions(
@ -606,9 +604,7 @@ val playerComponentsPatch = bytecodePatch(
) )
if (is_20_12_or_greater) { if (is_20_12_or_greater) {
filmStripOverlayMotionEventPrimaryFingerprint.matchOrThrow( filmStripOverlayMotionEventPrimaryFingerprint.matchOrThrow(filmStripOverlayStartParentFingerprint).let {
filmStripOverlayStartParentFingerprint
).let {
it.method.apply { it.method.apply {
val index = it.patternMatch!!.startIndex val index = it.patternMatch!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(index).registerA val register = getInstruction<TwoRegisterInstruction>(index).registerA
@ -617,9 +613,7 @@ val playerComponentsPatch = bytecodePatch(
} }
} }
filmStripOverlayMotionEventSecondaryFingerprint.matchOrThrow( filmStripOverlayMotionEventSecondaryFingerprint.matchOrThrow(filmStripOverlayStartParentFingerprint).let {
filmStripOverlayStartParentFingerprint
).let {
it.method.apply { it.method.apply {
val index = it.patternMatch!!.startIndex + 2 val index = it.patternMatch!!.startIndex + 2
val register = getInstruction<OneRegisterInstruction>(index).registerA 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.fingerprint.legacyFingerprint
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.util.MethodUtil
internal val broadcastReceiverFingerprint = legacyFingerprint( internal val broadcastReceiverFingerprint = legacyFingerprint(
name = "broadcastReceiverFingerprint", 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.hookBackgroundPlayVideoInformation
import app.revanced.patches.youtube.video.information.videoEndMethod import app.revanced.patches.youtube.video.information.videoEndMethod
import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.Utils.printWarn
import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findMethodOrThrow import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
@ -312,6 +313,8 @@ val fullscreenComponentsPatch = bytecodePatch(
} }
settingArray += "SETTINGS: KEEP_LANDSCAPE_MODE" settingArray += "SETTINGS: KEEP_LANDSCAPE_MODE"
} else {
printWarn("\"Keep landscape mode\" is not supported in this version. Use YouTube 19.16.39 or earlier.")
} }
// endregion // endregion

View File

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

View File

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

View File

@ -256,8 +256,7 @@ val overlayButtonsPatch = resourcePatch(
width != "0.0dip", width != "0.0dip",
) )
val isButton = val isButton = id.endsWith("_button") && id != "@id/multiview_button" || id == "@id/youtube_controls_fullscreen_button_stub"
id.endsWith("_button") && id != "@id/multiview_button" || id == "@id/youtube_controls_fullscreen_button_stub"
// Adjust TimeBar and Chapter bottom padding // Adjust TimeBar and Chapter bottom padding
val timBarItem = mutableMapOf( val timBarItem = mutableMapOf(
@ -287,10 +286,7 @@ val overlayButtonsPatch = resourcePatch(
if (id.equals("@+id/bottom_margin")) { if (id.equals("@+id/bottom_margin")) {
node.setAttribute("android:layout_height", marginBottom) node.setAttribute("android:layout_height", marginBottom)
} else if (id.equals("@id/time_bar_reference_view")) { } else if (id.equals("@id/time_bar_reference_view")) {
node.setAttribute( node.setAttribute("yt:layout_constraintBottom_toTopOf", "@id/quick_actions_container")
"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.Opcode
import com.android.tools.smali.dexlib2.iface.Method 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.MethodReference
import kotlin.collections.listOf
internal val shortsSeekbarColorFingerprint = legacyFingerprint( internal val shortsSeekbarColorFingerprint = legacyFingerprint(
name = "shortsSeekbarColorFingerprint", name = "shortsSeekbarColorFingerprint",

View File

@ -139,8 +139,7 @@ val seekbarComponentsPatch = bytecodePatch(
reference?.returnType == "V" && reference?.returnType == "V" &&
reference.parameterTypes.isEmpty() reference.parameterTypes.isEmpty()
} }
val thisInstanceRegister = val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(tapSeekIndex).registerC
getInstruction<FiveRegisterInstruction>(tapSeekIndex).registerC
val tapSeekClass = getInstruction(tapSeekIndex) val tapSeekClass = getInstruction(tapSeekIndex)
.getReference<MethodReference>()!! .getReference<MethodReference>()!!
@ -275,10 +274,7 @@ val seekbarComponentsPatch = bytecodePatch(
playerSeekbarHandleColorPrimaryFingerprint, playerSeekbarHandleColorPrimaryFingerprint,
playerSeekbarHandleColorSecondaryFingerprint playerSeekbarHandleColorSecondaryFingerprint
).forEach { ).forEach {
it.methodOrThrow().addColorChangeInstructions( it.methodOrThrow().addColorChangeInstructions(ytStaticBrandRed, "getVideoPlayerSeekbarColorAccent")
ytStaticBrandRed,
"getVideoPlayerSeekbarColorAccent"
)
} }
// If hiding feed seekbar thumbnails, then turn off the cairo gradient // 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 // 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.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference 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.MethodReference
import kotlin.collections.listOf
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"
}
)
internal val bottomSheetMenuListBuilderFingerprint = legacyFingerprint( internal val bottomSheetMenuListBuilderFingerprint = legacyFingerprint(
name = "bottomSheetMenuListBuilderFingerprint", 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_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_49_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_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_25_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_28_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 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.patches.youtube.video.videoid.videoIdPatch
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.ResourceGroup import app.revanced.util.ResourceGroup
import app.revanced.util.addEntryValues
import app.revanced.util.cloneMutable import app.revanced.util.cloneMutable
import app.revanced.util.copyResources import app.revanced.util.copyResources
import app.revanced.util.findMethodOrThrow import app.revanced.util.findMethodOrThrow
@ -339,35 +339,7 @@ private val shortsCustomActionsPatch = bytecodePatch(
} }
} }
if (is_19_11_or_greater) { recyclerViewTreeObserverHook("$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
// 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
""",
)
}
// endregion // endregion
@ -435,7 +407,7 @@ private val shortsRepeatPatch = bytecodePatch(
"setMainActivity" "setMainActivity"
) )
val endScreenReference = with(reelEnumConstructorFingerprint.methodOrThrow()) { val endScreenReference = with (reelEnumConstructorFingerprint.methodOrThrow()) {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID) val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID)
addInstructions( addInstructions(
@ -515,11 +487,7 @@ private val shortsRepeatPatch = bytecodePatch(
// Manually add the 'Autoplay' code that Google removed. // Manually add the 'Autoplay' code that Google removed.
// Tested on YouTube 20.10. // Tested on YouTube 20.10.
if (is_20_09_or_greater) { if (is_20_09_or_greater) {
val (directReference, virtualReference) = with( val (directReference, virtualReference) = with (reelPlaybackFingerprint.methodOrThrow(videoIdFingerprintShorts)) {
reelPlaybackFingerprint.methodOrThrow(
videoIdFingerprintShorts
)
) {
val directIndex = indexOfInitializationInstruction(this) val directIndex = indexOfInitializationInstruction(this)
val virtualIndex = indexOfFirstInstructionOrThrow(directIndex) { val virtualIndex = indexOfFirstInstructionOrThrow(directIndex) {
opcode == Opcode.INVOKE_VIRTUAL && opcode == Opcode.INVOKE_VIRTUAL &&
@ -537,8 +505,7 @@ private val shortsRepeatPatch = bytecodePatch(
opcode == Opcode.INVOKE_STATIC && opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.definingClass == EXTENSION_REPEAT_STATE_CLASS_DESCRIPTOR getReference<MethodReference>()?.definingClass == EXTENSION_REPEAT_STATE_CLASS_DESCRIPTOR
} }
val enumRegister = val enumRegister = getInstruction<OneRegisterInstruction>(extensionIndex + 1).registerA
getInstruction<OneRegisterInstruction>(extensionIndex + 1).registerA
val freeIndex = indexOfFirstInstructionOrThrow(extensionIndex) { val freeIndex = indexOfFirstInstructionOrThrow(extensionIndex) {
opcode == Opcode.SGET_OBJECT && opcode == Opcode.SGET_OBJECT &&
getReference<FieldReference>()?.name != "a" getReference<FieldReference>()?.name != "a"
@ -1024,8 +991,7 @@ val shortsComponentPatch = bytecodePatch(
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
} }
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
val playbackStartRegister = val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructionsWithLabels( addInstructionsWithLabels(
index + 2, index + 2,

View File

@ -1,17 +1,41 @@
package app.revanced.patches.youtube.shorts.startupshortsreset package app.revanced.patches.youtube.shorts.startupshortsreset
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags 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", name = "userWasInShortsABConfigFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, returnType = "V",
returnType = "Z", strings = listOf("Failed to get offline response: "),
literals = listOf(45358360L) 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.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch 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.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR 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.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.is_20_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference 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.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstStringInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -36,19 +42,50 @@ val resumingShortsOnStartupPatch = bytecodePatch(
execute { execute {
userWasInShortsConfigFingerprint fun MutableMethod.hookUserWasInShortsABConfig(startIndex: Int) {
.methodOrThrow() val walkerIndex = implementation!!.instructions.let {
.addInstructionsWithLabels( val subListIndex =
0, """ it.subList(startIndex, startIndex + 20).indexOfFirst { instruction ->
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z val reference = instruction.getReference<MethodReference>()
move-result v0 instruction.opcode == Opcode.INVOKE_VIRTUAL &&
if-eqz v0, :show reference?.returnType == "Z" &&
const/4 v0, 0x0 reference.definingClass != "Lj${'$'}/util/Optional;" &&
return v0 reference.parameterTypes.isEmpty()
:show }
nop 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) { if (is_20_02_or_greater) {
userWasInShortsAlternativeFingerprint.matchOrThrow().let { userWasInShortsAlternativeFingerprint.matchOrThrow().let {

View File

@ -10,7 +10,6 @@ import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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.MethodReference
internal val fullScreenEngagementOverlayFingerprint = legacyFingerprint( 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. // This method is always called "a" because this kind of class always has a single method.
method.name == "a" && method.name == "a" &&
classDef.methods.count() == 2 && 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.ResourceGroup
import app.revanced.util.copyResources import app.revanced.util.copyResources
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.getReference 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 // region patch for disable swipe to enter fullscreen mode (in the player) and disable swipe to exit fullscreen mode
playerGestureConfigSyntheticFingerprint.methodOrThrow().apply { playerGestureConfigSyntheticFingerprint.matchOrThrow().let {
val disableSwipeToExitFullscreenModeIndex = val endIndex = it.patternMatch!!.endIndex
indexOfPlayerConfigModelBooleanInstruction(this)
val disableSwipeToEnterFullscreenModeInThePlayerIndex =
indexOfPlayerConfigModelBooleanInstruction(this, disableSwipeToExitFullscreenModeIndex + 1)
mapOf( mapOf(
disableSwipeToExitFullscreenModeIndex to "disableSwipeToExitFullscreenMode", 3 to "disableSwipeToEnterFullscreenModeInThePlayer",
disableSwipeToEnterFullscreenModeInThePlayerIndex to "disableSwipeToEnterFullscreenModeInThePlayer" 9 to "disableSwipeToExitFullscreenMode"
).forEach { (walkerIndex, methodName) -> ).forEach { (offSet, methodName) ->
getWalkerMethod(walkerIndex).apply { it.getWalkerMethod(endIndex - offSet).apply {
val index = implementation!!.instructions.lastIndex val index = implementation!!.instructions.lastIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA 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.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.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.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 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.shared.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.extension.hooks.applicationInitHook 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. // TODO: Move this to a "Hook.kt" file. Same for other extension hook patches.
val sharedExtensionPatch = sharedExtensionPatch( val sharedExtensionPatch = sharedExtensionPatch(
applicationInitHook, 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 -> ?.let { node ->
node.insertNode("Preference", node) { node.insertNode("Preference", node) {
for (index in 0 until node.attributes.length) { for (index in 0 until node.attributes.length) {
with(node.attributes.item(index)) { with (node.attributes.item(index)) {
setAttribute(nodeName, nodeValue) setAttribute(nodeName, nodeValue)
} }
} }

View File

@ -1,10 +1,10 @@
package app.revanced.patches.youtube.utils.fix.cairo 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.fingerprint.legacyFingerprint
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags 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 import com.android.tools.smali.dexlib2.Opcode
/** /**

View File

@ -1,9 +1,12 @@
package app.revanced.patches.youtube.utils.fix.splash package app.revanced.patches.youtube.utils.fix.splash
import app.revanced.patcher.patch.resourcePatch 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.is_19_32_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch 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.restoreOldSplashAnimationIncluded
import app.revanced.patches.youtube.utils.settings.ResourceUtils.youtubePackageName
import app.revanced.util.findElementByAttributeValueOrThrow
import org.w3c.dom.Element import org.w3c.dom.Element
/** /**
@ -25,6 +28,18 @@ val darkModeSplashScreenPatch = resourcePatch(
return@finalize 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) { if (restoreOldSplashAnimationIncluded) {
document("res/values-night/styles.xml").use { document -> document("res/values-night/styles.xml").use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element val resourcesNode = document.getElementsByTagName("resources").item(0) as Element

View File

@ -153,18 +153,3 @@ internal val onesieEncryptionAlternativeFeatureFlagFingerprint = legacyFingerpri
name = "onesieEncryptionAlternativeFeatureFlagFingerprint", name = "onesieEncryptionAlternativeFeatureFlagFingerprint",
literals = listOf(ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG), 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.compatibility.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA 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_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.is_20_10_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.request.buildRequestPatch import app.revanced.patches.youtube.utils.request.buildRequestPatch
@ -346,18 +345,11 @@ val spoofStreamingDataPatch = bytecodePatch(
"$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z"
) )
if (is_19_50_or_greater) { if (is_20_10_or_greater) {
playbackStartDescriptorFeatureFlagFingerprint.injectLiteralInstructionBooleanCall( onesieEncryptionAlternativeFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG, ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z" "$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" 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.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import kotlin.collections.mutableListOf
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR = private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/VideoUtils;" "$EXTENSION_PATH/utils/VideoUtils;"
@ -58,10 +59,7 @@ val fullscreenButtonHookPatch = bytecodePatch(
getReference<MethodReference>()?.name == "addListener" getReference<MethodReference>()?.name == "addListener"
} }
val animatorListenerAdapterClass = getInstruction<ReferenceInstruction>( val animatorListenerAdapterClass = getInstruction<ReferenceInstruction>(
indexOfFirstInstructionReversedOrThrow( indexOfFirstInstructionReversedOrThrow(addListenerIndex, Opcode.NEW_INSTANCE)
addListenerIndex,
Opcode.NEW_INSTANCE
)
).reference.toString() ).reference.toString()
return Pair( return Pair(
findMethodOrThrow(animatorListenerAdapterClass) { parameters.isEmpty() }, findMethodOrThrow(animatorListenerAdapterClass) { parameters.isEmpty() },

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