mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-03 08:04:36 +02:00
Compare commits
No commits in common. "revanced-extended" and "v5.6.1-dev.4" have entirely different histories.
revanced-e
...
v5.6.1-dev
165
README.md
165
README.md
@ -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": []
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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");
|
||||||
|
@ -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");
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
254
patches.json
254
patches.json
@ -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": []
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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)")
|
||||||
}
|
}
|
||||||
|
@ -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, """
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 ->
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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, """
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
)
|
@ -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(
|
||||||
|
@ -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",
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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;"
|
||||||
|
}
|
@ -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")
|
|
||||||
)
|
|
@ -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",
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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 =
|
||||||
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 &&
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
""",
|
""",
|
||||||
|
@ -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"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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,
|
||||||
|
@ -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"),
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
|
||||||
}
|
|
@ -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
|
||||||
|
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;")
|
|
||||||
}
|
|
||||||
)
|
|
@ -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.
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -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"
|
|
||||||
)
|
|
@ -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"
|
|
||||||
}
|
|
||||||
)
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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;")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
)
|
|
||||||
|
@ -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"
|
||||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user