mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-04-29 22:24:31 +02:00
Compare commits
53 Commits
v5.6.1-dev
...
revanced-e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
42cc8201f5 | ||
![]() |
15d0fafcf8 | ||
![]() |
db10e410d3 | ||
![]() |
8f23d76f37 | ||
![]() |
7168e49121 | ||
![]() |
2b52380294 | ||
![]() |
0cc693961a | ||
![]() |
bbf863c630 | ||
![]() |
783e366242 | ||
![]() |
4a19a960c5 | ||
![]() |
e55fd4eb74 | ||
![]() |
64af7fd8b6 | ||
![]() |
457dbfec6c | ||
![]() |
22b98336d5 | ||
![]() |
7e4a71b385 | ||
![]() |
bb5964ce98 | ||
![]() |
15db05c636 | ||
![]() |
3f9edca15d | ||
![]() |
1eca8c854c | ||
![]() |
692b4f2c53 | ||
![]() |
5162dccecc | ||
![]() |
56b713b0db | ||
![]() |
f761bc45ec | ||
![]() |
bccd6dc5df | ||
![]() |
09a8eb7114 | ||
![]() |
b439ef3ee7 | ||
![]() |
7f85e802c2 | ||
![]() |
bb1498df76 | ||
![]() |
1c58c6a36e | ||
![]() |
26bf2f4b82 | ||
![]() |
a037b25c13 | ||
![]() |
b72bb71e30 | ||
![]() |
28ff781786 | ||
![]() |
ae69aca189 | ||
![]() |
8d84304737 | ||
![]() |
1926becdfc | ||
![]() |
9146d9283b | ||
![]() |
8db38af44f | ||
![]() |
1aaf03df50 | ||
![]() |
ee0a7b5443 | ||
![]() |
a9408b0f51 | ||
![]() |
03c0826fab | ||
![]() |
0384907324 | ||
![]() |
2b0f12cb9e | ||
![]() |
5088daa434 | ||
![]() |
b5fe136898 | ||
![]() |
807494f8a7 | ||
![]() |
d8ae741853 | ||
![]() |
934c33a0f9 | ||
![]() |
b725e54aee | ||
![]() |
c57de97576 | ||
![]() |
2e3274152b | ||
![]() |
6499b7d3f0 |
165
README.md
165
README.md
@ -11,73 +11,73 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
|
||||
|
||||
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||
|:--------:|:--------------:|:-----------------:|
|
||||
| `Alternative thumbnails` | Adds options to replace video thumbnails using the DeArrow API or image captures from the video. | 19.05.36 ~ 20.03.43 |
|
||||
| `Ambient mode control` | Adds options to disable Ambient mode and to bypass Ambient mode restrictions. | 19.05.36 ~ 20.03.43 |
|
||||
| `Bypass URL redirects` | Adds an option to bypass URL redirects and open the original URL directly. | 19.05.36 ~ 20.03.43 |
|
||||
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 19.05.36 ~ 20.03.43 |
|
||||
| `Change form factor` | Adds an option to change the UI appearance to a phone, tablet, or automotive device. | 19.05.36 ~ 20.03.43 |
|
||||
| `Change live ring click action` | Adds an option to open the channel instead of the live stream when clicking on the live ring. | 19.05.36 ~ 20.03.43 |
|
||||
| `Change player flyout menu toggles` | Adds an option to use text toggles instead of switch toggles within the additional settings menu. | 19.05.36 ~ 20.03.43 |
|
||||
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 19.05.36 ~ 20.03.43 |
|
||||
| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 19.05.36 ~ 20.03.43 |
|
||||
| `Custom Shorts action buttons` | Changes, at compile time, the icon of the action buttons of the Shorts player. | 19.05.36 ~ 20.03.43 |
|
||||
| `Custom branding icon for YouTube` | Changes the YouTube app icon to the icon specified in patch options. | 19.05.36 ~ 20.03.43 |
|
||||
| `Custom branding name for YouTube` | Changes the YouTube app name to the name specified in patch options. | 19.05.36 ~ 20.03.43 |
|
||||
| `Custom double tap length` | Adds Double-tap to seek values that are specified in patch options. | 19.05.36 ~ 20.03.43 |
|
||||
| `Custom header for YouTube` | Applies a custom header in the top left corner within the app. | 19.05.36 ~ 20.03.43 |
|
||||
| `Description components` | Adds options to hide and disable description components. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable QUIC protocol` | Adds an option to disable CronetEngine's QUIC protocol. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable forced auto audio tracks` | Adds an option to disable audio tracks from being automatically enabled. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable forced auto captions` | Adds an option to disable captions from being automatically enabled. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable haptic feedback` | Adds options to disable haptic feedback when swiping in the video player. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable layout updates` | Adds an option to disable layout updates by server. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable resuming Miniplayer on startup` | Adds an option to disable the Miniplayer 'Continue watching' from resuming on app startup. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable resuming Shorts on startup` | Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched. | 19.05.36 ~ 20.03.43 |
|
||||
| `Disable splash animation` | Adds an option to disable the splash animation on app startup. | 19.05.36 ~ 20.03.43 |
|
||||
| `Enable OPUS codec` | Adds an option to enable the OPUS audio codec if the player response includes it. | 19.05.36 ~ 20.03.43 |
|
||||
| `Enable debug logging` | Adds an option to enable debug logging. | 19.05.36 ~ 20.03.43 |
|
||||
| `Enable gradient loading screen` | Adds an option to enable the gradient loading screen. | 19.05.36 ~ 20.03.43 |
|
||||
| `Force hide player buttons background` | Removes, at compile time, the dark background surrounding the video player controls. | 19.05.36 ~ 20.03.43 |
|
||||
| `Fullscreen components` | Adds options to hide or change components related to fullscreen. | 19.05.36 ~ 20.03.43 |
|
||||
| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide Shorts dimming` | Removes, at compile time, the dimming effect at the top and bottom of Shorts videos. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide accessibility controls dialog` | Removes, at compile time, accessibility controls dialog 'Turn on accessibility controls for the video player?'. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide action buttons` | Adds options to hide action buttons under videos. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide ads` | Adds options to hide ads. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide comments components` | Adds options to hide components related to comments. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide feed components` | Adds options to hide components related to feeds. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide feed flyout menu` | Adds the ability to hide feed flyout menu components using a custom filter. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide layout components` | Adds options to hide general layout components. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide player buttons` | Adds options to hide buttons in the video player. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide player flyout menu` | Adds options to hide player flyout menu components. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hide shortcuts` | Remove, at compile time, the app shortcuts that appears when the app icon is long pressed. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hook YouTube Music actions` | Adds support for opening music in RVX Music using the in-app YouTube Music button. | 19.05.36 ~ 20.03.43 |
|
||||
| `Hook download actions` | Adds support to download videos with an external downloader app using the in-app download button. | 19.05.36 ~ 20.03.43 |
|
||||
| `MaterialYou` | Applies the MaterialYou theme for Android 12+ devices. | 19.05.36 ~ 20.03.43 |
|
||||
| `Miniplayer` | Adds options to change the in-app minimized player, and if patching target 19.16+ adds options to use modern miniplayers. | 19.05.36 ~ 20.03.43 |
|
||||
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 19.05.36 ~ 20.03.43 |
|
||||
| `Open links externally` | Adds an option to always open links in your browser instead of the in-app browser. | 19.05.36 ~ 20.03.43 |
|
||||
| `Overlay buttons` | Adds options to display useful overlay buttons in the video player. | 19.05.36 ~ 20.03.43 |
|
||||
| `Player components` | Adds options to hide or change components related to the video player. | 19.05.36 ~ 20.03.43 |
|
||||
| `Remove background playback restrictions` | Removes restrictions on background playback, including for music and kids videos. | 19.05.36 ~ 20.03.43 |
|
||||
| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 19.05.36 ~ 20.03.43 |
|
||||
| `Return YouTube Dislike` | Adds an option to show the dislike count of videos using the Return YouTube Dislike API. | 19.05.36 ~ 20.03.43 |
|
||||
| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 19.05.36 ~ 20.03.43 |
|
||||
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 19.05.36 ~ 20.03.43 |
|
||||
| `Seekbar components` | Adds options to hide or change components related to the seekbar. | 19.05.36 ~ 20.03.43 |
|
||||
| `Settings for YouTube` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 19.05.36 ~ 20.03.43 |
|
||||
| `Shorts components` | Adds options to hide or change components related to YouTube Shorts. | 19.05.36 ~ 20.03.43 |
|
||||
| `Snack bar components` | Adds options to hide or change components related to the snack bar. | 19.05.36 ~ 20.03.43 |
|
||||
| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as sponsored content. | 19.05.36 ~ 20.03.43 |
|
||||
| `Spoof app version` | Adds options to spoof the YouTube client version. This can be used to restore old UI elements and features. | 19.05.36 ~ 20.03.43 |
|
||||
| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 19.05.36 ~ 20.03.43 |
|
||||
| `Swipe controls` | Adds options for controlling volume and brightness with swiping, and whether to enter fullscreen when swiping down below the player. | 19.05.36 ~ 20.03.43 |
|
||||
| `Theme` | Changes the app's themes to the values specified in patch options. | 19.05.36 ~ 20.03.43 |
|
||||
| `Toolbar components` | Adds options to hide or change components located on the toolbar, such as the search bar, header, and toolbar buttons. | 19.05.36 ~ 20.03.43 |
|
||||
| `Translations for YouTube` | Add translations or remove string resources. | 19.05.36 ~ 20.03.43 |
|
||||
| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 19.05.36 ~ 20.03.43 |
|
||||
| `Visual preferences icons for YouTube` | Adds icons to specific preferences in the settings. | 19.05.36 ~ 20.03.43 |
|
||||
| `Watch history` | Adds an option to change the domain of the watch history or check its status. | 19.05.36 ~ 20.03.43 |
|
||||
| `Alternative thumbnails` | Adds options to replace video thumbnails using the DeArrow API or image captures from the video. | 19.05.36 ~ 19.47.53 |
|
||||
| `Ambient mode control` | Adds options to disable Ambient mode and to bypass Ambient mode restrictions. | 19.05.36 ~ 19.47.53 |
|
||||
| `Bypass URL redirects` | Adds an option to bypass URL redirects and open the original URL directly. | 19.05.36 ~ 19.47.53 |
|
||||
| `Bypass image region restrictions` | Adds an option to use a different host for static images, so that images blocked in some countries can be received. | 19.05.36 ~ 19.47.53 |
|
||||
| `Change form factor` | Adds an option to change the UI appearance to a phone, tablet, or automotive device. | 19.05.36 ~ 19.47.53 |
|
||||
| `Change live ring click action` | Adds an option to open the channel instead of the live stream when clicking on the live ring. | 19.05.36 ~ 19.47.53 |
|
||||
| `Change player flyout menu toggles` | Adds an option to use text toggles instead of switch toggles within the additional settings menu. | 19.05.36 ~ 19.47.53 |
|
||||
| `Change share sheet` | Adds an option to change the in-app share sheet to the system share sheet. | 19.05.36 ~ 19.47.53 |
|
||||
| `Change start page` | Adds an option to set which page the app opens in instead of the homepage. | 19.05.36 ~ 19.47.53 |
|
||||
| `Custom Shorts action buttons` | Changes, at compile time, the icon of the action buttons of the Shorts player. | 19.05.36 ~ 19.47.53 |
|
||||
| `Custom branding icon for YouTube` | Changes the YouTube app icon to the icon specified in patch options. | 19.05.36 ~ 19.47.53 |
|
||||
| `Custom branding name for YouTube` | Changes the YouTube app name to the name specified in patch options. | 19.05.36 ~ 19.47.53 |
|
||||
| `Custom double tap length` | Adds Double-tap to seek values that are specified in patch options. | 19.05.36 ~ 19.47.53 |
|
||||
| `Custom header for YouTube` | Applies a custom header in the top left corner within the app. | 19.05.36 ~ 19.47.53 |
|
||||
| `Description components` | Adds options to hide and disable description components. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable QUIC protocol` | Adds an option to disable CronetEngine's QUIC protocol. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable forced auto audio tracks` | Adds an option to disable audio tracks from being automatically enabled. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable forced auto captions` | Adds an option to disable captions from being automatically enabled. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable haptic feedback` | Adds options to disable haptic feedback when swiping in the video player. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable layout updates` | Adds an option to disable layout updates by server. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable resuming Miniplayer on startup` | Adds an option to disable the Miniplayer 'Continue watching' from resuming on app startup. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable resuming Shorts on startup` | Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched. | 19.05.36 ~ 19.47.53 |
|
||||
| `Disable splash animation` | Adds an option to disable the splash animation on app startup. | 19.05.36 ~ 19.47.53 |
|
||||
| `Enable OPUS codec` | Adds an option to enable the OPUS audio codec if the player response includes it. | 19.05.36 ~ 19.47.53 |
|
||||
| `Enable debug logging` | Adds an option to enable debug logging. | 19.05.36 ~ 19.47.53 |
|
||||
| `Enable gradient loading screen` | Adds an option to enable the gradient loading screen. | 19.05.36 ~ 19.47.53 |
|
||||
| `Force hide player buttons background` | Removes, at compile time, the dark background surrounding the video player controls. | 19.05.36 ~ 19.47.53 |
|
||||
| `Fullscreen components` | Adds options to hide or change components related to fullscreen. | 19.05.36 ~ 19.47.53 |
|
||||
| `GmsCore support` | Allows patched Google apps to run without root and under a different package name by using GmsCore instead of Google Play Services. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide Shorts dimming` | Removes, at compile time, the dimming effect at the top and bottom of Shorts videos. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide accessibility controls dialog` | Removes, at compile time, accessibility controls dialog 'Turn on accessibility controls for the video player?'. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide action buttons` | Adds options to hide action buttons under videos. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide ads` | Adds options to hide ads. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide comments components` | Adds options to hide components related to comments. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide feed components` | Adds options to hide components related to feeds. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide feed flyout menu` | Adds the ability to hide feed flyout menu components using a custom filter. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide layout components` | Adds options to hide general layout components. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide player buttons` | Adds options to hide buttons in the video player. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide player flyout menu` | Adds options to hide player flyout menu components. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hide shortcuts` | Remove, at compile time, the app shortcuts that appears when the app icon is long pressed. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hook YouTube Music actions` | Adds support for opening music in RVX Music using the in-app YouTube Music button. | 19.05.36 ~ 19.47.53 |
|
||||
| `Hook download actions` | Adds support to download videos with an external downloader app using the in-app download button. | 19.05.36 ~ 19.47.53 |
|
||||
| `MaterialYou` | Applies the MaterialYou theme for Android 12+ devices. | 19.05.36 ~ 19.47.53 |
|
||||
| `Miniplayer` | Adds options to change the in-app minimized player, and if patching target 19.16+ adds options to use modern miniplayers. | 19.05.36 ~ 19.47.53 |
|
||||
| `Navigation bar components` | Adds options to hide or change components related to the navigation bar. | 19.05.36 ~ 19.47.53 |
|
||||
| `Open links externally` | Adds an option to always open links in your browser instead of the in-app browser. | 19.05.36 ~ 19.47.53 |
|
||||
| `Overlay buttons` | Adds options to display useful overlay buttons in the video player. | 19.05.36 ~ 19.47.53 |
|
||||
| `Player components` | Adds options to hide or change components related to the video player. | 19.05.36 ~ 19.47.53 |
|
||||
| `Remove background playback restrictions` | Removes restrictions on background playback, including for music and kids videos. | 19.05.36 ~ 19.47.53 |
|
||||
| `Remove viewer discretion dialog` | Adds an option to remove the dialog that appears when opening a video that has been age-restricted by accepting it automatically. This does not bypass the age restriction. | 19.05.36 ~ 19.47.53 |
|
||||
| `Return YouTube Dislike` | Adds an option to show the dislike count of videos using the Return YouTube Dislike API. | 19.05.36 ~ 19.47.53 |
|
||||
| `Return YouTube Username` | Adds an option to replace YouTube handles with usernames in comments using YouTube Data API v3. | 19.05.36 ~ 19.47.53 |
|
||||
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 19.05.36 ~ 19.47.53 |
|
||||
| `Seekbar components` | Adds options to hide or change components related to the seekbar. | 19.05.36 ~ 19.47.53 |
|
||||
| `Settings for YouTube` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 19.05.36 ~ 19.47.53 |
|
||||
| `Shorts components` | Adds options to hide or change components related to YouTube Shorts. | 19.05.36 ~ 19.47.53 |
|
||||
| `Snack bar components` | Adds options to hide or change components related to the snack bar. | 19.05.36 ~ 19.47.53 |
|
||||
| `SponsorBlock` | Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as sponsored content. | 19.05.36 ~ 19.47.53 |
|
||||
| `Spoof app version` | Adds options to spoof the YouTube client version. This can be used to restore old UI elements and features. | 19.05.36 ~ 19.47.53 |
|
||||
| `Spoof streaming data` | Adds options to spoof the streaming data to allow playback. | 19.05.36 ~ 19.47.53 |
|
||||
| `Swipe controls` | Adds options for controlling volume and brightness with swiping, and whether to enter fullscreen when swiping down below the player. | 19.05.36 ~ 19.47.53 |
|
||||
| `Theme` | Changes the app's themes to the values specified in patch options. | 19.05.36 ~ 19.47.53 |
|
||||
| `Toolbar components` | Adds options to hide or change components located on the toolbar, such as the search bar, header, and toolbar buttons. | 19.05.36 ~ 19.47.53 |
|
||||
| `Translations for YouTube` | Add translations or remove string resources. | 19.05.36 ~ 19.47.53 |
|
||||
| `Video playback` | Adds options to customize settings related to video playback, such as default video quality and playback speed. | 19.05.36 ~ 19.47.53 |
|
||||
| `Visual preferences icons for YouTube` | Adds icons to specific preferences in the settings. | 19.05.36 ~ 19.47.53 |
|
||||
| `Watch history` | Adds an option to change the domain of the watch history or check its status. | 19.05.36 ~ 19.47.53 |
|
||||
</details>
|
||||
|
||||
### [📦 `com.google.android.apps.youtube.music`](https://play.google.com/store/apps/details?id=com.google.android.apps.youtube.music)
|
||||
@ -134,19 +134,19 @@ See the [documentation](https://github.com/inotia00/revanced-documentation#readm
|
||||
|
||||
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||
|:--------:|:--------------:|:-----------------:|
|
||||
| `Change package name` | Changes the package name for Reddit to the name specified in patch options. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Custom branding name for Reddit` | Changes the Reddit app name to the name specified in patch options. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Disable screenshot popup` | Adds an option to disable the popup that appears when taking a screenshot. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Hide Recently Visited shelf` | Adds an option to hide the Recently Visited shelf in the sidebar. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Hide ads` | Adds options to hide ads. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Hide navigation buttons` | Adds options to hide buttons in the navigation bar. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Hide recommended communities shelf` | Adds an option to hide the recommended communities shelves in subreddits. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Open links directly` | Adds an option to skip over redirection URLs in external links. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Open links externally` | Adds an option to always open links in your browser instead of in the in-app-browser. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Remove subreddit dialog` | Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 2024.17.0 ~ 2025.12.0 |
|
||||
| `Change package name` | Changes the package name for Reddit to the name specified in patch options. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Custom branding name for Reddit` | Changes the Reddit app name to the name specified in patch options. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Disable screenshot popup` | Adds an option to disable the popup that appears when taking a screenshot. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Hide Recently Visited shelf` | Adds an option to hide the Recently Visited shelf in the sidebar. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Hide ads` | Adds options to hide ads. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Hide navigation buttons` | Adds options to hide buttons in the navigation bar. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Hide recommended communities shelf` | Adds an option to hide the recommended communities shelves in subreddits. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Open links directly` | Adds an option to skip over redirection URLs in external links. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Open links externally` | Adds an option to always open links in your browser instead of in the in-app-browser. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Premium icon` | Unlocks premium app icons. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Remove subreddit dialog` | Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Sanitize sharing links` | Adds an option to sanitize sharing links by removing tracking query parameters. | 2024.17.0 ~ 2025.12.1 |
|
||||
| `Settings for Reddit` | Applies mandatory patches to implement ReVanced Extended settings into the application. | 2024.17.0 ~ 2025.12.1 |
|
||||
</details>
|
||||
|
||||
|
||||
@ -169,8 +169,7 @@ Example:
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -201,7 +200,7 @@ Example:
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
|
@ -2,7 +2,10 @@ package app.revanced.extension.reddit.patches;
|
||||
|
||||
import static app.revanced.extension.shared.utils.StringRef.str;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@ -34,6 +37,35 @@ public class RemoveSubRedditDialogPatch {
|
||||
return Settings.REMOVE_NSFW_DIALOG.get() || hasBeenVisited;
|
||||
}
|
||||
|
||||
public static void dismissNSFWDialog(Object customDialog) {
|
||||
if (Settings.REMOVE_NSFW_DIALOG.get() &&
|
||||
customDialog instanceof Dialog dialog) {
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.height = 0;
|
||||
params.width = 0;
|
||||
|
||||
// Change the size of dialog to 0.
|
||||
window.setAttributes(params);
|
||||
|
||||
// Disable dialog's background dim.
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
|
||||
// Hide DecorView.
|
||||
View decorView = window.getDecorView();
|
||||
decorView.setVisibility(View.GONE);
|
||||
|
||||
// Dismiss dialog.
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean removeNSFWDialog() {
|
||||
return Settings.REMOVE_NSFW_DIALOG.get();
|
||||
}
|
||||
|
||||
public static boolean spoofLoggedInStatus(boolean isLoggedIn) {
|
||||
return !Settings.REMOVE_NOTIFICATION_DIALOG.get() && isLoggedIn;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import app.revanced.extension.reddit.settings.Settings;
|
||||
@SuppressWarnings("unused")
|
||||
public class ScreenshotPopupPatch {
|
||||
|
||||
public static boolean disableScreenshotPopup() {
|
||||
return Settings.DISABLE_SCREENSHOT_POPUP.get();
|
||||
public static Boolean disableScreenshotPopup(Boolean original) {
|
||||
return Settings.DISABLE_SCREENSHOT_POPUP.get() ? Boolean.FALSE : original;
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,11 @@ class StreamingDataRequest private constructor(
|
||||
}
|
||||
|
||||
handleConnectionError(str("revanced_spoof_streaming_data_failed_forbidden"), null, true)
|
||||
handleConnectionError(str("revanced_spoof_streaming_data_failed_forbidden_suggestion"), null, true)
|
||||
handleConnectionError(
|
||||
str("revanced_spoof_streaming_data_failed_forbidden_suggestion"),
|
||||
null,
|
||||
true
|
||||
)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -183,8 +183,8 @@ public class PlayerPatch {
|
||||
// The type of descriptionView can be either ViewGroup or TextView. (A/B tests)
|
||||
// If the type of descriptionView is TextView, longer delay is required.
|
||||
final long delayMillis = descriptionView instanceof TextView
|
||||
? 500
|
||||
: 100;
|
||||
? 750
|
||||
: 200;
|
||||
|
||||
Utils.runOnMainThreadDelayed(() -> Utils.clickView(descriptionView), delayMillis);
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ public class PatchStatus {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modified by a patch. Do not touch.
|
||||
public static boolean OldSeekbarThumbnailsDefaultBoolean() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean OldSplashAnimation() {
|
||||
// Replace this with true if the Restore old splash animation (Custom branding icon) succeeds
|
||||
return false;
|
||||
@ -40,23 +45,22 @@ public class PatchStatus {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String SpoofAppVersionDefaultString() {
|
||||
return "18.17.43";
|
||||
}
|
||||
|
||||
public static boolean ToolBarComponents() {
|
||||
// Replace this with true if the Toolbar components patch succeeds
|
||||
return false;
|
||||
}
|
||||
|
||||
public static long PatchedTime() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public static String SpoofAppVersionDefaultString() {
|
||||
return "18.17.43";
|
||||
}
|
||||
|
||||
// Modified by a patch. Do not touch.
|
||||
public static String RVXMusicPackageName() {
|
||||
return "com.google.android.apps.youtube.music";
|
||||
}
|
||||
|
||||
// Modified by a patch. Do not touch.
|
||||
public static boolean OldSeekbarThumbnailsDefaultBoolean() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package app.revanced.extension.youtube.patches.utils;
|
||||
|
||||
import static app.revanced.extension.shared.utils.StringRef.str;
|
||||
import static app.revanced.extension.shared.utils.Utils.runOnMainThreadDelayed;
|
||||
import static app.revanced.extension.youtube.utils.VideoUtils.dismissPlayer;
|
||||
import static app.revanced.extension.youtube.utils.VideoUtils.launchVideoExternalDownloader;
|
||||
import static app.revanced.extension.youtube.utils.VideoUtils.openPlaylist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.KeyEvent;
|
||||
@ -30,30 +34,20 @@ import app.revanced.extension.youtube.patches.utils.requests.EditPlaylistRequest
|
||||
import app.revanced.extension.youtube.patches.utils.requests.GetPlaylistsRequest;
|
||||
import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
import app.revanced.extension.youtube.shared.VideoInformation;
|
||||
import app.revanced.extension.youtube.utils.AuthUtils;
|
||||
import app.revanced.extension.youtube.utils.ExtendedUtils;
|
||||
import app.revanced.extension.youtube.utils.VideoUtils;
|
||||
import kotlin.Pair;
|
||||
|
||||
// TODO: Implement sync queue and clean up code.
|
||||
@SuppressWarnings({"unused", "StaticFieldLeak"})
|
||||
public class PlaylistPatch extends 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"
|
||||
};
|
||||
public class PlaylistPatch extends AuthUtils {
|
||||
private static final boolean QUEUE_MANAGER =
|
||||
Settings.OVERLAY_BUTTON_EXTERNAL_DOWNLOADER_QUEUE_MANAGER.get()
|
||||
|| Settings.OVERRIDE_VIDEO_DOWNLOAD_BUTTON_QUEUE_MANAGER.get();
|
||||
|
||||
private static Context mContext;
|
||||
private static volatile String authorization = "";
|
||||
public static volatile String dataSyncId = "";
|
||||
public static volatile boolean isIncognito = false;
|
||||
private static volatile Map<String, String> requestHeader;
|
||||
private static volatile String playlistId = "";
|
||||
private static volatile String videoId = "";
|
||||
|
||||
private static String checkFailedAuth;
|
||||
private static String checkFailedPlaylistId;
|
||||
@ -124,6 +118,7 @@ public class PlaylistPatch extends VideoUtils {
|
||||
if (videoId != null) {
|
||||
lastVideoIds.remove(videoId, setVideoId);
|
||||
EditPlaylistRequest.clearVideoId(videoId);
|
||||
Logger.printDebug(() -> "Video removed by YouTube flyout menu: " + videoId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,30 +133,6 @@ public class PlaylistPatch extends VideoUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@ -182,9 +153,22 @@ public class PlaylistPatch extends VideoUtils {
|
||||
} else {
|
||||
videoId = currentVideoId;
|
||||
synchronized (lastVideoIds) {
|
||||
QueueManager[] customActionsEntries = playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null
|
||||
? QueueManager.addToQueueEntries
|
||||
: QueueManager.removeFromQueueEntries;
|
||||
QueueManager[] customActionsEntries;
|
||||
boolean canReload = PlayerType.getCurrent().isMaximizedOrFullscreen() &&
|
||||
lastVideoIds.get(VideoInformation.getVideoId()) != null;
|
||||
if (playlistId.isEmpty() || lastVideoIds.get(currentVideoId) == null) {
|
||||
if (canReload) {
|
||||
customActionsEntries = QueueManager.addToQueueWithReloadEntries;
|
||||
} else {
|
||||
customActionsEntries = QueueManager.addToQueueEntries;
|
||||
}
|
||||
} else {
|
||||
if (canReload) {
|
||||
customActionsEntries = QueueManager.removeFromQueueWithReloadEntries;
|
||||
} else {
|
||||
customActionsEntries = QueueManager.removeFromQueueEntries;
|
||||
}
|
||||
}
|
||||
|
||||
buildBottomSheetDialog(customActionsEntries);
|
||||
}
|
||||
@ -213,7 +197,8 @@ public class PlaylistPatch extends VideoUtils {
|
||||
ExtendedUtils.showBottomSheetDialog(mContext, mScrollView, actionsMap);
|
||||
}
|
||||
|
||||
private static void fetchQueue(boolean remove, boolean openPlaylist, boolean openVideo) {
|
||||
private static void fetchQueue(boolean remove, boolean openPlaylist,
|
||||
boolean openVideo, boolean reload) {
|
||||
try {
|
||||
String currentPlaylistId = playlistId;
|
||||
String currentVideoId = videoId;
|
||||
@ -233,7 +218,7 @@ public class PlaylistPatch extends VideoUtils {
|
||||
showToast(fetchSucceededCreate);
|
||||
Logger.printDebug(() -> "Queue successfully created, playlistId: " + createdPlaylistId + ", setVideoId: " + setVideoId);
|
||||
if (openPlaylist) {
|
||||
openQueue(currentVideoId, openVideo);
|
||||
openQueue(currentVideoId, openVideo, reload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -251,22 +236,24 @@ public class PlaylistPatch extends VideoUtils {
|
||||
String fetchedSetVideoId = request.getResult();
|
||||
Logger.printDebug(() -> "fetchedSetVideoId: " + fetchedSetVideoId);
|
||||
if (remove) { // Remove from queue.
|
||||
if (StringUtils.isEmpty(fetchedSetVideoId)) {
|
||||
if ("".equals(fetchedSetVideoId)) {
|
||||
lastVideoIds.remove(currentVideoId, setVideoId);
|
||||
EditPlaylistRequest.clearVideoId(currentVideoId);
|
||||
showToast(fetchSucceededRemove);
|
||||
if (openPlaylist) {
|
||||
openQueue(currentVideoId, openVideo);
|
||||
openQueue(currentVideoId, openVideo, reload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
showToast(fetchFailedRemove);
|
||||
} else { // Add to queue.
|
||||
if (StringUtils.isNotEmpty(fetchedSetVideoId)) {
|
||||
if (fetchedSetVideoId != null && !fetchedSetVideoId.isEmpty()) {
|
||||
lastVideoIds.putIfAbsent(currentVideoId, fetchedSetVideoId);
|
||||
EditPlaylistRequest.clearVideoId(currentVideoId);
|
||||
showToast(fetchSucceededAdd);
|
||||
Logger.printDebug(() -> "Video successfully added, setVideoId: " + fetchedSetVideoId);
|
||||
if (openPlaylist) {
|
||||
openQueue(currentVideoId, openVideo);
|
||||
openQueue(currentVideoId, openVideo, reload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -388,10 +375,10 @@ public class PlaylistPatch extends VideoUtils {
|
||||
}
|
||||
|
||||
private static void openQueue() {
|
||||
openQueue("", false);
|
||||
openQueue("", false, false);
|
||||
}
|
||||
|
||||
private static void openQueue(String currentVideoId, boolean openVideo) {
|
||||
private static void openQueue(String currentVideoId, boolean openVideo, boolean reload) {
|
||||
String currentPlaylistId = playlistId;
|
||||
if (currentPlaylistId.isEmpty()) {
|
||||
handleCheckError(checkFailedQueue);
|
||||
@ -403,7 +390,15 @@ public class PlaylistPatch extends VideoUtils {
|
||||
return;
|
||||
}
|
||||
// Open a video from a playlist
|
||||
openPlaylist(currentPlaylistId, currentVideoId);
|
||||
if (reload) {
|
||||
// Since the Queue is not automatically synced, a 'reload' action has been added as a workaround.
|
||||
// The 'reload' action simply closes the video and reopens it.
|
||||
// It is important to close the video, otherwise the Queue will not be updated.
|
||||
dismissPlayer();
|
||||
openPlaylist(currentPlaylistId, VideoInformation.getVideoId(), true);
|
||||
} else {
|
||||
openPlaylist(currentPlaylistId, currentVideoId);
|
||||
}
|
||||
} else {
|
||||
// Open a playlist
|
||||
openPlaylist(currentPlaylistId);
|
||||
@ -422,27 +417,37 @@ public class PlaylistPatch extends VideoUtils {
|
||||
ADD_TO_QUEUE(
|
||||
"revanced_queue_manager_add_to_queue",
|
||||
"yt_outline_list_add_black_24",
|
||||
() -> fetchQueue(false, false, false)
|
||||
() -> fetchQueue(false, false, false, false)
|
||||
),
|
||||
ADD_TO_QUEUE_AND_OPEN_QUEUE(
|
||||
"revanced_queue_manager_add_to_queue_and_open_queue",
|
||||
"yt_outline_list_add_black_24",
|
||||
() -> fetchQueue(false, true, false)
|
||||
() -> fetchQueue(false, true, false, false)
|
||||
),
|
||||
ADD_TO_QUEUE_AND_PLAY_VIDEO(
|
||||
"revanced_queue_manager_add_to_queue_and_play_video",
|
||||
"yt_outline_list_play_arrow_black_24",
|
||||
() -> fetchQueue(false, true, true)
|
||||
() -> fetchQueue(false, true, true, false)
|
||||
),
|
||||
ADD_TO_QUEUE_AND_RELOAD_VIDEO(
|
||||
"revanced_queue_manager_add_to_queue_and_reload_video",
|
||||
"yt_outline_arrow_circle_black_24",
|
||||
() -> fetchQueue(false, true, true, true)
|
||||
),
|
||||
REMOVE_FROM_QUEUE(
|
||||
"revanced_queue_manager_remove_from_queue",
|
||||
"yt_outline_trash_can_black_24",
|
||||
() -> fetchQueue(true, false, false)
|
||||
() -> fetchQueue(true, false, false, false)
|
||||
),
|
||||
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE(
|
||||
"revanced_queue_manager_remove_from_queue_and_open_queue",
|
||||
"yt_outline_trash_can_black_24",
|
||||
() -> fetchQueue(true, true, false)
|
||||
() -> fetchQueue(true, true, false, false)
|
||||
),
|
||||
REMOVE_FROM_QUEUE_AND_RELOAD_VIDEO(
|
||||
"revanced_queue_manager_remove_from_queue_and_reload_video",
|
||||
"yt_outline_arrow_circle_black_24",
|
||||
() -> fetchQueue(true, true, true, true)
|
||||
),
|
||||
OPEN_QUEUE(
|
||||
"revanced_queue_manager_open_queue",
|
||||
@ -490,6 +495,17 @@ public class PlaylistPatch extends VideoUtils {
|
||||
SAVE_QUEUE,
|
||||
};
|
||||
|
||||
public static final QueueManager[] addToQueueWithReloadEntries = {
|
||||
ADD_TO_QUEUE,
|
||||
ADD_TO_QUEUE_AND_OPEN_QUEUE,
|
||||
ADD_TO_QUEUE_AND_PLAY_VIDEO,
|
||||
ADD_TO_QUEUE_AND_RELOAD_VIDEO,
|
||||
OPEN_QUEUE,
|
||||
//REMOVE_QUEUE,
|
||||
EXTERNAL_DOWNLOADER,
|
||||
SAVE_QUEUE,
|
||||
};
|
||||
|
||||
public static final QueueManager[] removeFromQueueEntries = {
|
||||
REMOVE_FROM_QUEUE,
|
||||
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE,
|
||||
@ -499,6 +515,16 @@ public class PlaylistPatch extends VideoUtils {
|
||||
SAVE_QUEUE,
|
||||
};
|
||||
|
||||
public static final QueueManager[] removeFromQueueWithReloadEntries = {
|
||||
REMOVE_FROM_QUEUE,
|
||||
REMOVE_FROM_QUEUE_AND_OPEN_QUEUE,
|
||||
REMOVE_FROM_QUEUE_AND_RELOAD_VIDEO,
|
||||
OPEN_QUEUE,
|
||||
//REMOVE_QUEUE,
|
||||
EXTERNAL_DOWNLOADER,
|
||||
SAVE_QUEUE,
|
||||
};
|
||||
|
||||
public static final QueueManager[] noVideoIdQueueEntries = {
|
||||
OPEN_QUEUE,
|
||||
//REMOVE_QUEUE,
|
||||
|
@ -8,7 +8,6 @@ import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.EDIT_PLA
|
||||
import app.revanced.extension.shared.requests.Requester
|
||||
import app.revanced.extension.shared.utils.Logger
|
||||
import app.revanced.extension.shared.utils.Utils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
@ -216,7 +215,7 @@ class EditPlaylistRequest private constructor(
|
||||
dataSyncId,
|
||||
)
|
||||
if (json != null) {
|
||||
return parseResponse(json, StringUtils.isNotEmpty(setVideoId))
|
||||
return parseResponse(json, setVideoId != null && setVideoId.isNotEmpty())
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -3,10 +3,14 @@ package app.revanced.extension.youtube.patches.video;
|
||||
import static app.revanced.extension.shared.utils.StringRef.str;
|
||||
import static app.revanced.extension.youtube.shared.RootView.isShortsActive;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||
import app.revanced.extension.shared.settings.FloatSetting;
|
||||
import app.revanced.extension.shared.utils.Logger;
|
||||
@ -28,48 +32,61 @@ public class PlaybackSpeedPatch {
|
||||
Settings.DISABLE_DEFAULT_PLAYBACK_SPEED_MUSIC.get();
|
||||
private static final long TOAST_DELAY_MILLISECONDS = 750;
|
||||
private static long lastTimeSpeedChanged;
|
||||
|
||||
/**
|
||||
* The last used playback speed.
|
||||
* This value is used when the default playback speed is 'Auto'.
|
||||
*/
|
||||
private static float lastSelectedPlaybackSpeed = 1.0f;
|
||||
private static float lastSelectedShortsPlaybackSpeed = 1.0f;
|
||||
|
||||
private static volatile String channelId = "";
|
||||
private static volatile String videoId = "";
|
||||
private static boolean isLiveStream;
|
||||
/**
|
||||
* The last regular video id.
|
||||
*/
|
||||
private static String videoId = "";
|
||||
|
||||
private static volatile String channelIdShorts = "";
|
||||
private static volatile String videoIdShorts = "";
|
||||
private static boolean isLiveStreamShorts;
|
||||
@GuardedBy("itself")
|
||||
private static final Map<String, Float> ignoredPlaybackSpeedVideoIds = new LinkedHashMap<>() {
|
||||
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 3;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* This method is used to reset the playback speed to 1.0 when a general video is started, whether it is a live stream, music, or whitelist.
|
||||
*/
|
||||
public static void newVideoStarted(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
|
||||
@NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle,
|
||||
final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) {
|
||||
if (isShortsActive()) {
|
||||
channelIdShorts = newlyLoadedChannelId;
|
||||
videoIdShorts = newlyLoadedVideoId;
|
||||
isLiveStreamShorts = newlyLoadedLiveStreamValue;
|
||||
|
||||
Logger.printDebug(() -> "newVideoStarted: " + newlyLoadedVideoId);
|
||||
} else {
|
||||
channelId = newlyLoadedChannelId;
|
||||
videoId = newlyLoadedVideoId;
|
||||
isLiveStream = newlyLoadedLiveStreamValue;
|
||||
|
||||
Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (videoId.equals(newlyLoadedVideoId)) {
|
||||
return;
|
||||
}
|
||||
videoId = 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;
|
||||
boolean isMusic = isMusic(newlyLoadedVideoId);
|
||||
boolean isWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(newlyLoadedVideoId);
|
||||
|
||||
Logger.printDebug(() -> "newShortsVideoStarted: " + newlyLoadedVideoId);
|
||||
if (newlyLoadedLiveStreamValue || isMusic || isWhitelisted) {
|
||||
synchronized(ignoredPlaybackSpeedVideoIds) {
|
||||
if (!ignoredPlaybackSpeedVideoIds.containsKey(newlyLoadedVideoId)) {
|
||||
lastSelectedPlaybackSpeed = 1.0f;
|
||||
ignoredPlaybackSpeedVideoIds.put(newlyLoadedVideoId, lastSelectedPlaybackSpeed);
|
||||
|
||||
VideoInformation.setPlaybackSpeed(lastSelectedPlaybackSpeed);
|
||||
VideoInformation.overridePlaybackSpeed(lastSelectedPlaybackSpeed);
|
||||
|
||||
Logger.printDebug(() -> "changing playback speed to: 1.0, isLiveStream: " + newlyLoadedLiveStreamValue +
|
||||
", isMusic: " + isMusic + ", isWhitelisted: " + isWhitelisted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,32 +115,29 @@ public class PlaybackSpeedPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* This method is called every second for regular videos and Shorts.
|
||||
*/
|
||||
public static float getPlaybackSpeed(float playbackSpeed) {
|
||||
boolean isShorts = isShortsActive();
|
||||
String currentChannelId = isShorts ? channelIdShorts : channelId;
|
||||
String currentVideoId = isShorts ? videoIdShorts : videoId;
|
||||
boolean currentVideoIsLiveStream = isShorts ? isLiveStreamShorts : isLiveStream;
|
||||
boolean currentVideoIsWhitelisted = Whitelist.isChannelWhitelistedPlaybackSpeed(currentChannelId);
|
||||
boolean currentVideoIsMusic = !isShorts && isMusic();
|
||||
|
||||
if (currentVideoIsLiveStream || currentVideoIsWhitelisted || currentVideoIsMusic) {
|
||||
Logger.printDebug(() -> "changing playback speed to: 1.0");
|
||||
VideoInformation.setPlaybackSpeed(1.0f);
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float defaultPlaybackSpeed = isShorts ? DEFAULT_PLAYBACK_SPEED_SHORTS.get() : DEFAULT_PLAYBACK_SPEED.get();
|
||||
|
||||
if (defaultPlaybackSpeed < 0) {
|
||||
float finalPlaybackSpeed = isShorts ? playbackSpeed : lastSelectedPlaybackSpeed;
|
||||
if (defaultPlaybackSpeed < 0) { // If the default playback speed is 'Auto', it will be overridden to the last used playback speed.
|
||||
float finalPlaybackSpeed = isShorts ? lastSelectedShortsPlaybackSpeed : lastSelectedPlaybackSpeed;
|
||||
VideoInformation.overridePlaybackSpeed(finalPlaybackSpeed);
|
||||
Logger.printDebug(() -> "changing playback speed to: " + finalPlaybackSpeed);
|
||||
return finalPlaybackSpeed;
|
||||
} else {
|
||||
if (isShorts) {
|
||||
VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed);
|
||||
} else { // Otherwise the default playback speed is used.
|
||||
synchronized (ignoredPlaybackSpeedVideoIds) {
|
||||
if (isShorts) {
|
||||
// For Shorts, the VideoInformation.overridePlaybackSpeed() method is not used, so manually save the playback speed in VideoInformation.
|
||||
VideoInformation.setPlaybackSpeed(defaultPlaybackSpeed);
|
||||
} else if (ignoredPlaybackSpeedVideoIds.containsKey(videoId)) {
|
||||
// For general videos, check whether the default video playback speed should not be applied.
|
||||
Logger.printDebug(() -> "changing playback speed to: 1.0");
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "changing playback speed to: " + defaultPlaybackSpeed);
|
||||
return defaultPlaybackSpeed;
|
||||
}
|
||||
@ -138,6 +152,19 @@ public class PlaybackSpeedPatch {
|
||||
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
|
||||
try {
|
||||
boolean isShorts = isShortsActive();
|
||||
|
||||
// Saves the user-selected playback speed in the method.
|
||||
if (isShorts) {
|
||||
lastSelectedShortsPlaybackSpeed = playbackSpeed;
|
||||
} else {
|
||||
lastSelectedPlaybackSpeed = playbackSpeed;
|
||||
// If the user has manually changed the playback speed, the whitelist has already been applied.
|
||||
// If there is a videoId on the map, it will be removed.
|
||||
synchronized (ignoredPlaybackSpeedVideoIds) {
|
||||
ignoredPlaybackSpeedVideoIds.remove(videoId);
|
||||
}
|
||||
}
|
||||
|
||||
if (PatchStatus.RememberPlaybackSpeed()) {
|
||||
BooleanSetting rememberPlaybackSpeedLastSelectedSetting = isShorts
|
||||
? Settings.REMEMBER_PLAYBACK_SPEED_SHORTS_LAST_SELECTED
|
||||
@ -178,15 +205,23 @@ public class PlaybackSpeedPatch {
|
||||
}
|
||||
}, TOAST_DELAY_MILLISECONDS);
|
||||
}
|
||||
} else if (!isShorts){
|
||||
lastSelectedPlaybackSpeed = playbackSpeed;
|
||||
}
|
||||
} catch (Exception 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()) {
|
||||
try {
|
||||
MusicRequest request = MusicRequest.getRequestForVideoId(videoId);
|
||||
|
@ -637,31 +637,31 @@ public class Settings extends BaseSettings {
|
||||
public static final FloatSetting SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00");
|
||||
public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
|
||||
public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
|
||||
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
|
||||
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
|
||||
public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
|
||||
public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
|
||||
public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900");
|
||||
public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
|
||||
public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
|
||||
public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
|
||||
public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
|
||||
|
||||
// SB Setting not exported
|
||||
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
|
||||
|
@ -6,6 +6,8 @@ import static app.revanced.extension.shared.utils.Utils.isSDKAbove;
|
||||
import android.preference.Preference;
|
||||
import android.preference.SwitchPreference;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.youtube.patches.general.ChangeFormFactorPatch;
|
||||
import app.revanced.extension.youtube.patches.utils.PatchStatus;
|
||||
@ -44,6 +46,7 @@ public class ReVancedSettingsPreference extends ReVancedPreferenceFragment {
|
||||
AmbientModePreferenceLinks();
|
||||
FullScreenPanelPreferenceLinks();
|
||||
NavigationPreferenceLinks();
|
||||
PatchInformationPreferenceLinks();
|
||||
RYDPreferenceLinks();
|
||||
SeekBarPreferenceLinks();
|
||||
ShortsPreferenceLinks();
|
||||
@ -143,6 +146,26 @@ 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
|
||||
*/
|
||||
|
@ -37,7 +37,6 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockViewController
|
||||
|
||||
/**
|
||||
* Not thread safe. All fields/methods must be accessed from the main thread.
|
||||
*
|
||||
*/
|
||||
public class SponsorBlockUtils {
|
||||
private static final int LOCKED_COLOR = Color.parseColor("#FFC83D");
|
||||
|
@ -1,7 +1,36 @@
|
||||
package app.revanced.extension.youtube.sponsorblock.objects;
|
||||
|
||||
import static app.revanced.extension.shared.utils.StringRef.sf;
|
||||
import static app.revanced.extension.youtube.settings.Settings.*;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_FILLER_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_HIGHLIGHT_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTERACTION_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_INTRO_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_OUTRO_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_PREVIEW_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SELF_PROMO_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_SPONSOR_OPACITY;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED_COLOR;
|
||||
import static app.revanced.extension.youtube.settings.Settings.SB_CATEGORY_UNSUBMITTED_OPACITY;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
@ -56,7 +85,6 @@ public enum SegmentCategory {
|
||||
SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR, SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY),
|
||||
UNSUBMITTED("unsubmitted", StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"),
|
||||
SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR, SB_CATEGORY_UNSUBMITTED_OPACITY);
|
||||
;
|
||||
|
||||
private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
|
||||
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");
|
||||
|
@ -145,8 +145,10 @@ class SwipeControlsOverlayLayout(
|
||||
addView(feedbackTextView)
|
||||
// get icons scaled, assuming square icons
|
||||
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
|
||||
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
|
||||
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
|
||||
autoBrightnessIcon =
|
||||
getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
|
||||
manualBrightnessIcon =
|
||||
getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
|
||||
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
|
||||
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight)
|
||||
}
|
||||
@ -186,11 +188,18 @@ class SwipeControlsOverlayLayout(
|
||||
/**
|
||||
* Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
|
||||
*/
|
||||
private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
|
||||
private fun showFeedbackView(
|
||||
value: String,
|
||||
progress: Int,
|
||||
max: Int,
|
||||
icon: Drawable,
|
||||
isBrightness: Boolean
|
||||
) {
|
||||
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
||||
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
||||
|
||||
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
||||
val viewToShow =
|
||||
if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
||||
viewToShow.apply {
|
||||
setProgress(progress, max, value, isBrightness)
|
||||
this.icon = icon
|
||||
@ -241,7 +250,13 @@ class SwipeControlsOverlayLayout(
|
||||
brightnessValue < 75 -> highBrightnessIcon
|
||||
else -> fullBrightnessIcon
|
||||
}
|
||||
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
|
||||
showFeedbackView(
|
||||
"$brightnessValue%",
|
||||
brightnessValue,
|
||||
100,
|
||||
icon,
|
||||
isBrightness = true
|
||||
)
|
||||
} else {
|
||||
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
|
||||
}
|
||||
@ -274,7 +289,12 @@ abstract class AbstractProgressView(
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
// Combined paint creation function for both fill and stroke styles
|
||||
private fun createPaint(color: Int, style: Paint.Style = Paint.Style.FILL, strokeCap: Paint.Cap = Paint.Cap.BUTT, strokeWidth: Float = 0f) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
private fun createPaint(
|
||||
color: Int,
|
||||
style: Paint.Style = Paint.Style.FILL,
|
||||
strokeCap: Paint.Cap = Paint.Cap.BUTT,
|
||||
strokeWidth: Float = 0f
|
||||
) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
this.style = style
|
||||
this.color = color
|
||||
this.strokeCap = strokeCap
|
||||
@ -282,13 +302,18 @@ abstract class AbstractProgressView(
|
||||
}
|
||||
|
||||
// Initialize paints
|
||||
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
|
||||
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||
val progressPaint = createPaint(
|
||||
overlayProgressColor,
|
||||
style = Paint.Style.STROKE,
|
||||
strokeCap = Paint.Cap.ROUND,
|
||||
strokeWidth = 20f
|
||||
)
|
||||
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
||||
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = overlayTextColor
|
||||
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = overlayTextColor
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 40f // Can adjust based on need
|
||||
textSize = 40f // Can adjust based on need
|
||||
}
|
||||
|
||||
protected var progress = 0
|
||||
@ -337,11 +362,11 @@ class CircularProgressView(
|
||||
|
||||
init {
|
||||
textPaint.textSize = 40f // Override default text size for circular view
|
||||
progressPaint.strokeWidth = 20f
|
||||
progressPaint.strokeWidth = 20f
|
||||
fillBackgroundPaint.strokeWidth = 20f
|
||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.STROKE
|
||||
progressPaint.style = Paint.Style.STROKE
|
||||
fillBackgroundPaint.style = Paint.Style.STROKE
|
||||
}
|
||||
|
||||
@ -352,7 +377,12 @@ class CircularProgressView(
|
||||
rectF.set(20f, 20f, size - 20f, size - 20f)
|
||||
|
||||
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
|
||||
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
|
||||
canvas.drawCircle(
|
||||
width / 2f,
|
||||
height / 2f,
|
||||
size / 3,
|
||||
backgroundPaint
|
||||
) // Draw the inner circle.
|
||||
|
||||
// Select the paint for drawing based on whether it's brightness or volume.
|
||||
val sweepAngle = (progress.toFloat() / maxProgress) * 360
|
||||
@ -399,13 +429,13 @@ class HorizontalProgressView(
|
||||
) {
|
||||
|
||||
private val iconSize = 60f
|
||||
private val padding = 40f
|
||||
private val padding = 40f
|
||||
|
||||
init {
|
||||
textPaint.textSize = 36f // Override default text size for horizontal view
|
||||
textPaint.textSize = 36f // Override default text size for horizontal view
|
||||
progressPaint.strokeWidth = 0f
|
||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.FILL
|
||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.FILL
|
||||
fillBackgroundPaint.style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
@ -428,7 +458,15 @@ class HorizontalProgressView(
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||
} else {
|
||||
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||
canvas.drawRoundRect(
|
||||
minimalStartX,
|
||||
0f,
|
||||
minimalStartX + minimalElementWidth,
|
||||
height,
|
||||
cornerRadius,
|
||||
cornerRadius,
|
||||
backgroundPaint
|
||||
)
|
||||
}
|
||||
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
@ -466,7 +504,12 @@ class HorizontalProgressView(
|
||||
padding + minimalStartX
|
||||
}
|
||||
val iconY = height / 2 - iconSize / 2
|
||||
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
|
||||
it.setBounds(
|
||||
iconX.toInt(),
|
||||
iconY.toInt(),
|
||||
(iconX + iconSize).toInt(),
|
||||
(iconY + iconSize).toInt()
|
||||
)
|
||||
it.draw(canvas)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -133,13 +133,25 @@ public class VideoUtils extends IntentUtils {
|
||||
}
|
||||
|
||||
public static void openPlaylist(@NonNull String playlistId, @NonNull String videoId) {
|
||||
openPlaylist(playlistId, videoId, false);
|
||||
}
|
||||
|
||||
public static void openPlaylist(@NonNull String playlistId, @NonNull String videoId, boolean withTimestamp) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (videoId.isEmpty()) {
|
||||
sb.append(getPlaylistUrl(playlistId));
|
||||
} else {
|
||||
sb.append(getVideoScheme(videoId, false));
|
||||
sb.append("&list=");
|
||||
sb.append(VIDEO_URL);
|
||||
sb.append(videoId);
|
||||
sb.append("?list=");
|
||||
sb.append(playlistId);
|
||||
if (withTimestamp) {
|
||||
final long currentVideoTimeInSeconds = VideoInformation.getVideoTimeInSeconds();
|
||||
if (currentVideoTimeInSeconds > 0) {
|
||||
sb.append("&t=");
|
||||
sb.append(currentVideoTimeInSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
launchView(sb.toString(), getContext().getPackageName());
|
||||
}
|
||||
@ -269,6 +281,13 @@ public class VideoUtils extends IntentUtils {
|
||||
return !isExternalDownloaderLaunched.get() && original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest of the implementation added by patch.
|
||||
*/
|
||||
public static void dismissPlayer() {
|
||||
Logger.printDebug(() -> "Dismiss player");
|
||||
}
|
||||
|
||||
/**
|
||||
* Rest of the implementation added by patch.
|
||||
*/
|
||||
|
@ -4,5 +4,5 @@ org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
kotlin.jvm.target.validation.mode = IGNORE
|
||||
version = 5.6.1-dev.5
|
||||
version = 5.6.2
|
||||
|
||||
|
251
patches.json
251
patches.json
@ -15,8 +15,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -35,8 +34,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -75,8 +73,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -117,8 +114,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -157,8 +153,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -179,8 +174,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -194,7 +188,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -226,8 +220,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -270,8 +263,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -311,8 +303,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -352,8 +343,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -389,8 +379,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -496,7 +485,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -527,8 +516,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -611,8 +599,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -640,8 +627,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -760,8 +746,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -844,8 +829,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -893,8 +877,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -935,8 +918,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -954,8 +936,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -973,8 +954,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1015,8 +995,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1035,8 +1014,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1052,7 +1030,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1071,8 +1049,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1113,8 +1090,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1153,8 +1129,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1172,8 +1147,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1241,8 +1215,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1267,8 +1240,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1337,6 +1309,15 @@
|
||||
"Clone": "com.rvx.android.apps.youtube.music",
|
||||
"Default": "app.rvx.android.apps.youtube.music"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "patchAllManifest",
|
||||
"title": "Patch all manifest components",
|
||||
"description": "Patch all permissions, intents and content provider authorities supported by GmsCore.",
|
||||
"required": true,
|
||||
"type": "kotlin.Boolean",
|
||||
"default": true,
|
||||
"values": null
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1354,8 +1335,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -1402,6 +1382,15 @@
|
||||
"Clone": "com.rvx.android.apps.youtube.music",
|
||||
"Default": "app.rvx.android.apps.youtube.music"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "patchAllManifest",
|
||||
"title": "Patch all manifest components",
|
||||
"description": "Patch all permissions, intents and content provider authorities supported by GmsCore.",
|
||||
"required": true,
|
||||
"type": "kotlin.Boolean",
|
||||
"default": true,
|
||||
"values": null
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1416,7 +1405,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1434,8 +1423,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1453,8 +1441,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1523,8 +1510,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1568,7 +1554,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1591,8 +1577,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1613,8 +1598,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1639,8 +1623,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1659,8 +1642,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1708,8 +1690,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1725,7 +1706,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1769,8 +1750,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1792,8 +1772,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1828,7 +1807,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1847,8 +1826,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -1903,8 +1881,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1925,8 +1902,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1945,8 +1921,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -1966,8 +1941,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2014,8 +1988,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2032,7 +2005,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2049,7 +2022,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2068,8 +2041,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2093,8 +2065,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -2193,8 +2164,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2208,7 +2178,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2249,8 +2219,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2260,13 +2229,14 @@
|
||||
"description": "Adds options to remove the NSFW community warning and notifications suggestion dialogs by dismissing them automatically.",
|
||||
"use": true,
|
||||
"dependencies": [
|
||||
"Settings for Reddit"
|
||||
"Settings for Reddit",
|
||||
"ResourcePatch"
|
||||
],
|
||||
"compatiblePackages": {
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2307,8 +2277,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2373,8 +2342,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2416,8 +2384,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2455,7 +2422,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2474,8 +2441,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2499,8 +2465,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2517,7 +2482,7 @@
|
||||
"com.reddit.frontpage": [
|
||||
"2024.17.0",
|
||||
"2025.05.1",
|
||||
"2025.12.0"
|
||||
"2025.12.1"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -2552,8 +2517,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -2563,7 +2527,7 @@
|
||||
"description": "The settings menu name that the RVX settings menu should be above.",
|
||||
"required": true,
|
||||
"type": "kotlin.String",
|
||||
"default": "@string/about_key",
|
||||
"default": "@string/parent_tools_key",
|
||||
"values": {
|
||||
"Parent settings": "@string/parent_tools_key",
|
||||
"General": "@string/general_key",
|
||||
@ -2664,8 +2628,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2684,8 +2647,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -2797,8 +2759,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -2859,8 +2820,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2905,8 +2865,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -2939,8 +2898,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -2959,8 +2917,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -3020,8 +2977,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -3039,8 +2995,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -3159,6 +3114,7 @@
|
||||
"BytecodePatch",
|
||||
"BytecodePatch",
|
||||
"BytecodePatch",
|
||||
"BytecodePatch",
|
||||
"ResourcePatch"
|
||||
],
|
||||
"compatiblePackages": {
|
||||
@ -3167,8 +3123,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
@ -3186,8 +3141,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
@ -3291,8 +3245,7 @@
|
||||
"19.16.39",
|
||||
"19.43.41",
|
||||
"19.44.39",
|
||||
"19.47.53",
|
||||
"20.03.43"
|
||||
"19.47.53"
|
||||
]
|
||||
},
|
||||
"options": []
|
||||
|
@ -458,6 +458,10 @@ public final class app/revanced/patches/reddit/utils/extension/SharedExtensionPa
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/reddit/utils/resourceid/SharedResourceIdPatchKt {
|
||||
public static final fun getNsfwDialogTitle ()J
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/reddit/utils/settings/SettingsPatchKt {
|
||||
public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
public static final fun is_2024_26_or_greater ()Z
|
||||
@ -891,6 +895,10 @@ public final class app/revanced/patches/youtube/utils/FingerprintsKt {
|
||||
public static final fun indexOfSpannedCharSequenceInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/utils/auth/AuthHookPatchKt {
|
||||
public static final fun getAuthHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/utils/bottomsheet/BottomSheetHookPatchKt {
|
||||
public static final fun getBottomSheetHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@ -903,6 +911,10 @@ public final class app/revanced/patches/youtube/utils/controlsoverlay/ControlsOv
|
||||
public static final fun getControlsOverlayConfigPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/utils/dismiss/DismissPlayerHookPatchKt {
|
||||
public static final fun getDismissPlayerHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatchKt {
|
||||
public static final fun getEngagementPanelHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@ -1050,6 +1062,7 @@ public final class app/revanced/patches/youtube/utils/playservice/VersionCheckPa
|
||||
public static final fun is_19_34_or_greater ()Z
|
||||
public static final fun is_19_36_or_greater ()Z
|
||||
public static final fun is_19_41_or_greater ()Z
|
||||
public static final fun is_19_42_or_greater ()Z
|
||||
public static final fun is_19_43_or_greater ()Z
|
||||
public static final fun is_19_44_or_greater ()Z
|
||||
public static final fun is_19_46_or_greater ()Z
|
||||
|
@ -152,7 +152,10 @@ private enum class MethodCall(
|
||||
RegisterNetworkCallback1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"registerNetworkCallback",
|
||||
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;"
|
||||
),
|
||||
"V",
|
||||
),
|
||||
RegisterNetworkCallback2(
|
||||
@ -174,13 +177,20 @@ private enum class MethodCall(
|
||||
RequestNetwork1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"requestNetwork",
|
||||
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;"
|
||||
),
|
||||
"V",
|
||||
),
|
||||
RequestNetwork2(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"requestNetwork",
|
||||
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
"I"
|
||||
),
|
||||
"V",
|
||||
),
|
||||
RequestNetwork3(
|
||||
|
@ -19,7 +19,8 @@ val edgeToEdgeDisplayPatch = resourcePatch(
|
||||
// Instead, it checks compileSdkVersion and prints a warning.
|
||||
try {
|
||||
val manifestElement = document.getNode("manifest") as Element
|
||||
val compileSdkVersion = Integer.parseInt(manifestElement.getAttribute("android:compileSdkVersion"))
|
||||
val compileSdkVersion =
|
||||
Integer.parseInt(manifestElement.getAttribute("android:compileSdkVersion"))
|
||||
if (compileSdkVersion < 35) {
|
||||
printWarn("This app may not be forcing edge to edge display (compileSdkVersion: $compileSdkVersion)")
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ val accountComponentsPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// account switcher
|
||||
val textViewField = with (
|
||||
val textViewField = with(
|
||||
channelHandleFingerprint
|
||||
.methodOrThrow(namesInactiveAccountThumbnailSizeFingerprint)
|
||||
) {
|
||||
@ -117,7 +117,8 @@ val accountComponentsPatch = bytecodePatch(
|
||||
.forEach { index ->
|
||||
val insertIndex = index - 1
|
||||
if (!hook && getInstruction(insertIndex).opcode == Opcode.IF_NEZ) {
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
val insertRegister =
|
||||
getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex, """
|
||||
|
@ -115,7 +115,8 @@ val adsPatch = bytecodePatch(
|
||||
.methodOrThrow(getPremiumDialogParentFingerprint)
|
||||
.apply {
|
||||
val setContentViewIndex = indexOfSetContentViewInstruction(this)
|
||||
val dialogInstruction = getInstruction<FiveRegisterInstruction>(setContentViewIndex)
|
||||
val dialogInstruction =
|
||||
getInstruction<FiveRegisterInstruction>(setContentViewIndex)
|
||||
val dialogRegister = dialogInstruction.registerC
|
||||
val viewRegister = dialogInstruction.registerD
|
||||
|
||||
|
@ -206,7 +206,8 @@ val changeHeaderPatch = resourcePatch(
|
||||
printWarn(warnings)
|
||||
}
|
||||
|
||||
val isLegacyLogoExists = get("res").resolve("drawable-xxhdpi").resolve("ytm_logo.png").exists()
|
||||
val isLegacyLogoExists =
|
||||
get("res").resolve("drawable-xxhdpi").resolve("ytm_logo.png").exists()
|
||||
if (is_7_27_or_greater && isLegacyLogoExists) {
|
||||
document("res/layout/signin_fragment.xml").use { document ->
|
||||
document.doRecursively node@{ node ->
|
||||
|
@ -1,13 +1,13 @@
|
||||
package app.revanced.patches.music.misc.watchhistory
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.trackingurlhook.hookWatchHistory
|
||||
import app.revanced.patches.shared.trackingurlhook.trackingUrlHookPatch
|
||||
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.music.utils.patch.PatchList.WATCH_HISTORY
|
||||
import app.revanced.patches.music.utils.settings.CategoryType
|
||||
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
|
||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.patches.shared.trackingurlhook.hookWatchHistory
|
||||
import app.revanced.patches.shared.trackingurlhook.trackingUrlHookPatch
|
||||
|
||||
@Suppress("unused")
|
||||
val watchHistoryPatch = bytecodePatch(
|
||||
|
@ -124,14 +124,17 @@ val navigationBarComponentsPatch = bytecodePatch(
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/String;"
|
||||
}
|
||||
val browseIdReference = getInstruction<ReferenceInstruction>(browseIdIndex).reference as FieldReference
|
||||
val browseIdReference =
|
||||
getInstruction<ReferenceInstruction>(browseIdIndex).reference as FieldReference
|
||||
val fieldName = browseIdReference.name
|
||||
val componentIndex = indexOfFirstInstructionOrThrow(stringIndex) {
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
getReference<FieldReference>()?.toString() == browseIdReference.toString()
|
||||
}
|
||||
val browseIdRegister = getInstruction<TwoRegisterInstruction>(componentIndex).registerA
|
||||
val componentRegister = getInstruction<TwoRegisterInstruction>(componentIndex).registerB
|
||||
val browseIdRegister =
|
||||
getInstruction<TwoRegisterInstruction>(componentIndex).registerA
|
||||
val componentRegister =
|
||||
getInstruction<TwoRegisterInstruction>(componentIndex).registerB
|
||||
|
||||
val enumIndex = it.patternMatch!!.startIndex + 3
|
||||
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
|
||||
|
@ -54,7 +54,7 @@ internal val engagementPanelHeightFingerprint = legacyFingerprint(
|
||||
parameters = emptyList(),
|
||||
customFingerprint = { method, _ ->
|
||||
AccessFlags.FINAL.isSet(method.accessFlags) &&
|
||||
method.containsLiteralInstruction(1) &&
|
||||
method.containsLiteralInstruction(1) &&
|
||||
method.indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>()?.name == "booleanValue"
|
||||
|
@ -747,7 +747,8 @@ val playerComponentsPatch = bytecodePatch(
|
||||
getInstruction<FiveRegisterInstruction>(bottomSheetBehaviorIndex).registerD
|
||||
|
||||
val getFieldIndex = bottomSheetBehaviorIndex - 2
|
||||
val getFieldReference = getInstruction<ReferenceInstruction>(getFieldIndex).reference
|
||||
val getFieldReference =
|
||||
getInstruction<ReferenceInstruction>(getFieldIndex).reference
|
||||
val getFieldInstruction = getInstruction<TwoRegisterInstruction>(getFieldIndex)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
|
@ -22,7 +22,6 @@ import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
||||
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
|
||||
import app.revanced.patches.music.utils.settings.addSwitchPreference
|
||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.patches.shared.spoof.blockrequest.blockRequestPatch
|
||||
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
|
||||
import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch
|
||||
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
||||
@ -31,6 +30,7 @@ import app.revanced.patches.shared.indexOfBrandInstruction
|
||||
import app.revanced.patches.shared.indexOfManufacturerInstruction
|
||||
import app.revanced.patches.shared.indexOfModelInstruction
|
||||
import app.revanced.patches.shared.indexOfReleaseInstruction
|
||||
import app.revanced.patches.shared.spoof.blockrequest.blockRequestPatch
|
||||
import app.revanced.util.findMethodOrThrow
|
||||
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
||||
import app.revanced.util.fingerprint.matchOrThrow
|
||||
|
@ -142,7 +142,8 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
endButtonsContainer = getResourceId(ID, "end_buttons_container")
|
||||
floatingLayout = getResourceId(ID, "floating_layout")
|
||||
historyMenuItem = getResourceId(ID, "history_menu_item")
|
||||
inlineTimeBarAdBreakMarkerColor = getResourceId(COLOR, "inline_time_bar_ad_break_marker_color")
|
||||
inlineTimeBarAdBreakMarkerColor =
|
||||
getResourceId(COLOR, "inline_time_bar_ad_break_marker_color")
|
||||
inlineTimeBarProgressColor = getResourceId(COLOR, "inline_time_bar_progress_color")
|
||||
interstitialsContainer = getResourceId(ID, "interstitials_container")
|
||||
isTablet = getResourceId(BOOL, "is_tablet")
|
||||
@ -156,7 +157,8 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
modernDialogBackground = getResourceId(DRAWABLE, "modern_dialog_background")
|
||||
musicNotifierShelf = getResourceId(LAYOUT, "music_notifier_shelf")
|
||||
musicTasteBuilderShelf = getResourceId(LAYOUT, "music_tastebuilder_shelf")
|
||||
namesInactiveAccountThumbnailSize = getResourceId(DIMEN, "names_inactive_account_thumbnail_size")
|
||||
namesInactiveAccountThumbnailSize =
|
||||
getResourceId(DIMEN, "names_inactive_account_thumbnail_size")
|
||||
offlineSettingsMenuItem = getResourceId(ID, "offline_settings_menu_item")
|
||||
playerOverlayChip = getResourceId(ID, "player_overlay_chip")
|
||||
playerViewPager = getResourceId(ID, "player_view_pager")
|
||||
|
@ -25,7 +25,8 @@ val videoTypeHookPatch = bytecodePatch(
|
||||
|
||||
videoTypeFingerprint.methodOrThrow(videoTypeParentFingerprint).apply {
|
||||
val getEnumIndex = indexOfGetEnumInstruction(this)
|
||||
val enumClass = (getInstruction<ReferenceInstruction>(getEnumIndex).reference as MethodReference).definingClass
|
||||
val enumClass =
|
||||
(getInstruction<ReferenceInstruction>(getEnumIndex).reference as MethodReference).definingClass
|
||||
val referenceIndex = indexOfFirstInstructionOrThrow(getEnumIndex) {
|
||||
opcode == Opcode.SGET_OBJECT &&
|
||||
getReference<FieldReference>()?.type == enumClass
|
||||
|
@ -71,7 +71,8 @@ val playerResponseMethodHookPatch = bytecodePatch(
|
||||
val beforeVideoIdHooks =
|
||||
hooks.filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed()
|
||||
val videoIdHooks = hooks.filterIsInstance<Hook.VideoId>().asReversed()
|
||||
val videoIdAndPlaylistIdHooks = hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
|
||||
val videoIdAndPlaylistIdHooks =
|
||||
hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
|
||||
val afterVideoIdHooks = hooks.filterIsInstance<Hook.PlayerParameter>().asReversed()
|
||||
|
||||
// Add the hooks in this specific order as they insert instructions at the beginning of the method.
|
||||
|
@ -56,7 +56,8 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
if (bottomNavScreenFingerprint.resolvable()) {
|
||||
val bottomNavScreenMutableClass = with(bottomNavScreenFingerprint.methodOrThrow()) {
|
||||
val startIndex = indexOfGetDimensionPixelSizeInstruction(this)
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(startIndex, Opcode.NEW_INSTANCE)
|
||||
val targetIndex =
|
||||
indexOfFirstInstructionOrThrow(startIndex, Opcode.NEW_INSTANCE)
|
||||
val targetReference =
|
||||
getInstruction<ReferenceInstruction>(targetIndex).reference.toString()
|
||||
|
||||
@ -65,7 +66,9 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
?: throw ClassNotFoundException("Failed to find class $targetReference")
|
||||
}
|
||||
|
||||
bottomNavScreenOnGlobalLayoutFingerprint.second.matchOrNull(bottomNavScreenMutableClass)
|
||||
bottomNavScreenOnGlobalLayoutFingerprint.second.matchOrNull(
|
||||
bottomNavScreenMutableClass
|
||||
)
|
||||
?.let {
|
||||
it.method.apply {
|
||||
val startIndex = it.patternMatch!!.startIndex
|
||||
@ -82,7 +85,8 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
// Legacy method.
|
||||
bottomNavScreenHandlerFingerprint.methodOrThrow().apply {
|
||||
val targetIndex = indexOfGetItemsInstruction(this) + 1
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
val targetRegister =
|
||||
getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex + 1, """
|
||||
|
@ -1,15 +0,0 @@
|
||||
package app.revanced.patches.reddit.layout.screenshotpopup
|
||||
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val screenshotBannerContainerFingerprint = legacyFingerprint(
|
||||
name = "screenshotTakenBannerFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
strings = listOf(
|
||||
"bannerContainer",
|
||||
"scope",
|
||||
)
|
||||
)
|
@ -1,31 +1,23 @@
|
||||
package app.revanced.patches.reddit.layout.screenshotpopup
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
|
||||
import app.revanced.patches.reddit.utils.patch.PatchList.DISABLE_SCREENSHOT_POPUP
|
||||
import app.revanced.patches.reddit.utils.settings.settingsPatch
|
||||
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
|
||||
import app.revanced.util.findMutableMethodOf
|
||||
import app.revanced.util.fingerprint.methodCall
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstStringInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_METHOD_DESCRIPTOR =
|
||||
"$PATCHES_PATH/ScreenshotPopupPatch;->disableScreenshotPopup()Z"
|
||||
|
||||
@Suppress("unused")
|
||||
val screenshotPopupPatch = bytecodePatch(
|
||||
DISABLE_SCREENSHOT_POPUP.title,
|
||||
@ -37,68 +29,57 @@ val screenshotPopupPatch = bytecodePatch(
|
||||
|
||||
execute {
|
||||
|
||||
val screenshotTriggerSharingListenerMethodCall =
|
||||
screenshotBannerContainerFingerprint.methodCall()
|
||||
|
||||
fun indexOfScreenshotTriggerInstruction(method: Method) =
|
||||
fun indexOfShowBannerInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<MethodReference>()?.toString() == screenshotTriggerSharingListenerMethodCall
|
||||
val reference = getReference<FieldReference>()
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
reference?.name?.contains("shouldShowBanner") == true &&
|
||||
reference.definingClass.startsWith("Lcom/reddit/sharing/screenshot/") == true
|
||||
}
|
||||
|
||||
val isScreenshotTriggerMethod: Method.() -> Boolean = {
|
||||
indexOfScreenshotTriggerInstruction(this) >= 0
|
||||
fun indexOfSetValueInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<MethodReference>()?.name == "setValue"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
fun MutableMethod.hook() {
|
||||
if (returnType == "V") {
|
||||
addInstructionsWithLabels(
|
||||
0, """
|
||||
invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR
|
||||
move-result v0
|
||||
if-eqz v0, :shown
|
||||
return-void
|
||||
""", ExternalLabel("shown", getInstruction(0))
|
||||
)
|
||||
|
||||
hookCount++
|
||||
} else if (returnType.startsWith("L")) { // Reddit 2025.06+
|
||||
val insertIndex =
|
||||
indexOfFirstStringInstruction("screenshotTriggerSharingListener")
|
||||
|
||||
if (insertIndex >= 0) {
|
||||
val insertRegister =
|
||||
getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
val triggerIndex =
|
||||
indexOfScreenshotTriggerInstruction(this)
|
||||
val jumpIndex =
|
||||
indexOfFirstInstructionOrThrow(triggerIndex, Opcode.RETURN_OBJECT)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex, """
|
||||
invoke-static {}, $EXTENSION_METHOD_DESCRIPTOR
|
||||
move-result v$insertRegister
|
||||
if-nez v$insertRegister, :hidden
|
||||
""", ExternalLabel("hidden", getInstruction(jumpIndex))
|
||||
)
|
||||
|
||||
hookCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screenshotBannerContainerFingerprint
|
||||
.methodOrThrow()
|
||||
.hook()
|
||||
|
||||
classes.forEach { classDef ->
|
||||
classDef.methods.forEach { method ->
|
||||
if (method.isScreenshotTriggerMethod()) {
|
||||
if (method.isScreenShotMethod()) {
|
||||
proxy(classDef)
|
||||
.mutableClass
|
||||
.findMutableMethodOf(method)
|
||||
.hook()
|
||||
.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++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.reddit.layout.subredditdialog
|
||||
|
||||
import app.revanced.patches.reddit.utils.resourceid.nsfwDialogTitle
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
@ -71,6 +72,14 @@ fun indexOfHasBeenVisitedInstruction(method: Method) =
|
||||
reference.returnType == "Z"
|
||||
}
|
||||
|
||||
internal val nsfwAlertBuilderFingerprint = legacyFingerprint(
|
||||
name = "nsfwAlertBuilderFingerprint",
|
||||
literals = listOf(nsfwDialogTitle),
|
||||
customFingerprint = { method, _ ->
|
||||
method.definingClass.startsWith("Lcom/reddit/screen/nsfw")
|
||||
}
|
||||
)
|
||||
|
||||
internal val redditAlertDialogsFingerprint = legacyFingerprint(
|
||||
name = "redditAlertDialogsFingerprint",
|
||||
returnType = "V",
|
||||
|
@ -3,10 +3,12 @@ package app.revanced.patches.reddit.layout.subredditdialog
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.reddit.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.reddit.utils.extension.Constants.PATCHES_PATH
|
||||
import app.revanced.patches.reddit.utils.patch.PatchList.REMOVE_SUBREDDIT_DIALOG
|
||||
import app.revanced.patches.reddit.utils.resourceid.sharedResourceIdPatch
|
||||
import app.revanced.patches.reddit.utils.settings.is_2024_41_or_greater
|
||||
import app.revanced.patches.reddit.utils.settings.is_2025_01_or_greater
|
||||
import app.revanced.patches.reddit.utils.settings.is_2025_05_or_greater
|
||||
@ -14,7 +16,9 @@ import app.revanced.patches.reddit.utils.settings.is_2025_06_or_greater
|
||||
import app.revanced.patches.reddit.utils.settings.settingsPatch
|
||||
import app.revanced.patches.reddit.utils.settings.updatePatchStatus
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.fingerprint.mutableClassOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
@ -32,7 +36,10 @@ val subRedditDialogPatch = bytecodePatch(
|
||||
) {
|
||||
compatibleWith(COMPATIBLE_PACKAGE)
|
||||
|
||||
dependsOn(settingsPatch)
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
sharedResourceIdPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@ -42,7 +49,8 @@ val subRedditDialogPatch = bytecodePatch(
|
||||
.apply {
|
||||
listOfIsLoggedInInstruction(this)
|
||||
.forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
val register =
|
||||
getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 2, """
|
||||
@ -81,6 +89,32 @@ 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.
|
||||
|
@ -11,7 +11,7 @@ internal object Constants {
|
||||
setOf(
|
||||
"2024.17.0", // This is the last version that can be patched without anti-split.
|
||||
"2025.05.1", // This was the latest version supported by the previous RVX patch.
|
||||
"2025.12.0", // This is the latest version supported by the RVX patch.
|
||||
"2025.12.1", // This is the latest version supported by the RVX patch.
|
||||
)
|
||||
)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package app.revanced.patches.reddit.utils.resourceid
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.shared.mapping.ResourceType.STRING
|
||||
import app.revanced.patches.shared.mapping.getResourceId
|
||||
import app.revanced.patches.shared.mapping.resourceMappingPatch
|
||||
|
||||
var nsfwDialogTitle = -1L
|
||||
private set
|
||||
|
||||
internal val sharedResourceIdPatch = resourcePatch(
|
||||
description = "sharedResourceIdPatch"
|
||||
) {
|
||||
dependsOn(resourceMappingPatch)
|
||||
|
||||
execute {
|
||||
nsfwDialogTitle = getResourceId(STRING, "nsfw_dialog_title")
|
||||
}
|
||||
}
|
@ -62,7 +62,7 @@ fun baseAdsPatch(
|
||||
)
|
||||
}
|
||||
|
||||
val getAdvertisingIdMethod = with (advertisingIdFingerprint.methodOrThrow()) {
|
||||
val getAdvertisingIdMethod = with(advertisingIdFingerprint.methodOrThrow()) {
|
||||
val getAdvertisingIdIndex = indexOfGetAdvertisingIdInstruction(this)
|
||||
getWalkerMethod(getAdvertisingIdIndex)
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
@ -108,3 +106,8 @@ internal val primesLifecycleEventFingerprint = legacyFingerprint(
|
||||
} >= 0
|
||||
}
|
||||
)
|
||||
|
||||
internal val primeMethodFingerprint = legacyFingerprint(
|
||||
name = "primesLifecycleEventFingerprint",
|
||||
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
|
||||
)
|
@ -4,6 +4,7 @@ import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
@ -18,9 +19,13 @@ import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
||||
import app.revanced.patches.shared.gms.Constants.ACTIONS
|
||||
import app.revanced.patches.shared.gms.Constants.ACTIONS_LEGACY
|
||||
import app.revanced.patches.shared.gms.Constants.AUTHORITIES
|
||||
import app.revanced.patches.shared.gms.Constants.AUTHORITIES_LEGACY
|
||||
import app.revanced.patches.shared.gms.Constants.PERMISSIONS
|
||||
import app.revanced.patches.shared.gms.Constants.PERMISSIONS_LEGACY
|
||||
import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.fingerprint.methodOrNull
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.fingerprint.mutableClassOrThrow
|
||||
import app.revanced.util.getReference
|
||||
@ -83,9 +88,9 @@ fun gmsCoreSupportPatch(
|
||||
key = "gmsCoreVendorGroupId",
|
||||
default = "app.revanced",
|
||||
values =
|
||||
mapOf(
|
||||
"ReVanced" to "app.revanced",
|
||||
),
|
||||
mapOf(
|
||||
"ReVanced" to "app.revanced",
|
||||
),
|
||||
title = "GmsCore vendor group ID",
|
||||
description = "The vendor's group ID for GmsCore.",
|
||||
required = true,
|
||||
@ -127,6 +132,14 @@ fun gmsCoreSupportPatch(
|
||||
required = true
|
||||
) { it!!.matches(Regex(PACKAGE_NAME_REGEX_PATTERN)) && it != ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC }
|
||||
|
||||
val patchAllManifest by booleanOption(
|
||||
key = "patchAllManifest",
|
||||
default = true,
|
||||
title = "Patch all manifest components",
|
||||
description = "Patch all permissions, intents and content provider authorities supported by GmsCore.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
dependsOn(
|
||||
gmsCoreSupportResourcePatchFactory(
|
||||
gmsCoreVendorGroupIdOption,
|
||||
@ -139,6 +152,20 @@ fun gmsCoreSupportPatch(
|
||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
|
||||
execute {
|
||||
val patchAllManifestEnabled = patchAllManifest == true
|
||||
val permissions = if (patchAllManifestEnabled)
|
||||
PERMISSIONS
|
||||
else
|
||||
PERMISSIONS_LEGACY
|
||||
val actions = if (patchAllManifestEnabled)
|
||||
ACTIONS
|
||||
else
|
||||
ACTIONS_LEGACY
|
||||
val authorities = if (patchAllManifestEnabled)
|
||||
AUTHORITIES
|
||||
else
|
||||
AUTHORITIES_LEGACY
|
||||
|
||||
fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
|
||||
val mutableClass by lazy {
|
||||
proxy(it).mutableClass
|
||||
@ -182,9 +209,9 @@ fun gmsCoreSupportPatch(
|
||||
when (referencedString) {
|
||||
"com.google",
|
||||
"com.google.android.gms",
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES,
|
||||
in permissions,
|
||||
in actions,
|
||||
in authorities,
|
||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
||||
|
||||
// TODO: Add this permission when bumping GmsCore
|
||||
@ -196,7 +223,7 @@ fun gmsCoreSupportPatch(
|
||||
// only when content:// uri
|
||||
if (str.startsWith("content://")) {
|
||||
// check if matches any authority
|
||||
for (authority in AUTHORITIES) {
|
||||
for (authority in authorities) {
|
||||
val uriPrefix = "content://$authority"
|
||||
if (str.startsWith(uriPrefix)) {
|
||||
return str.replace(
|
||||
@ -223,40 +250,56 @@ fun gmsCoreSupportPatch(
|
||||
}
|
||||
}
|
||||
|
||||
fun transformPrimeMethod() {
|
||||
setOf(
|
||||
primesBackgroundInitializationFingerprint,
|
||||
primesLifecycleEventFingerprint
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.methodOrThrow().apply {
|
||||
val exceptionIndex = indexOfFirstInstructionReversedOrThrow {
|
||||
opcode == Opcode.NEW_INSTANCE &&
|
||||
(this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;"
|
||||
fun transformPrimeMethod(packageName: String) {
|
||||
if (patchAllManifestEnabled) {
|
||||
primeMethodFingerprint.methodOrNull()?.apply {
|
||||
var register = 2
|
||||
|
||||
val index = instructions.indexOfFirst {
|
||||
if (it.getReference<StringReference>()?.string != fromPackageName) return@indexOfFirst false
|
||||
|
||||
register = (it as OneRegisterInstruction).registerA
|
||||
return@indexOfFirst true
|
||||
}
|
||||
val index =
|
||||
indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
addInstruction(
|
||||
index,
|
||||
"const/4 v$register, 0x1"
|
||||
)
|
||||
|
||||
replaceInstruction(index, "const-string v$register, \"$packageName\"")
|
||||
}
|
||||
}
|
||||
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"
|
||||
)
|
||||
} else {
|
||||
setOf(
|
||||
primesBackgroundInitializationFingerprint,
|
||||
primesLifecycleEventFingerprint
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.methodOrThrow().apply {
|
||||
val exceptionIndex = indexOfFirstInstructionReversedOrThrow {
|
||||
opcode == Opcode.NEW_INSTANCE &&
|
||||
(this as? ReferenceInstruction)?.reference?.toString() == "Ljava/lang/IllegalStateException;"
|
||||
}
|
||||
val index =
|
||||
indexOfFirstInstructionReversedOrThrow(exceptionIndex, Opcode.IF_EQZ)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
addInstruction(
|
||||
index,
|
||||
"const/4 v$register, 0x1"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
primesApiFingerprint.mutableClassOrThrow().methods.filter { method ->
|
||||
method.name != "<clinit>" &&
|
||||
method.returnType == "V"
|
||||
}.forEach { method ->
|
||||
method.apply {
|
||||
val index = if (MethodUtil.isConstructor(method))
|
||||
indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_DIRECT &&
|
||||
getReference<MethodReference>()?.name == "<init>"
|
||||
} + 1
|
||||
else 0
|
||||
addInstruction(
|
||||
index,
|
||||
"return-void"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,37 +323,40 @@ fun gmsCoreSupportPatch(
|
||||
return@transform null
|
||||
}
|
||||
|
||||
// Return these methods early to prevent the app from crashing.
|
||||
setOf(
|
||||
val earlyReturnFingerprints = mutableListOf(
|
||||
castContextFetchFingerprint,
|
||||
castDynamiteModuleFingerprint,
|
||||
castDynamiteModuleV2Fingerprint,
|
||||
googlePlayUtilityFingerprint,
|
||||
serviceCheckFingerprint,
|
||||
sslGuardFingerprint,
|
||||
).forEach { it.methodOrThrow().returnEarly() }
|
||||
serviceCheckFingerprint
|
||||
)
|
||||
|
||||
// Prevent spam logs.
|
||||
eCatcherFingerprint.methodOrThrow().apply {
|
||||
val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY)
|
||||
addInstruction(index, "return-void")
|
||||
if (patchAllManifestEnabled) {
|
||||
earlyReturnFingerprints += listOf(sslGuardFingerprint)
|
||||
|
||||
// Prevent spam logs.
|
||||
eCatcherFingerprint.methodOrThrow().apply {
|
||||
val index = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY)
|
||||
addInstruction(index, "return-void")
|
||||
}
|
||||
}
|
||||
|
||||
// Return these methods early to prevent the app from crashing.
|
||||
earlyReturnFingerprints.forEach { it.methodOrThrow().returnEarly() }
|
||||
|
||||
// Specific method that needs to be patched.
|
||||
transformPrimeMethod()
|
||||
transformPrimeMethod(packageName)
|
||||
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.method.apply {
|
||||
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
|
||||
val setContextIndex = indexOfFirstInstruction {
|
||||
val reference =
|
||||
getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
||||
if (checkGmsCore == true) {
|
||||
mainActivityOnCreateFingerprint.method.apply {
|
||||
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
|
||||
val setContextIndex = indexOfFirstInstruction {
|
||||
val reference =
|
||||
getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
||||
|
||||
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.
|
||||
if (checkGmsCore == true) {
|
||||
// Add after setContext call, because this patch needs the context.
|
||||
addInstructions(
|
||||
if (setContextIndex < 0) 0 else setContextIndex + 1,
|
||||
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
@ -335,7 +381,30 @@ fun gmsCoreSupportPatch(
|
||||
* that are present in GmsCore which need to be transformed.
|
||||
*/
|
||||
private object Constants {
|
||||
/**
|
||||
* All permissions.
|
||||
*/
|
||||
val PERMISSIONS = setOf(
|
||||
"com.google.android.c2dm.permission.RECEIVE",
|
||||
"com.google.android.c2dm.permission.SEND",
|
||||
"com.google.android.gms.auth.api.phone.permission.SEND",
|
||||
"com.google.android.gms.permission.AD_ID",
|
||||
"com.google.android.gms.permission.AD_ID_NOTIFICATION",
|
||||
"com.google.android.gms.permission.CAR_FUEL",
|
||||
"com.google.android.gms.permission.CAR_INFORMATION",
|
||||
"com.google.android.gms.permission.CAR_MILEAGE",
|
||||
"com.google.android.gms.permission.CAR_SPEED",
|
||||
"com.google.android.gms.permission.CAR_VENDOR_EXTENSION",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
|
||||
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
|
||||
"com.google.android.providers.gsf.permission.READ_GSERVICES",
|
||||
)
|
||||
|
||||
val PERMISSIONS_LEGACY = setOf(
|
||||
// C2DM / GCM
|
||||
"com.google.android.c2dm.permission.RECEIVE",
|
||||
"com.google.android.c2dm.permission.SEND",
|
||||
@ -349,7 +418,234 @@ private object Constants {
|
||||
// "com.google.android.gms.permission.ACTIVITY_RECOGNITION",
|
||||
)
|
||||
|
||||
/**
|
||||
* All intent actions.
|
||||
*/
|
||||
val ACTIONS = setOf(
|
||||
"com.google.android.c2dm.intent.RECEIVE",
|
||||
"com.google.android.c2dm.intent.REGISTER",
|
||||
"com.google.android.c2dm.intent.REGISTRATION",
|
||||
"com.google.android.c2dm.intent.UNREGISTER",
|
||||
"com.google.android.contextmanager.service.ContextManagerService.START",
|
||||
"com.google.android.gcm.intent.SEND",
|
||||
"com.google.android.gms.accounts.ACCOUNT_SERVICE",
|
||||
"com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS",
|
||||
"com.google.android.gms.accountsettings.action.BROWSE_SETTINGS",
|
||||
"com.google.android.gms.accountsettings.action.VIEW_SETTINGS",
|
||||
"com.google.android.gms.accountsettings.MY_ACCOUNT",
|
||||
"com.google.android.gms.accountsettings.PRIVACY_SETTINGS",
|
||||
"com.google.android.gms.accountsettings.SECURITY_SETTINGS",
|
||||
"com.google.android.gms.ads.gservice.START",
|
||||
"com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION",
|
||||
"com.google.android.gms.ads.service.CACHE",
|
||||
"com.google.android.gms.ads.service.CONSENT_LOOKUP",
|
||||
"com.google.android.gms.ads.service.HTTP",
|
||||
"com.google.android.gms.analytics.service.START",
|
||||
"com.google.android.gms.app.settings.GoogleSettingsLink",
|
||||
"com.google.android.gms.appstate.service.START",
|
||||
"com.google.android.gms.appusage.service.START",
|
||||
"com.google.android.gms.asterism.service.START",
|
||||
"com.google.android.gms.audiomodem.service.AudioModemService.START",
|
||||
"com.google.android.gms.audit.service.START",
|
||||
"com.google.android.gms.auth.account.authapi.START",
|
||||
"com.google.android.gms.auth.account.authenticator.auto.service.START",
|
||||
"com.google.android.gms.auth.account.authenticator.chromeos.START",
|
||||
"com.google.android.gms.auth.account.authenticator.tv.service.START",
|
||||
"com.google.android.gms.auth.account.data.service.START",
|
||||
"com.google.android.gms.auth.api.credentials.PICKER",
|
||||
"com.google.android.gms.auth.api.credentials.service.START",
|
||||
"com.google.android.gms.auth.api.identity.service.authorization.START",
|
||||
"com.google.android.gms.auth.api.identity.service.credentialsaving.START",
|
||||
"com.google.android.gms.auth.api.identity.service.signin.START",
|
||||
"com.google.android.gms.auth.api.phone.service.InternalService.START",
|
||||
"com.google.android.gms.auth.api.signin.service.START",
|
||||
"com.google.android.gms.auth.be.appcert.AppCertService",
|
||||
"com.google.android.gms.auth.blockstore.service.START",
|
||||
"com.google.android.gms.auth.config.service.START",
|
||||
"com.google.android.gms.auth.cryptauth.cryptauthservice.START",
|
||||
"com.google.android.gms.auth.GOOGLE_SIGN_IN",
|
||||
"com.google.android.gms.auth.login.LOGIN",
|
||||
"com.google.android.gms.auth.proximity.devicesyncservice.START",
|
||||
"com.google.android.gms.auth.proximity.securechannelservice.START",
|
||||
"com.google.android.gms.auth.proximity.START",
|
||||
"com.google.android.gms.auth.service.START",
|
||||
"com.google.android.gms.backup.ACTION_BACKUP_SETTINGS",
|
||||
"com.google.android.gms.backup.G1_BACKUP",
|
||||
"com.google.android.gms.backup.G1_RESTORE",
|
||||
"com.google.android.gms.backup.GMS_MODULE_RESTORE",
|
||||
"com.google.android.gms.beacon.internal.IBleService.START",
|
||||
"com.google.android.gms.car.service.START",
|
||||
"com.google.android.gms.carrierauth.service.START",
|
||||
"com.google.android.gms.cast.firstparty.START",
|
||||
"com.google.android.gms.cast.remote_display.service.START",
|
||||
"com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE",
|
||||
"com.google.android.gms.cast_mirroring.service.START",
|
||||
"com.google.android.gms.checkin.BIND_TO_SERVICE",
|
||||
"com.google.android.gms.chromesync.service.START",
|
||||
"com.google.android.gms.clearcut.service.START",
|
||||
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
|
||||
"com.google.android.gms.common.download.START",
|
||||
"com.google.android.gms.common.service.START",
|
||||
"com.google.android.gms.common.telemetry.service.START",
|
||||
"com.google.android.gms.config.START",
|
||||
"com.google.android.gms.constellation.service.START",
|
||||
"com.google.android.gms.credential.manager.service.firstparty.START",
|
||||
"com.google.android.gms.deviceconnection.service.START",
|
||||
"com.google.android.gms.drive.ApiService.RESET_AFTER_BOOT",
|
||||
"com.google.android.gms.drive.ApiService.START",
|
||||
"com.google.android.gms.drive.ApiService.STOP",
|
||||
"com.google.android.gms.droidguard.service.INIT",
|
||||
"com.google.android.gms.droidguard.service.PING",
|
||||
"com.google.android.gms.droidguard.service.START",
|
||||
"com.google.android.gms.enterprise.loader.service.START",
|
||||
"com.google.android.gms.facs.cache.service.START",
|
||||
"com.google.android.gms.facs.internal.service.START",
|
||||
"com.google.android.gms.feedback.internal.IFeedbackService",
|
||||
"com.google.android.gms.fido.credentialstore.internal_service.START",
|
||||
"com.google.android.gms.fido.fido2.privileged.START",
|
||||
"com.google.android.gms.fido.fido2.regular.START",
|
||||
"com.google.android.gms.fido.fido2.zeroparty.START",
|
||||
"com.google.android.gms.fido.sourcedevice.service.START",
|
||||
"com.google.android.gms.fido.targetdevice.internal_service.START",
|
||||
"com.google.android.gms.fido.u2f.privileged.START",
|
||||
"com.google.android.gms.fido.u2f.thirdparty.START",
|
||||
"com.google.android.gms.fido.u2f.zeroparty.START",
|
||||
"com.google.android.gms.fitness.BleApi",
|
||||
"com.google.android.gms.fitness.ConfigApi",
|
||||
"com.google.android.gms.fitness.GoalsApi",
|
||||
"com.google.android.gms.fitness.GoogleFitnessService.START",
|
||||
"com.google.android.gms.fitness.HistoryApi",
|
||||
"com.google.android.gms.fitness.InternalApi",
|
||||
"com.google.android.gms.fitness.RecordingApi",
|
||||
"com.google.android.gms.fitness.SensorsApi",
|
||||
"com.google.android.gms.fitness.SessionsApi",
|
||||
"com.google.android.gms.fonts.service.START",
|
||||
"com.google.android.gms.freighter.service.START",
|
||||
"com.google.android.gms.games.internal.connect.service.START",
|
||||
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
|
||||
"com.google.android.gms.games.service.START",
|
||||
"com.google.android.gms.gass.START",
|
||||
"com.google.android.gms.gmscompliance.service.START",
|
||||
"com.google.android.gms.googlehelp.HELP",
|
||||
"com.google.android.gms.googlehelp.service.GoogleHelpService.START",
|
||||
"com.google.android.gms.growth.service.START",
|
||||
"com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START",
|
||||
"com.google.android.gms.icing.INDEX_SERVICE",
|
||||
"com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE",
|
||||
"com.google.android.gms.identity.service.BIND",
|
||||
"com.google.android.gms.inappreach.service.START",
|
||||
"com.google.android.gms.instantapps.START",
|
||||
"com.google.android.gms.kids.service.START",
|
||||
"com.google.android.gms.languageprofile.service.START",
|
||||
"com.google.android.gms.learning.internal.dynamitesupport.START",
|
||||
"com.google.android.gms.learning.intservice.START",
|
||||
"com.google.android.gms.learning.predictor.START",
|
||||
"com.google.android.gms.learning.trainer.START",
|
||||
"com.google.android.gms.learning.training.background.START",
|
||||
"com.google.android.gms.location.places.GeoDataApi",
|
||||
"com.google.android.gms.location.places.PlaceDetectionApi",
|
||||
"com.google.android.gms.location.places.PlacesApi",
|
||||
"com.google.android.gms.location.reporting.service.START",
|
||||
"com.google.android.gms.location.settings.LOCATION_HISTORY",
|
||||
"com.google.android.gms.location.settings.LOCATION_REPORTING_SETTINGS",
|
||||
"com.google.android.gms.locationsharing.api.START",
|
||||
"com.google.android.gms.locationsharingreporter.service.START",
|
||||
"com.google.android.gms.lockbox.service.START",
|
||||
"com.google.android.gms.matchstick.lighter.service.START",
|
||||
"com.google.android.gms.mdm.services.DeviceManagerApiService.START",
|
||||
"com.google.android.gms.mdm.services.START",
|
||||
"com.google.android.gms.mdns.service.START",
|
||||
"com.google.android.gms.measurement.START",
|
||||
"com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START",
|
||||
"com.google.android.gms.nearby.connection.service.START",
|
||||
"com.google.android.gms.nearby.fastpair.START",
|
||||
"com.google.android.gms.nearby.messages.service.NearbyMessagesService.START",
|
||||
"com.google.android.gms.nearby.sharing.service.NearbySharingService.START",
|
||||
"com.google.android.gms.nearby.sharing.START_SERVICE",
|
||||
"com.google.android.gms.notifications.service.START",
|
||||
"com.google.android.gms.ocr.service.internal.START",
|
||||
"com.google.android.gms.ocr.service.START",
|
||||
"com.google.android.gms.oss.licenses.service.START",
|
||||
"com.google.android.gms.payse.service.BIND",
|
||||
"com.google.android.gms.people.contactssync.service.START",
|
||||
"com.google.android.gms.people.service.START",
|
||||
"com.google.android.gms.phenotype.service.START",
|
||||
"com.google.android.gms.photos.autobackup.service.START",
|
||||
"com.google.android.gms.playlog.service.START",
|
||||
"com.google.android.gms.plus.service.default.INTENT",
|
||||
"com.google.android.gms.plus.service.image.INTENT",
|
||||
"com.google.android.gms.plus.service.internal.START",
|
||||
"com.google.android.gms.plus.service.START",
|
||||
"com.google.android.gms.potokens.service.START",
|
||||
"com.google.android.gms.pseudonymous.service.START",
|
||||
"com.google.android.gms.rcs.START",
|
||||
"com.google.android.gms.reminders.service.START",
|
||||
"com.google.android.gms.romanesco.MODULE_BACKUP_AGENT",
|
||||
"com.google.android.gms.romanesco.service.START",
|
||||
"com.google.android.gms.safetynet.service.START",
|
||||
"com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE",
|
||||
"com.google.android.gms.search.service.SEARCH_AUTH_START",
|
||||
"com.google.android.gms.semanticlocation.service.START_ODLH",
|
||||
"com.google.android.gms.sesame.service.BIND",
|
||||
"com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS",
|
||||
"com.google.android.gms.setup.auth.SecondDeviceAuth.START",
|
||||
"com.google.android.gms.signin.service.START",
|
||||
"com.google.android.gms.smartdevice.d2d.SourceDeviceService.START",
|
||||
"com.google.android.gms.smartdevice.d2d.TargetDeviceService.START",
|
||||
"com.google.android.gms.smartdevice.directtransfer.SourceDirectTransferService.START",
|
||||
"com.google.android.gms.smartdevice.directtransfer.TargetDirectTransferService.START",
|
||||
"com.google.android.gms.smartdevice.postsetup.PostSetupService.START",
|
||||
"com.google.android.gms.smartdevice.setup.accounts.AccountsService.START",
|
||||
"com.google.android.gms.smartdevice.wifi.START_WIFI_HELPER_SERVICE",
|
||||
"com.google.android.gms.social.location.activity.service.START",
|
||||
"com.google.android.gms.speech.service.START",
|
||||
"com.google.android.gms.statementservice.EXECUTE",
|
||||
"com.google.android.gms.stats.ACTION_UPLOAD_DROPBOX_ENTRIES",
|
||||
"com.google.android.gms.tapandpay.service.BIND",
|
||||
"com.google.android.gms.telephonyspam.service.START",
|
||||
"com.google.android.gms.testsupport.service.START",
|
||||
"com.google.android.gms.thunderbird.service.START",
|
||||
"com.google.android.gms.trustagent.BridgeApi.START",
|
||||
"com.google.android.gms.trustagent.StateApi.START",
|
||||
"com.google.android.gms.trustagent.trustlet.trustletmanagerservice.BIND",
|
||||
"com.google.android.gms.trustlet.bluetooth.service.BIND",
|
||||
"com.google.android.gms.trustlet.connectionlessble.service.BIND",
|
||||
"com.google.android.gms.trustlet.face.service.BIND",
|
||||
"com.google.android.gms.trustlet.nfc.service.BIND",
|
||||
"com.google.android.gms.trustlet.onbody.service.BIND",
|
||||
"com.google.android.gms.trustlet.place.service.BIND",
|
||||
"com.google.android.gms.trustlet.voiceunlock.service.BIND",
|
||||
"com.google.android.gms.udc.service.START",
|
||||
"com.google.android.gms.update.START_API_SERVICE",
|
||||
"com.google.android.gms.update.START_SERVICE",
|
||||
"com.google.android.gms.update.START_SINGLE_USER_API_SERVICE",
|
||||
"com.google.android.gms.update.START_TV_API_SERVICE",
|
||||
"com.google.android.gms.usagereporting.service.START",
|
||||
"com.google.android.gms.userlocation.service.START",
|
||||
"com.google.android.gms.vehicle.cabin.service.START",
|
||||
"com.google.android.gms.vehicle.climate.service.START",
|
||||
"com.google.android.gms.vehicle.info.service.START",
|
||||
"com.google.android.gms.wallet.service.BIND",
|
||||
"com.google.android.gms.walletp2p.service.firstparty.BIND",
|
||||
"com.google.android.gms.walletp2p.service.zeroparty.BIND",
|
||||
"com.google.android.gms.wearable.BIND",
|
||||
"com.google.android.gms.wearable.BIND_LISTENER",
|
||||
"com.google.android.gms.wearable.DATA_CHANGED",
|
||||
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
|
||||
"com.google.android.gms.wearable.NODE_CHANGED",
|
||||
"com.google.android.gsf.action.GET_GLS",
|
||||
"com.google.android.location.settings.LOCATION_REPORTING_SETTINGS",
|
||||
"com.google.android.mdd.service.START",
|
||||
"com.google.android.mdh.service.listener.START",
|
||||
"com.google.android.mdh.service.START",
|
||||
"com.google.android.mobstore.service.START",
|
||||
"com.google.firebase.auth.api.gms.service.START",
|
||||
"com.google.firebase.dynamiclinks.service.START",
|
||||
"com.google.iid.TOKEN_REQUEST",
|
||||
"com.google.android.gms.location.places.ui.PICK_PLACE",
|
||||
)
|
||||
|
||||
val ACTIONS_LEGACY = setOf(
|
||||
// C2DM / GCM
|
||||
"com.google.android.c2dm.intent.REGISTER",
|
||||
"com.google.android.c2dm.intent.REGISTRATION",
|
||||
@ -407,7 +703,19 @@ private object Constants {
|
||||
"com.google.android.gms.droidguard.service.START",
|
||||
)
|
||||
|
||||
/**
|
||||
* All content provider authorities.
|
||||
*/
|
||||
val AUTHORITIES = setOf(
|
||||
"com.google.android.gms.auth.accounts",
|
||||
"com.google.android.gms.chimera",
|
||||
"com.google.android.gms.fonts",
|
||||
"com.google.android.gms.phenotype",
|
||||
"com.google.android.gsf.gservices",
|
||||
"com.google.settings",
|
||||
)
|
||||
|
||||
val AUTHORITIES_LEGACY = setOf(
|
||||
// gsf
|
||||
"com.google.android.gsf.gservices",
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package app.revanced.patches.shared.mapping
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import org.w3c.dom.Element
|
||||
|
||||
|
@ -147,7 +147,8 @@ fun ResourcePatchContext.baseTranslationsPatch(
|
||||
val length = text.length
|
||||
if (!text.endsWith("DEFAULT") &&
|
||||
length >= 2 &&
|
||||
text.subSequence(length - 2, length) !in filteredAppLanguages) {
|
||||
text.subSequence(length - 2, length) !in filteredAppLanguages
|
||||
) {
|
||||
nodesToRemove.add(item)
|
||||
}
|
||||
}
|
||||
|
@ -262,8 +262,10 @@ val feedComponentsPatch = bytecodePatch(
|
||||
val insertIndex = indexOfBufferParserInstruction(this)
|
||||
|
||||
if (is_19_46_or_greater) {
|
||||
val objectIndex = indexOfFirstInstructionReversedOrThrow(insertIndex, Opcode.IGET_OBJECT)
|
||||
val objectRegister = getInstruction<TwoRegisterInstruction>(objectIndex).registerA
|
||||
val objectIndex =
|
||||
indexOfFirstInstructionReversedOrThrow(insertIndex, Opcode.IGET_OBJECT)
|
||||
val objectRegister =
|
||||
getInstruction<TwoRegisterInstruction>(objectIndex).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex, """
|
||||
@ -275,7 +277,8 @@ val feedComponentsPatch = bytecodePatch(
|
||||
)
|
||||
} else {
|
||||
val objectIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT)
|
||||
val objectRegister = getInstruction<TwoRegisterInstruction>(objectIndex).registerA
|
||||
val objectRegister =
|
||||
getInstruction<TwoRegisterInstruction>(objectIndex).registerA
|
||||
val jumpIndex = it.patternMatch!!.startIndex
|
||||
|
||||
addInstructionsWithLabels(
|
||||
|
@ -90,7 +90,8 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
val playbackStartIndex = indexOfPlaybackStartDescriptorInstruction(this) + 1
|
||||
val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex).registerA
|
||||
val playbackStartRegister =
|
||||
getInstruction<OneRegisterInstruction>(playbackStartIndex).registerA
|
||||
|
||||
val mapIndex = indexOfFirstInstructionOrThrow(playbackStartIndex) {
|
||||
val reference = getReference<MethodReference>()
|
||||
@ -169,15 +170,24 @@ val openChannelOfLiveAvatarPatch = bytecodePatch(
|
||||
val playbackStartIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
|
||||
}
|
||||
val mapIndex = indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
|
||||
val mapIndex =
|
||||
indexOfFirstInstructionReversedOrThrow(playbackStartIndex, Opcode.IPUT)
|
||||
val mapRegister = getInstruction<TwoRegisterInstruction>(mapIndex).registerA
|
||||
val playbackStartRegister = getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA
|
||||
val videoIdRegister = getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
|
||||
val playbackStartRegister =
|
||||
getInstruction<OneRegisterInstruction>(playbackStartIndex + 1).registerA
|
||||
val videoIdRegister =
|
||||
getInstruction<FiveRegisterInstruction>(playbackStartIndex).registerC
|
||||
|
||||
addInstructionsWithLabels(
|
||||
playbackStartIndex + 2, """
|
||||
move-object/from16 v$mapRegister, p2
|
||||
${fetchChannelIdInstructions(playbackStartRegister, mapRegister, videoIdRegister)}
|
||||
${
|
||||
fetchChannelIdInstructions(
|
||||
playbackStartRegister,
|
||||
mapRegister,
|
||||
videoIdRegister
|
||||
)
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
@ -80,7 +80,8 @@ private val snackBarComponentsBytecodePatch = bytecodePatch(
|
||||
bottomUiContainerThemeFingerprint.matchOrThrow().let {
|
||||
it.method.apply {
|
||||
val darkThemeIndex = it.patternMatch!!.startIndex + 2
|
||||
val darkThemeReference = getInstruction<ReferenceInstruction>(darkThemeIndex).reference.toString()
|
||||
val darkThemeReference =
|
||||
getInstruction<ReferenceInstruction>(darkThemeIndex).reference.toString()
|
||||
|
||||
implementation!!.instructions
|
||||
.withIndex()
|
||||
@ -91,7 +92,8 @@ private val snackBarComponentsBytecodePatch = bytecodePatch(
|
||||
.map { (index, _) -> index }
|
||||
.reversed()
|
||||
.forEach { index ->
|
||||
val appThemeIndex = indexOfFirstInstructionReversedOrThrow(index, Opcode.MOVE_RESULT_OBJECT)
|
||||
val appThemeIndex =
|
||||
indexOfFirstInstructionReversedOrThrow(index, Opcode.MOVE_RESULT_OBJECT)
|
||||
val appThemeRegister =
|
||||
getInstruction<OneRegisterInstruction>(appThemeIndex).registerA
|
||||
val darkThemeRegister =
|
||||
|
@ -86,7 +86,11 @@ val shortsActionButtonsPatch = resourcePatch(
|
||||
|
||||
// Some directory is missing in the bundles.
|
||||
if (inputStreamForLegacy != null && fromFileResolved.exists()) {
|
||||
Files.copy(inputStreamForLegacy, fromFileResolved.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.copy(
|
||||
inputStreamForLegacy,
|
||||
fromFileResolved.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
|
||||
if (is_19_36_or_greater) {
|
||||
@ -95,7 +99,11 @@ val shortsActionButtonsPatch = resourcePatch(
|
||||
|
||||
// Some directory is missing in the bundles.
|
||||
if (inputStreamForNew != null && toFileResolved.exists()) {
|
||||
Files.copy(inputStreamForNew, toFileResolved.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.copy(
|
||||
inputStreamForNew,
|
||||
toFileResolved.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,30 +217,24 @@ val customBrandingIconPatch = resourcePatch(
|
||||
}
|
||||
}
|
||||
|
||||
val styleList = if (is_19_32_or_greater)
|
||||
listOf(
|
||||
Triple(
|
||||
"values-night-v31",
|
||||
"Theme.YouTube.Home",
|
||||
"@style/Base.V27.Theme.YouTube.Home"
|
||||
),
|
||||
Triple(
|
||||
"values-v31",
|
||||
"Theme.YouTube.Home",
|
||||
"@style/Base.V27.Theme.YouTube.Home"
|
||||
),
|
||||
)
|
||||
else
|
||||
listOf(
|
||||
Triple(
|
||||
"values-v31",
|
||||
"Base.Theme.YouTube.Launcher",
|
||||
"@style/Theme.AppCompat.DayNight.NoActionBar"
|
||||
),
|
||||
)
|
||||
val styleList = mutableListOf(
|
||||
Pair(
|
||||
"Base.Theme.YouTube.Launcher",
|
||||
"@style/Theme.AppCompat.DayNight.NoActionBar"
|
||||
),
|
||||
)
|
||||
|
||||
styleList.forEach { (directory, nodeAttributeName, nodeAttributeParent) ->
|
||||
document("res/$directory/styles.xml").use { document ->
|
||||
if (is_19_32_or_greater) {
|
||||
styleList += listOf(
|
||||
Pair(
|
||||
"Theme.YouTube.Home",
|
||||
"@style/Base.V27.Theme.YouTube.Home"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
styleList.forEach { (nodeAttributeName, nodeAttributeParent) ->
|
||||
document("res/values-v31/styles.xml").use { document ->
|
||||
val resourcesNode =
|
||||
document.getElementsByTagName("resources").item(0) as Element
|
||||
|
||||
@ -249,7 +243,10 @@ val customBrandingIconPatch = resourcePatch(
|
||||
style.setAttribute("parent", nodeAttributeParent)
|
||||
|
||||
val splashScreenAnimatedIcon = document.createElement("item")
|
||||
splashScreenAnimatedIcon.setAttribute("name", "android:windowSplashScreenAnimatedIcon")
|
||||
splashScreenAnimatedIcon.setAttribute(
|
||||
"name",
|
||||
"android:windowSplashScreenAnimatedIcon"
|
||||
)
|
||||
splashScreenAnimatedIcon.textContent = "@drawable/avd_anim"
|
||||
|
||||
// Deprecated in Android 13+
|
||||
@ -258,10 +255,11 @@ val customBrandingIconPatch = resourcePatch(
|
||||
"name",
|
||||
"android:windowSplashScreenAnimationDuration"
|
||||
)
|
||||
splashScreenAnimationDuration.textContent = if (appIcon.startsWith("revancify"))
|
||||
"1500"
|
||||
else
|
||||
"1000"
|
||||
splashScreenAnimationDuration.textContent =
|
||||
if (appIcon.startsWith("revancify"))
|
||||
"1500"
|
||||
else
|
||||
"1000"
|
||||
|
||||
style.appendChild(splashScreenAnimatedIcon)
|
||||
style.appendChild(splashScreenAnimationDuration)
|
||||
|
@ -4,7 +4,6 @@ import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_NAME_FOR_YOUTUBE
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusLabel
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.removeStringsElements
|
||||
import app.revanced.util.valueOrThrow
|
||||
@ -53,7 +52,5 @@ val customBrandingNamePatch = resourcePatch(
|
||||
.appendChild(stringElement)
|
||||
}
|
||||
|
||||
updatePatchStatusLabel(appName)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +61,10 @@ val sharedThemePatch = resourcePatch(
|
||||
0 -> when (nodeAttributeName) {
|
||||
"Base.Theme.YouTube.Launcher.Dark",
|
||||
"Base.Theme.YouTube.Launcher.Cairo.Dark" -> "@color/yt_black1"
|
||||
|
||||
"Base.Theme.YouTube.Launcher.Light",
|
||||
"Base.Theme.YouTube.Launcher.Cairo.Light" -> "@color/yt_white1"
|
||||
|
||||
else -> "null"
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,10 @@ val accessibilityPatch = bytecodePatch(
|
||||
.methods
|
||||
.first { method -> method.name == "<init>" }
|
||||
.apply {
|
||||
val lifecycleObserverIndex = indexOfFirstInstructionReversedOrThrow(Opcode.NEW_INSTANCE)
|
||||
val lifecycleObserverClass = getInstruction<ReferenceInstruction>(lifecycleObserverIndex).reference.toString()
|
||||
val lifecycleObserverIndex =
|
||||
indexOfFirstInstructionReversedOrThrow(Opcode.NEW_INSTANCE)
|
||||
val lifecycleObserverClass =
|
||||
getInstruction<ReferenceInstruction>(lifecycleObserverIndex).reference.toString()
|
||||
|
||||
findMethodOrThrow(lifecycleObserverClass) {
|
||||
accessFlags == AccessFlags.PUBLIC or AccessFlags.FINAL &&
|
||||
|
@ -85,17 +85,19 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
backgroundPlaybackManagerCairoFragmentPrimaryFingerprint,
|
||||
backgroundPlaybackManagerCairoFragmentSecondaryFingerprint
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.matchOrThrow(backgroundPlaybackManagerCairoFragmentParentFingerprint).let {
|
||||
it.method.apply {
|
||||
val insertIndex = it.patternMatch!!.startIndex + 4
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
fingerprint.matchOrThrow(backgroundPlaybackManagerCairoFragmentParentFingerprint)
|
||||
.let {
|
||||
it.method.apply {
|
||||
val insertIndex = it.patternMatch!!.startIndex + 4
|
||||
val insertRegister =
|
||||
getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"const/4 v$insertRegister, 0x0"
|
||||
)
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"const/4 v$insertRegister, 0x0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipInputConsumerFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
|
||||
|
@ -57,20 +57,26 @@ val actionButtonsPatch = bytecodePatch(
|
||||
findMethodOrThrow(parameters[1].type) {
|
||||
name == "toString"
|
||||
}
|
||||
val identifierReference = with (conversionContextToStringMethod) {
|
||||
val identifierReference = with(conversionContextToStringMethod) {
|
||||
val identifierStringIndex =
|
||||
indexOfFirstStringInstructionOrThrow(", identifierProperty=")
|
||||
val identifierStringAppendIndex =
|
||||
indexOfFirstInstructionOrThrow(identifierStringIndex, Opcode.INVOKE_VIRTUAL)
|
||||
val identifierStringAppendIndexRegister = getInstruction<FiveRegisterInstruction>(identifierStringAppendIndex).registerD
|
||||
val identifierStringAppendIndexRegister =
|
||||
getInstruction<FiveRegisterInstruction>(identifierStringAppendIndex).registerD
|
||||
val identifierAppendIndex =
|
||||
indexOfFirstInstructionOrThrow(identifierStringAppendIndex + 1, Opcode.INVOKE_VIRTUAL)
|
||||
val identifierRegister = getInstruction<FiveRegisterInstruction>(identifierAppendIndex).registerD
|
||||
val identifierIndex = indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) {
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/String;" &&
|
||||
(this as? TwoRegisterInstruction)?.registerA == identifierRegister
|
||||
}
|
||||
indexOfFirstInstructionOrThrow(
|
||||
identifierStringAppendIndex + 1,
|
||||
Opcode.INVOKE_VIRTUAL
|
||||
)
|
||||
val identifierRegister =
|
||||
getInstruction<FiveRegisterInstruction>(identifierAppendIndex).registerD
|
||||
val identifierIndex =
|
||||
indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) {
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/String;" &&
|
||||
(this as? TwoRegisterInstruction)?.registerA == identifierRegister
|
||||
}
|
||||
getInstruction<ReferenceInstruction>(identifierIndex).reference
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,11 @@ import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutCircl
|
||||
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutIcon
|
||||
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo
|
||||
import app.revanced.patches.youtube.utils.resourceid.offlineActionsVideoDeletedUndoSnackbarText
|
||||
import app.revanced.patches.youtube.utils.resourceid.verticalTouchOffsetToEnterFineScrubbing
|
||||
import app.revanced.patches.youtube.utils.resourceid.seekEasyHorizontalTouchOffsetToStartScrubbing
|
||||
import app.revanced.patches.youtube.utils.resourceid.suggestedAction
|
||||
import app.revanced.patches.youtube.utils.resourceid.tapBloomView
|
||||
import app.revanced.patches.youtube.utils.resourceid.touchArea
|
||||
import app.revanced.patches.youtube.utils.resourceid.verticalTouchOffsetToEnterFineScrubbing
|
||||
import app.revanced.patches.youtube.utils.resourceid.verticalTouchOffsetToStartFineScrubbing
|
||||
import app.revanced.patches.youtube.utils.resourceid.videoZoomSnapIndicator
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
|
@ -129,9 +129,11 @@ private val speedOverlayPatch = bytecodePatch(
|
||||
|
||||
// region patch for Custom speed overlay float value
|
||||
|
||||
val speedFieldReference = with (speedOverlayFloatValueFingerprint.methodOrThrow()) {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(SPEED_OVERLAY_LEGACY_FEATURE_FLAG)
|
||||
val floatIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
|
||||
val speedFieldReference = with(speedOverlayFloatValueFingerprint.methodOrThrow()) {
|
||||
val literalIndex =
|
||||
indexOfFirstLiteralInstructionOrThrow(SPEED_OVERLAY_LEGACY_FEATURE_FLAG)
|
||||
val floatIndex =
|
||||
indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
|
||||
val floatRegister = getInstruction<TwoRegisterInstruction>(floatIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
@ -604,7 +606,9 @@ val playerComponentsPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
if (is_20_12_or_greater) {
|
||||
filmStripOverlayMotionEventPrimaryFingerprint.matchOrThrow(filmStripOverlayStartParentFingerprint).let {
|
||||
filmStripOverlayMotionEventPrimaryFingerprint.matchOrThrow(
|
||||
filmStripOverlayStartParentFingerprint
|
||||
).let {
|
||||
it.method.apply {
|
||||
val index = it.patternMatch!!.startIndex
|
||||
val register = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
@ -613,7 +617,9 @@ val playerComponentsPatch = bytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
filmStripOverlayMotionEventSecondaryFingerprint.matchOrThrow(filmStripOverlayStartParentFingerprint).let {
|
||||
filmStripOverlayMotionEventSecondaryFingerprint.matchOrThrow(
|
||||
filmStripOverlayStartParentFingerprint
|
||||
).let {
|
||||
it.method.apply {
|
||||
val index = it.patternMatch!!.startIndex + 2
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
@ -7,7 +7,6 @@ import app.revanced.patches.youtube.utils.resourceid.quickActionsElementContaine
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
internal val broadcastReceiverFingerprint = legacyFingerprint(
|
||||
name = "broadcastReceiverFingerprint",
|
||||
|
@ -63,6 +63,7 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
|
||||
// region modern miniplayer
|
||||
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
|
||||
|
||||
// In later targets this feature flag does nothing and is dead code.
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
|
||||
internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L
|
||||
|
@ -239,7 +239,8 @@ val miniplayerPatch = bytecodePatch(
|
||||
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex + 1, """
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getMiniplayerDefaultSize(I)I
|
||||
move-result v$register
|
||||
""",
|
||||
|
@ -256,7 +256,8 @@ val overlayButtonsPatch = resourcePatch(
|
||||
width != "0.0dip",
|
||||
)
|
||||
|
||||
val isButton = id.endsWith("_button") && id != "@id/multiview_button" || id == "@id/youtube_controls_fullscreen_button_stub"
|
||||
val isButton =
|
||||
id.endsWith("_button") && id != "@id/multiview_button" || id == "@id/youtube_controls_fullscreen_button_stub"
|
||||
|
||||
// Adjust TimeBar and Chapter bottom padding
|
||||
val timBarItem = mutableMapOf(
|
||||
@ -286,7 +287,10 @@ val overlayButtonsPatch = resourcePatch(
|
||||
if (id.equals("@+id/bottom_margin")) {
|
||||
node.setAttribute("android:layout_height", marginBottom)
|
||||
} else if (id.equals("@id/time_bar_reference_view")) {
|
||||
node.setAttribute("yt:layout_constraintBottom_toTopOf", "@id/quick_actions_container")
|
||||
node.setAttribute(
|
||||
"yt:layout_constraintBottom_toTopOf",
|
||||
"@id/quick_actions_container"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import kotlin.collections.listOf
|
||||
|
||||
internal val shortsSeekbarColorFingerprint = legacyFingerprint(
|
||||
name = "shortsSeekbarColorFingerprint",
|
||||
|
@ -139,7 +139,8 @@ val seekbarComponentsPatch = bytecodePatch(
|
||||
reference?.returnType == "V" &&
|
||||
reference.parameterTypes.isEmpty()
|
||||
}
|
||||
val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(tapSeekIndex).registerC
|
||||
val thisInstanceRegister =
|
||||
getInstruction<FiveRegisterInstruction>(tapSeekIndex).registerC
|
||||
|
||||
val tapSeekClass = getInstruction(tapSeekIndex)
|
||||
.getReference<MethodReference>()!!
|
||||
@ -274,7 +275,10 @@ val seekbarComponentsPatch = bytecodePatch(
|
||||
playerSeekbarHandleColorPrimaryFingerprint,
|
||||
playerSeekbarHandleColorSecondaryFingerprint
|
||||
).forEach {
|
||||
it.methodOrThrow().addColorChangeInstructions(ytStaticBrandRed, "getVideoPlayerSeekbarColorAccent")
|
||||
it.methodOrThrow().addColorChangeInstructions(
|
||||
ytStaticBrandRed,
|
||||
"getVideoPlayerSeekbarColorAccent"
|
||||
)
|
||||
}
|
||||
// If hiding feed seekbar thumbnails, then turn off the cairo gradient
|
||||
// of the watch history menu items as they use the same gradient as the
|
||||
|
@ -22,7 +22,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import kotlin.collections.listOf
|
||||
|
||||
internal val bottomSheetMenuDismissFingerprint = legacyFingerprint(
|
||||
name = "bottomSheetMenuDismissFingerprint",
|
||||
|
@ -344,7 +344,11 @@ private val shortsCustomActionsPatch = bytecodePatch(
|
||||
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 dismissReference = with(
|
||||
bottomSheetMenuDismissFingerprint.methodOrThrow(
|
||||
bottomSheetMenuListBuilderFingerprint
|
||||
)
|
||||
) {
|
||||
val dismissIndex = indexOfDismissInstruction(this)
|
||||
getInstruction<ReferenceInstruction>(dismissIndex).reference
|
||||
}
|
||||
@ -352,7 +356,8 @@ private val shortsCustomActionsPatch = bytecodePatch(
|
||||
bottomSheetMenuItemClickFingerprint
|
||||
.methodOrThrow(bottomSheetMenuListBuilderFingerprint)
|
||||
.addInstructionsWithLabels(
|
||||
0, """
|
||||
0,
|
||||
"""
|
||||
invoke-static/range {p2 .. p2}, $EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onBottomSheetMenuItemClick(Landroid/view/View;)Z
|
||||
move-result v0
|
||||
if-eqz v0, :ignore
|
||||
@ -430,7 +435,7 @@ private val shortsRepeatPatch = bytecodePatch(
|
||||
"setMainActivity"
|
||||
)
|
||||
|
||||
val endScreenReference = with (reelEnumConstructorFingerprint.methodOrThrow()) {
|
||||
val endScreenReference = with(reelEnumConstructorFingerprint.methodOrThrow()) {
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID)
|
||||
|
||||
addInstructions(
|
||||
@ -510,7 +515,11 @@ private val shortsRepeatPatch = bytecodePatch(
|
||||
// Manually add the 'Autoplay' code that Google removed.
|
||||
// Tested on YouTube 20.10.
|
||||
if (is_20_09_or_greater) {
|
||||
val (directReference, virtualReference) = with (reelPlaybackFingerprint.methodOrThrow(videoIdFingerprintShorts)) {
|
||||
val (directReference, virtualReference) = with(
|
||||
reelPlaybackFingerprint.methodOrThrow(
|
||||
videoIdFingerprintShorts
|
||||
)
|
||||
) {
|
||||
val directIndex = indexOfInitializationInstruction(this)
|
||||
val virtualIndex = indexOfFirstInstructionOrThrow(directIndex) {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
@ -528,7 +537,8 @@ private val shortsRepeatPatch = bytecodePatch(
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
getReference<MethodReference>()?.definingClass == EXTENSION_REPEAT_STATE_CLASS_DESCRIPTOR
|
||||
}
|
||||
val enumRegister = getInstruction<OneRegisterInstruction>(extensionIndex + 1).registerA
|
||||
val enumRegister =
|
||||
getInstruction<OneRegisterInstruction>(extensionIndex + 1).registerA
|
||||
val freeIndex = indexOfFirstInstructionOrThrow(extensionIndex) {
|
||||
opcode == Opcode.SGET_OBJECT &&
|
||||
getReference<FieldReference>()?.name != "a"
|
||||
@ -1014,7 +1024,8 @@ val shortsComponentPatch = bytecodePatch(
|
||||
getReference<MethodReference>()?.returnType == PLAYBACK_START_DESCRIPTOR_CLASS_DESCRIPTOR
|
||||
}
|
||||
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
|
||||
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
val playbackStartRegister =
|
||||
getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
index + 2,
|
||||
|
@ -1,41 +1,17 @@
|
||||
package app.revanced.patches.youtube.shorts.startupshortsreset
|
||||
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
/**
|
||||
* YouTube v18.15.40 ~ YouTube 19.46.42
|
||||
* YouTube v18.15.40+
|
||||
*/
|
||||
internal val userWasInShortsABConfigFingerprint = legacyFingerprint(
|
||||
internal val userWasInShortsConfigFingerprint = legacyFingerprint(
|
||||
name = "userWasInShortsABConfigFingerprint",
|
||||
returnType = "V",
|
||||
strings = listOf("Failed to get offline response: "),
|
||||
customFingerprint = { method, _ ->
|
||||
indexOfOptionalInstruction(method) >= 0
|
||||
}
|
||||
)
|
||||
|
||||
internal fun indexOfOptionalInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
getReference<MethodReference>().toString() == "Lj${'$'}/util/Optional;->of(Ljava/lang/Object;)Lj${'$'}/util/Optional;"
|
||||
}
|
||||
|
||||
/**
|
||||
* YouTube 19.47.53 ~
|
||||
*/
|
||||
internal val userWasInShortsABConfigAlternativeFingerprint = legacyFingerprint(
|
||||
name = "userWasInShortsABConfigAlternativeFingerprint",
|
||||
returnType = "V",
|
||||
parameters = listOf("I"),
|
||||
opcodes = listOf(Opcode.OR_INT_LIT8),
|
||||
strings = listOf("alias", "null"),
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
literals = listOf(45358360L)
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -4,14 +4,10 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.DISABLE_RESUMING_SHORTS_ON_STARTUP
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||
@ -19,10 +15,8 @@ import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.fingerprint.matchOrThrow
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.getWalkerMethod
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import app.revanced.util.indexOfFirstStringInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
@ -42,50 +36,19 @@ val resumingShortsOnStartupPatch = bytecodePatch(
|
||||
|
||||
execute {
|
||||
|
||||
fun MutableMethod.hookUserWasInShortsABConfig(startIndex: Int) {
|
||||
val walkerIndex = implementation!!.instructions.let {
|
||||
val subListIndex =
|
||||
it.subList(startIndex, startIndex + 20).indexOfFirst { instruction ->
|
||||
val reference = instruction.getReference<MethodReference>()
|
||||
instruction.opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
reference?.returnType == "Z" &&
|
||||
reference.definingClass != "Lj${'$'}/util/Optional;" &&
|
||||
reference.parameterTypes.isEmpty()
|
||||
}
|
||||
if (subListIndex < 0)
|
||||
throw PatchException("subListIndex not found")
|
||||
|
||||
startIndex + subListIndex
|
||||
}
|
||||
val walkerMethod = getWalkerMethod(walkerIndex)
|
||||
|
||||
// This method will only be called for the user being A/B tested.
|
||||
// Presumably a method that processes the ProtoDataStore value (boolean) for the 'user_was_in_shorts' key.
|
||||
walkerMethod.apply {
|
||||
addInstructionsWithLabels(
|
||||
0, """
|
||||
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
|
||||
move-result v0
|
||||
if-eqz v0, :show
|
||||
const/4 v0, 0x0
|
||||
return v0
|
||||
""", ExternalLabel("show", getInstruction(0))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_19_46_or_greater) {
|
||||
userWasInShortsABConfigAlternativeFingerprint.methodOrThrow().apply {
|
||||
val stringIndex = indexOfFirstStringInstructionOrThrow("null")
|
||||
val startIndex = indexOfFirstInstructionOrThrow(stringIndex, Opcode.OR_INT_LIT8)
|
||||
hookUserWasInShortsABConfig(startIndex)
|
||||
}
|
||||
} else {
|
||||
userWasInShortsABConfigFingerprint.methodOrThrow().apply {
|
||||
val startIndex = indexOfOptionalInstruction(this)
|
||||
hookUserWasInShortsABConfig(startIndex)
|
||||
}
|
||||
}
|
||||
userWasInShortsConfigFingerprint
|
||||
.methodOrThrow()
|
||||
.addInstructionsWithLabels(
|
||||
0, """
|
||||
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
|
||||
move-result v0
|
||||
if-eqz v0, :show
|
||||
const/4 v0, 0x0
|
||||
return v0
|
||||
:show
|
||||
nop
|
||||
"""
|
||||
)
|
||||
|
||||
if (is_20_02_or_greater) {
|
||||
userWasInShortsAlternativeFingerprint.matchOrThrow().let {
|
||||
|
@ -10,6 +10,7 @@ import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val fullScreenEngagementOverlayFingerprint = legacyFingerprint(
|
||||
@ -113,11 +114,17 @@ internal val playerGestureConfigSyntheticFingerprint = legacyFingerprint(
|
||||
// This method is always called "a" because this kind of class always has a single method.
|
||||
method.name == "a" &&
|
||||
classDef.methods.count() == 2 &&
|
||||
method.indexOfFirstInstruction {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
|
||||
reference.parameterTypes.isEmpty() &&
|
||||
reference.returnType == "Z"
|
||||
} >= 0
|
||||
indexOfPlayerConfigModelBooleanInstruction(method) >= 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,7 +30,6 @@ import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
||||
import app.revanced.util.fingerprint.matchOrThrow
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.fingerprint.mutableClassOrThrow
|
||||
import app.revanced.util.getReference
|
||||
@ -212,14 +211,17 @@ val swipeControlsPatch = bytecodePatch(
|
||||
|
||||
// region patch for disable swipe to enter fullscreen mode (in the player) and disable swipe to exit fullscreen mode
|
||||
|
||||
playerGestureConfigSyntheticFingerprint.matchOrThrow().let {
|
||||
val endIndex = it.patternMatch!!.endIndex
|
||||
playerGestureConfigSyntheticFingerprint.methodOrThrow().apply {
|
||||
val disableSwipeToExitFullscreenModeIndex =
|
||||
indexOfPlayerConfigModelBooleanInstruction(this)
|
||||
val disableSwipeToEnterFullscreenModeInThePlayerIndex =
|
||||
indexOfPlayerConfigModelBooleanInstruction(this, disableSwipeToExitFullscreenModeIndex + 1)
|
||||
|
||||
mapOf(
|
||||
3 to "disableSwipeToEnterFullscreenModeInThePlayer",
|
||||
9 to "disableSwipeToExitFullscreenMode"
|
||||
).forEach { (offSet, methodName) ->
|
||||
it.getWalkerMethod(endIndex - offSet).apply {
|
||||
disableSwipeToExitFullscreenModeIndex to "disableSwipeToExitFullscreenMode",
|
||||
disableSwipeToEnterFullscreenModeInThePlayerIndex to "disableSwipeToEnterFullscreenModeInThePlayer"
|
||||
).forEach { (walkerIndex, methodName) ->
|
||||
getWalkerMethod(walkerIndex).apply {
|
||||
val index = implementation!!.instructions.lastIndex
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
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")
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
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,8 +13,7 @@ internal object Constants {
|
||||
"19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
|
||||
"19.43.41", // This is the latest version where edge-to-edge display is not enforced on Android 15+.
|
||||
"19.44.39", // This is the only version that has experimental shortcut icons.
|
||||
"19.47.53", // This was the latest version supported by the previous RVX patch.
|
||||
"20.03.43", // This is the latest version supported by the RVX patch.
|
||||
"19.47.53", // This is the latest version supported by the RVX patch.
|
||||
)
|
||||
)
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
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"
|
||||
)
|
@ -0,0 +1,17 @@
|
||||
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"
|
||||
}
|
||||
)
|
@ -116,7 +116,7 @@ val cairoFragmentPatch = resourcePatch(
|
||||
?.let { node ->
|
||||
node.insertNode("Preference", node) {
|
||||
for (index in 0 until node.attributes.length) {
|
||||
with (node.attributes.item(index)) {
|
||||
with(node.attributes.item(index)) {
|
||||
setAttribute(nodeName, nodeValue)
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package app.revanced.patches.youtube.utils.fix.cairo
|
||||
|
||||
import app.revanced.patches.youtube.utils.resourceid.settingsFragment
|
||||
import app.revanced.patches.youtube.utils.resourceid.settingsFragmentCairo
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import app.revanced.patches.youtube.utils.resourceid.settingsFragment
|
||||
import app.revanced.patches.youtube.utils.resourceid.settingsFragmentCairo
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ package app.revanced.patches.youtube.utils.fix.splash
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_32_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.restoreOldSplashAnimationIncluded
|
||||
import org.w3c.dom.Element
|
||||
|
||||
/**
|
||||
@ -19,21 +20,45 @@ val darkModeSplashScreenPatch = resourcePatch(
|
||||
) {
|
||||
dependsOn(versionCheckPatch)
|
||||
|
||||
execute {
|
||||
finalize {
|
||||
if (!is_19_32_or_greater) {
|
||||
return@execute
|
||||
return@finalize
|
||||
}
|
||||
|
||||
arrayOf(
|
||||
"values-night" to "@style/Base.V23.Theme.YouTube.Home",
|
||||
"values-night-v27" to "@style/Base.V27.Theme.YouTube.Home",
|
||||
).forEach { (directory, parent) ->
|
||||
document("res/$directory/styles.xml").use { document ->
|
||||
if (restoreOldSplashAnimationIncluded) {
|
||||
document("res/values-night/styles.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
val childNodes = resourcesNode.childNodes
|
||||
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i) as? Element ?: continue
|
||||
val nodeAttributeName = node.getAttribute("name")
|
||||
if (nodeAttributeName.startsWith("Theme.YouTube.Launcher")) {
|
||||
val nodeAttributeParent = node.getAttribute("parent")
|
||||
|
||||
val style = document.createElement("style")
|
||||
style.setAttribute("name", "Theme.YouTube.Home")
|
||||
style.setAttribute("parent", nodeAttributeParent)
|
||||
|
||||
val windowItem = document.createElement("item")
|
||||
windowItem.setAttribute("name", "android:windowBackground")
|
||||
windowItem.textContent = "@color/yt_black1"
|
||||
style.appendChild(windowItem)
|
||||
|
||||
resourcesNode.removeChild(node)
|
||||
resourcesNode.appendChild(style)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
document("res/values-night-v27/styles.xml").use { document ->
|
||||
// Create a night mode specific override for the splash screen background.
|
||||
val style = document.createElement("style")
|
||||
style.setAttribute("name", "Theme.YouTube.Home")
|
||||
style.setAttribute("parent", parent)
|
||||
style.setAttribute("parent", "@style/Base.V27.Theme.YouTube.Home")
|
||||
|
||||
// Fix status and navigation bar showing white on some Android devices,
|
||||
// such as SDK 28 Android 10 medium tablet.
|
||||
val colorSplashBackgroundColor = "@color/yt_black1"
|
||||
arrayOf(
|
||||
"android:navigationBarColor" to colorSplashBackgroundColor,
|
||||
|
@ -22,7 +22,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
import kotlin.collections.mutableListOf
|
||||
|
||||
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
|
||||
"$EXTENSION_PATH/utils/VideoUtils;"
|
||||
@ -59,7 +58,10 @@ val fullscreenButtonHookPatch = bytecodePatch(
|
||||
getReference<MethodReference>()?.name == "addListener"
|
||||
}
|
||||
val animatorListenerAdapterClass = getInstruction<ReferenceInstruction>(
|
||||
indexOfFirstInstructionReversedOrThrow(addListenerIndex, Opcode.NEW_INSTANCE)
|
||||
indexOfFirstInstructionReversedOrThrow(
|
||||
addListenerIndex,
|
||||
Opcode.NEW_INSTANCE
|
||||
)
|
||||
).reference.toString()
|
||||
return Pair(
|
||||
findMethodOrThrow(animatorListenerAdapterClass) { parameters.isEmpty() },
|
||||
|
@ -86,7 +86,7 @@ internal val appCompatToolbarBackButtonFingerprint = legacyFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Landroid/graphics/drawable/Drawable;",
|
||||
parameters = emptyList(),
|
||||
customFingerprint = { _, classDef ->
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type == "Landroid/support/v7/widget/Toolbar;"
|
||||
},
|
||||
)
|
||||
|
@ -9,15 +9,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
internal val accountIdentityFingerprint = legacyFingerprint(
|
||||
name = "accountIdentityFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
customFingerprint = { method, _ ->
|
||||
method.definingClass.endsWith("${'$'}AutoValue_AccountIdentity;")
|
||||
}
|
||||
)
|
||||
|
||||
internal val editPlaylistConstructorFingerprint = legacyFingerprint(
|
||||
name = "editPlaylistConstructorFingerprint",
|
||||
returnType = "V",
|
||||
|
@ -7,11 +7,13 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.mainactivity.getMainActivityMethod
|
||||
import app.revanced.patches.youtube.utils.auth.authHookPatch
|
||||
import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
|
||||
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
|
||||
import app.revanced.patches.youtube.utils.request.buildRequestPatch
|
||||
import app.revanced.patches.youtube.utils.request.hookBuildRequest
|
||||
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
|
||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
import app.revanced.util.fingerprint.matchOrThrow
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
@ -28,21 +30,13 @@ val playlistPatch = bytecodePatch(
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
mainActivityResolvePatch,
|
||||
buildRequestPatch,
|
||||
dismissPlayerHookPatch,
|
||||
playerTypeHookPatch,
|
||||
videoInformationPatch,
|
||||
authHookPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
// In Incognito mode, sending a request always seems to fail.
|
||||
accountIdentityFingerprint.methodOrThrow().addInstructions(
|
||||
1, """
|
||||
sput-object p3, $EXTENSION_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String;
|
||||
sput-boolean p4, $EXTENSION_CLASS_DESCRIPTOR->isIncognito:Z
|
||||
"""
|
||||
)
|
||||
|
||||
// Get the header to use the auth token.
|
||||
hookBuildRequest("$EXTENSION_CLASS_DESCRIPTOR->setRequestHeaders(Ljava/lang/String;Ljava/util/Map;)V")
|
||||
|
||||
// Open the queue manager by pressing and holding the back button.
|
||||
getMainActivityMethod("onKeyLongPress")
|
||||
.addInstructionsWithLabels(
|
||||
@ -56,35 +50,6 @@ val playlistPatch = bytecodePatch(
|
||||
"""
|
||||
)
|
||||
|
||||
val setVideoIdReference = with (playlistEndpointFingerprint.methodOrThrow()) {
|
||||
val setVideoIdIndex = indexOfSetVideoIdInstruction(this)
|
||||
getInstruction<ReferenceInstruction>(setVideoIdIndex).reference as FieldReference
|
||||
}
|
||||
|
||||
// Users deleted videos via YouTube's flyout menu.
|
||||
editPlaylistFingerprint
|
||||
.matchOrThrow(editPlaylistConstructorFingerprint)
|
||||
.let {
|
||||
it.method.apply {
|
||||
val castIndex = it.patternMatch!!.startIndex
|
||||
val castClass = getInstruction<ReferenceInstruction>(castIndex).reference.toString()
|
||||
|
||||
if (castClass != setVideoIdReference.definingClass) {
|
||||
throw PatchException("Method signature parameter did not match: $castClass")
|
||||
}
|
||||
val castRegister = getInstruction<OneRegisterInstruction>(castIndex).registerA
|
||||
val insertIndex = castIndex + 1
|
||||
val insertRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex, """
|
||||
iget-object v$insertRegister, v$castRegister, $setVideoIdReference
|
||||
invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->removeFromQueue(Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
setPivotBarVisibilityFingerprint
|
||||
.matchOrThrow(setPivotBarVisibilityParentFingerprint)
|
||||
.let {
|
||||
@ -98,5 +63,36 @@ val playlistPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Users deleted videos via YouTube's flyout menu.
|
||||
val setVideoIdReference = with(playlistEndpointFingerprint.methodOrThrow()) {
|
||||
val setVideoIdIndex = indexOfSetVideoIdInstruction(this)
|
||||
getInstruction<ReferenceInstruction>(setVideoIdIndex).reference as FieldReference
|
||||
}
|
||||
|
||||
editPlaylistFingerprint
|
||||
.matchOrThrow(editPlaylistConstructorFingerprint)
|
||||
.let {
|
||||
it.method.apply {
|
||||
val castIndex = it.patternMatch!!.startIndex
|
||||
val castClass =
|
||||
getInstruction<ReferenceInstruction>(castIndex).reference.toString()
|
||||
|
||||
if (castClass != setVideoIdReference.definingClass) {
|
||||
throw PatchException("Method signature parameter did not match: $castClass")
|
||||
}
|
||||
val castRegister = getInstruction<OneRegisterInstruction>(castIndex).registerA
|
||||
val insertIndex = castIndex + 1
|
||||
val insertRegister =
|
||||
getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex, """
|
||||
iget-object v$insertRegister, v$castRegister, $setVideoIdReference
|
||||
invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->removeFromQueue(Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ var is_19_36_or_greater = false
|
||||
private set
|
||||
var is_19_41_or_greater = false
|
||||
private set
|
||||
var is_19_42_or_greater = false
|
||||
private set
|
||||
var is_19_43_or_greater = false
|
||||
private set
|
||||
var is_19_44_or_greater = false
|
||||
@ -116,7 +118,8 @@ val versionCheckPatch = resourcePatch(
|
||||
is_19_32_or_greater = 243305000 <= playStoreServicesVersion
|
||||
is_19_34_or_greater = 243499000 <= playStoreServicesVersion
|
||||
is_19_36_or_greater = 243705000 <= playStoreServicesVersion
|
||||
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
|
||||
is_19_41_or_greater = 244205000 <= playStoreServicesVersion
|
||||
is_19_42_or_greater = 244305000 <= playStoreServicesVersion
|
||||
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
|
||||
is_19_44_or_greater = 244505000 <= playStoreServicesVersion
|
||||
is_19_46_or_greater = 244705000 <= playStoreServicesVersion
|
||||
|
@ -316,9 +316,11 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
fullScreenEngagementPanel = getResourceId(ID, "fullscreen_engagement_panel_holder")
|
||||
horizontalCardList = getResourceId(LAYOUT, "horizontal_card_list")
|
||||
imageOnlyTab = getResourceId(LAYOUT, "image_only_tab")
|
||||
inlineTimeBarColorizedBarPlayedColorDark = getResourceId(COLOR, "inline_time_bar_colorized_bar_played_color_dark")
|
||||
inlineTimeBarColorizedBarPlayedColorDark =
|
||||
getResourceId(COLOR, "inline_time_bar_colorized_bar_played_color_dark")
|
||||
inlineTimeBarLiveSeekAbleRange = getResourceId(COLOR, "inline_time_bar_live_seekable_range")
|
||||
inlineTimeBarPlayedNotHighlightedColor = getResourceId(COLOR, "inline_time_bar_played_not_highlighted_color")
|
||||
inlineTimeBarPlayedNotHighlightedColor =
|
||||
getResourceId(COLOR, "inline_time_bar_played_not_highlighted_color")
|
||||
insetOverlayViewLayout = getResourceId(ID, "inset_overlay_view_layout")
|
||||
interstitialsContainer = getResourceId(ID, "interstitials_container")
|
||||
insetElementsWrapper = getResourceId(LAYOUT, "inset_elements_wrapper")
|
||||
@ -328,14 +330,19 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
modernMiniPlayerClose = getResourceId(ID, "modern_miniplayer_close")
|
||||
modernMiniPlayerExpand = getResourceId(ID, "modern_miniplayer_expand")
|
||||
modernMiniPlayerForwardButton = getResourceId(ID, "modern_miniplayer_forward_button")
|
||||
modernMiniPlayerOverlayActionButton = getResourceId(ID, "modern_miniplayer_overlay_action_button")
|
||||
modernMiniPlayerOverlayActionButton =
|
||||
getResourceId(ID, "modern_miniplayer_overlay_action_button")
|
||||
modernMiniPlayerRewindButton = getResourceId(ID, "modern_miniplayer_rewind_button")
|
||||
musicAppDeeplinkButtonView = getResourceId(ID, "music_app_deeplink_button_view")
|
||||
notificationBigPictureIconWidth = getResourceId(DIMEN, "notification_big_picture_icon_width")
|
||||
offlineActionsVideoDeletedUndoSnackbarText = getResourceId(STRING, "offline_actions_video_deleted_undo_snackbar_text")
|
||||
notificationBigPictureIconWidth =
|
||||
getResourceId(DIMEN, "notification_big_picture_icon_width")
|
||||
offlineActionsVideoDeletedUndoSnackbarText =
|
||||
getResourceId(STRING, "offline_actions_video_deleted_undo_snackbar_text")
|
||||
playerCollapseButton = getResourceId(ID, "player_collapse_button")
|
||||
playerControlPreviousButtonTouchArea = getResourceId(ID, "player_control_previous_button_touch_area")
|
||||
playerControlNextButtonTouchArea = getResourceId(ID, "player_control_next_button_touch_area")
|
||||
playerControlPreviousButtonTouchArea =
|
||||
getResourceId(ID, "player_control_previous_button_touch_area")
|
||||
playerControlNextButtonTouchArea =
|
||||
getResourceId(ID, "player_control_next_button_touch_area")
|
||||
playerVideoTitleView = getResourceId(ID, "player_video_title_view")
|
||||
posterArtWidthDefault = getResourceId(DIMEN, "poster_art_width_default")
|
||||
qualityAuto = getResourceId(STRING, "quality_auto")
|
||||
@ -356,7 +363,8 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
relatedChipCloudMargin = getResourceId(LAYOUT, "related_chip_cloud_reduced_margins")
|
||||
rightComment = getResourceId(DRAWABLE, "ic_right_comment_32c")
|
||||
scrimOverlay = getResourceId(ID, "scrim_overlay")
|
||||
seekEasyHorizontalTouchOffsetToStartScrubbing = getResourceId(DIMEN, "seek_easy_horizontal_touch_offset_to_start_scrubbing")
|
||||
seekEasyHorizontalTouchOffsetToStartScrubbing =
|
||||
getResourceId(DIMEN, "seek_easy_horizontal_touch_offset_to_start_scrubbing")
|
||||
seekUndoEduOverlayStub = getResourceId(ID, "seek_undo_edu_overlay_stub")
|
||||
settingsFragment = getResourceId(XML, "settings_fragment")
|
||||
settingsFragmentCairo = getResourceId(XML, "settings_fragment_cairo")
|
||||
@ -369,20 +377,26 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
toolTipContentView = getResourceId(LAYOUT, "tooltip_content_view")
|
||||
totalTime = getResourceId(STRING, "total_time")
|
||||
touchArea = getResourceId(ID, "touch_area")
|
||||
videoQualityBottomSheet = getResourceId(LAYOUT, "video_quality_bottom_sheet_list_fragment_title")
|
||||
videoQualityBottomSheet =
|
||||
getResourceId(LAYOUT, "video_quality_bottom_sheet_list_fragment_title")
|
||||
varispeedUnavailableTitle = getResourceId(STRING, "varispeed_unavailable_title")
|
||||
verticalTouchOffsetToEnterFineScrubbing = getResourceId(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing")
|
||||
verticalTouchOffsetToStartFineScrubbing = getResourceId(DIMEN, "vertical_touch_offset_to_start_fine_scrubbing")
|
||||
videoQualityUnavailableAnnouncement = getResourceId(STRING, "video_quality_unavailable_announcement")
|
||||
verticalTouchOffsetToEnterFineScrubbing =
|
||||
getResourceId(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing")
|
||||
verticalTouchOffsetToStartFineScrubbing =
|
||||
getResourceId(DIMEN, "vertical_touch_offset_to_start_fine_scrubbing")
|
||||
videoQualityUnavailableAnnouncement =
|
||||
getResourceId(STRING, "video_quality_unavailable_announcement")
|
||||
videoZoomSnapIndicator = getResourceId(ID, "video_zoom_snap_indicator")
|
||||
voiceSearch = getResourceId(ID, "voice_search")
|
||||
youTubeControlsOverlaySubtitleButton = getResourceId(LAYOUT, "youtube_controls_overlay_subtitle_button")
|
||||
youTubeControlsOverlaySubtitleButton =
|
||||
getResourceId(LAYOUT, "youtube_controls_overlay_subtitle_button")
|
||||
youTubeLogo = getResourceId(ID, "youtube_logo")
|
||||
ytCallToAction = getResourceId(ATTR, "ytCallToAction")
|
||||
ytFillBell = getResourceId(DRAWABLE, "yt_fill_bell_black_24")
|
||||
ytOutlineLibrary = getResourceId(DRAWABLE, "yt_outline_library_black_24")
|
||||
ytOutlineMoonZ = getResourceId(DRAWABLE, "yt_outline_moon_z_vd_theme_24")
|
||||
ytOutlinePictureInPictureWhite = getResourceId(DRAWABLE, "yt_outline_picture_in_picture_white_24")
|
||||
ytOutlinePictureInPictureWhite =
|
||||
getResourceId(DRAWABLE, "yt_outline_picture_in_picture_white_24")
|
||||
ytOutlineVideoCamera = getResourceId(DRAWABLE, "yt_outline_video_camera_black_24")
|
||||
ytOutlineXWhite = getResourceId(DRAWABLE, "yt_outline_x_white_24")
|
||||
ytPremiumWordMarkHeader = getResourceId(ATTR, "ytPremiumWordmarkHeader")
|
||||
|
@ -88,9 +88,6 @@ internal object ResourceUtils {
|
||||
updatePatchStatusSettings("Icon", "@string/revanced_icon_$iconName")
|
||||
}
|
||||
|
||||
fun updatePatchStatusLabel(appName: String) =
|
||||
updatePatchStatusSettings("Label", appName)
|
||||
|
||||
fun updatePatchStatusTheme(themeName: String) =
|
||||
updatePatchStatusSettings("Theme", themeName)
|
||||
|
||||
|
@ -1,15 +1,18 @@
|
||||
package app.revanced.patches.youtube.utils.settings
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableLongEncodedValue
|
||||
import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_PATH
|
||||
import app.revanced.patches.shared.mainactivity.injectConstructorMethodCall
|
||||
import app.revanced.patches.shared.mainactivity.injectOnCreateMethodCall
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
|
||||
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.utils.fix.attributes.themeAttributesPatch
|
||||
@ -25,11 +28,13 @@ import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.findMethodOrThrow
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.removeStringsElements
|
||||
import app.revanced.util.valueOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableLongEncodedValue
|
||||
import org.w3c.dom.Element
|
||||
import java.nio.file.Files
|
||||
import java.util.jar.Manifest
|
||||
@ -81,14 +86,21 @@ private val settingsBytecodePatch = bytecodePatch(
|
||||
EXTENSION_UTILS_CLASS_DESCRIPTOR,
|
||||
"setActivity"
|
||||
)
|
||||
|
||||
findMethodOrThrow(PATCH_STATUS_CLASS_DESCRIPTOR) {
|
||||
name == "PatchedTime"
|
||||
}.replaceInstruction(
|
||||
0,
|
||||
"const-wide v0, ${MutableLongEncodedValue(ImmutableLongEncodedValue(System.currentTimeMillis()))}L"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_ELEMENT = "@string/about_key"
|
||||
private const val DEFAULT_ELEMENT = "@string/parent_tools_key"
|
||||
private const val DEFAULT_LABEL = "RVX"
|
||||
|
||||
private val SETTINGS_ELEMENTS_MAP = mapOf(
|
||||
"Parent settings" to "@string/parent_tools_key",
|
||||
"Parent settings" to DEFAULT_ELEMENT,
|
||||
"General" to "@string/general_key",
|
||||
"Account" to "@string/account_switcher_key",
|
||||
"Data saving" to "@string/data_saving_settings_key",
|
||||
@ -109,7 +121,7 @@ private val SETTINGS_ELEMENTS_MAP = mapOf(
|
||||
"Live chat" to "@string/live_chat_key",
|
||||
"Captions" to "@string/captions_key",
|
||||
"Accessibility" to "@string/accessibility_settings_key",
|
||||
"About" to DEFAULT_ELEMENT
|
||||
"About" to "@string/about_key"
|
||||
)
|
||||
|
||||
private lateinit var settingsLabel: String
|
||||
|
@ -11,6 +11,8 @@ import app.revanced.patches.shared.customspeed.customPlaybackSpeedPatch
|
||||
import app.revanced.patches.shared.litho.addLithoFilter
|
||||
import app.revanced.patches.shared.litho.lithoFilterPatch
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch
|
||||
import app.revanced.patches.youtube.utils.dismiss.hookDismissObserver
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.VIDEO_PATH
|
||||
@ -25,7 +27,6 @@ import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.video.information.hookBackgroundPlayVideoInformation
|
||||
import app.revanced.patches.youtube.video.information.hookShortsVideoInformation
|
||||
import app.revanced.patches.youtube.video.information.hookVideoInformation
|
||||
import app.revanced.patches.youtube.video.information.onCreateHook
|
||||
import app.revanced.patches.youtube.video.information.speedSelectionInsertMethod
|
||||
@ -87,6 +88,7 @@ val videoPlaybackPatch = bytecodePatch(
|
||||
),
|
||||
flyoutMenuHookPatch,
|
||||
lithoFilterPatch,
|
||||
dismissPlayerHookPatch,
|
||||
playerTypeHookPatch,
|
||||
recyclerViewTreeObserverPatch,
|
||||
shortsPlaybackPatch,
|
||||
@ -162,13 +164,15 @@ val videoPlaybackPatch = bytecodePatch(
|
||||
loadVideoParamsFingerprint.matchOrThrow(loadVideoParamsParentFingerprint).let {
|
||||
it.method.apply {
|
||||
val targetIndex = it.patternMatch!!.endIndex
|
||||
val targetReference = getInstruction<ReferenceInstruction>(targetIndex).reference as MethodReference
|
||||
val targetReference =
|
||||
getInstruction<ReferenceInstruction>(targetIndex).reference as MethodReference
|
||||
|
||||
findMethodOrThrow(definingClass) {
|
||||
name == targetReference.name
|
||||
}.apply {
|
||||
val insertIndex = implementation!!.instructions.lastIndex
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
val insertRegister =
|
||||
getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex, """
|
||||
@ -181,9 +185,9 @@ val videoPlaybackPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
hookBackgroundPlayVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
|
||||
hookShortsVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newShortsVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
|
||||
hookVideoInformation("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
|
||||
hookPlayerResponseVideoId("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->fetchMusicRequest(Ljava/lang/String;Z)V")
|
||||
hookDismissObserver("$EXTENSION_PLAYBACK_SPEED_CLASS_DESCRIPTOR->onDismiss()V")
|
||||
|
||||
updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "RememberPlaybackSpeed")
|
||||
|
||||
|
@ -26,7 +26,8 @@ val playbackStartDescriptorPatch = bytecodePatch(
|
||||
&& reference.returnType == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
playbackStartVideoIdReference = getInstruction<ReferenceInstruction>(stringMethodIndex).reference
|
||||
playbackStartVideoIdReference =
|
||||
getInstruction<ReferenceInstruction>(stringMethodIndex).reference
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ val playerResponseMethodHookPatch = bytecodePatch(
|
||||
playerResponseMethod.apply {
|
||||
val setIndex = parameterTypes.indexOfFirst { it == "Ljava/util/Set;" }
|
||||
val parameterSize = parameterTypes.size
|
||||
val relativeIndex = parameterTypes.subList(setIndex, parameterSize - 1).indexOfFirst { it == "Z" }
|
||||
val relativeIndex =
|
||||
parameterTypes.subList(setIndex, parameterSize - 1).indexOfFirst { it == "Z" }
|
||||
|
||||
// YouTube 18.29 ~ 19.22 : p11
|
||||
// YouTube 19.23 ~ 20.09 : p12
|
||||
|
@ -22,12 +22,16 @@ import app.revanced.patches.shared.mapping.resourceMappingPatch
|
||||
import app.revanced.util.Utils.printWarn
|
||||
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.MethodParameter
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
@ -37,6 +41,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import java.util.EnumSet
|
||||
|
||||
const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX"
|
||||
|
||||
@ -52,6 +57,121 @@ fun parametersEqual(
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting from and including the instruction at index [startIndex],
|
||||
* finds the next register that is wrote to and not read from. If a return instruction
|
||||
* is encountered, then the lowest unused register is returned.
|
||||
*
|
||||
* This method can return a non 4-bit register, and the calling code may need to temporarily
|
||||
* swap register contents if a 4-bit register is required.
|
||||
*
|
||||
* @param startIndex Inclusive starting index.
|
||||
* @param registersToExclude Registers to exclude, and consider as used. For most use cases,
|
||||
* all registers used in injected code should be specified.
|
||||
* @throws IllegalArgumentException If a branch or conditional statement is encountered
|
||||
* before a suitable register is found.
|
||||
*/
|
||||
internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): Int {
|
||||
if (implementation == null) {
|
||||
throw IllegalArgumentException("Method has no implementation: $this")
|
||||
}
|
||||
if (startIndex < 0 || startIndex >= instructions.count()) {
|
||||
throw IllegalArgumentException("startIndex out of bounds: $startIndex")
|
||||
}
|
||||
|
||||
// All registers used by an instruction.
|
||||
fun Instruction.getRegistersUsed() = when (this) {
|
||||
is FiveRegisterInstruction -> listOf(registerC, registerD, registerE, registerF, registerG)
|
||||
is ThreeRegisterInstruction -> listOf(registerA, registerB, registerC)
|
||||
is TwoRegisterInstruction -> listOf(registerA, registerB)
|
||||
is OneRegisterInstruction -> listOf(registerA)
|
||||
is RegisterRangeInstruction -> (startRegister until (startRegister + registerCount)).toList()
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
// Register that is written to by an instruction.
|
||||
fun Instruction.getRegisterWritten() = when (this) {
|
||||
is ThreeRegisterInstruction -> registerA
|
||||
is TwoRegisterInstruction -> registerA
|
||||
is OneRegisterInstruction -> registerA
|
||||
else -> throw IllegalStateException("Not a write instruction: $this")
|
||||
}
|
||||
|
||||
val writeOpcodes = EnumSet.of(
|
||||
NEW_INSTANCE, NEW_ARRAY,
|
||||
MOVE, MOVE_FROM16, MOVE_16, MOVE_WIDE, MOVE_WIDE_FROM16, MOVE_WIDE_16, MOVE_OBJECT,
|
||||
MOVE_OBJECT_FROM16, MOVE_OBJECT_16, MOVE_RESULT, MOVE_RESULT_WIDE, MOVE_RESULT_OBJECT, MOVE_EXCEPTION,
|
||||
IGET, IGET_WIDE, IGET_OBJECT, IGET_BOOLEAN, IGET_BYTE, IGET_CHAR, IGET_SHORT,
|
||||
SGET, SGET_WIDE, SGET_OBJECT, SGET_BOOLEAN, SGET_BYTE, SGET_CHAR, SGET_SHORT,
|
||||
)
|
||||
|
||||
val branchOpcodes = EnumSet.of(
|
||||
GOTO, GOTO_16, GOTO_32,
|
||||
IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE,
|
||||
IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ,
|
||||
)
|
||||
|
||||
val returnOpcodes = EnumSet.of(
|
||||
RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT,
|
||||
)
|
||||
|
||||
// Highest 4-bit register available, exclusive. Ideally return a free register less than this.
|
||||
val maxRegister4Bits = 16
|
||||
var bestFreeRegisterFound: Int? = null
|
||||
val usedRegisters = registersToExclude.toMutableSet()
|
||||
|
||||
for (i in startIndex until instructions.count()) {
|
||||
val instruction = getInstruction(i)
|
||||
|
||||
if (instruction.opcode in returnOpcodes) {
|
||||
// Method returns. Use lowest register that hasn't been encountered.
|
||||
val freeRegister = (0 until implementation!!.registerCount).find {
|
||||
it !in usedRegisters
|
||||
}
|
||||
if (freeRegister != null) {
|
||||
return freeRegister
|
||||
}
|
||||
if (bestFreeRegisterFound != null) {
|
||||
return bestFreeRegisterFound;
|
||||
}
|
||||
|
||||
// Somehow every method register was read from before any register was wrote to.
|
||||
// In practice this never occurs.
|
||||
throw IllegalArgumentException("Could not find a free register from startIndex: " +
|
||||
"$startIndex excluding: $registersToExclude")
|
||||
}
|
||||
|
||||
if (instruction.opcode in branchOpcodes) {
|
||||
if (bestFreeRegisterFound != null) {
|
||||
return bestFreeRegisterFound;
|
||||
}
|
||||
// This method is simple and does not follow branching.
|
||||
throw IllegalArgumentException("Encountered a branch statement before a free register could be found")
|
||||
}
|
||||
|
||||
if (instruction.opcode in writeOpcodes) {
|
||||
val freeRegister = instruction.getRegisterWritten()
|
||||
if (freeRegister !in usedRegisters) {
|
||||
if (freeRegister < maxRegister4Bits) {
|
||||
// Found an ideal register.
|
||||
return freeRegister
|
||||
}
|
||||
|
||||
// Continue searching for a 4-bit register if available.
|
||||
if (bestFreeRegisterFound == null || freeRegister < bestFreeRegisterFound) {
|
||||
bestFreeRegisterFound = freeRegister
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usedRegisters.addAll(instruction.getRegistersUsed())
|
||||
}
|
||||
|
||||
// Cannot be reached since a branch or return statement will
|
||||
// be encountered before the end of the method.
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
|
||||
*
|
||||
|
@ -16,7 +16,7 @@ internal object FilesCompat {
|
||||
// Check for the existence of java.nio.file.Files class
|
||||
Class.forName("java.nio.file.Files")
|
||||
false
|
||||
} catch (_ : ClassNotFoundException) {
|
||||
} catch (_: ClassNotFoundException) {
|
||||
// Under Android 8.0
|
||||
true
|
||||
}
|
||||
|
@ -183,7 +183,7 @@
|
||||
<string name="revanced_restore_old_style_library_shelf_title">이전 보관함 선반으로 복원</string>
|
||||
<string name="revanced_restore_old_style_library_shelf_summary">이전 보관함 탭으로 복원합니다. (실험 기능)</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_title">시청 경고 다이얼로그 제거</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_summary">"다음 콘텐츠를 재생하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 콘텐츠\n• 혐오감을 주는 콘텐츠\n• 자살 또는 자해와 관련된 콘텐츠 ...\n\n이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다."</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_summary">"다음 콘텐츠를 재생하기 전에 표시되는 시청 경고 다이얼로그를 제거합니다:\n• 연령 제한 콘텐츠\n• 자살 또는 자해와 관련된 콘텐츠, etc.\n\n이 설정은 다이얼로그를 자동으로 허용하기만 하며 연령 제한(성인인증 절차)을 우회할 수 없습니다."</string>
|
||||
<string name="revanced_spoof_app_version_title">앱 버전 변경</string>
|
||||
<string name="revanced_spoof_app_version_summary">"이전 앱 버전으로 변경합니다.
|
||||
|
||||
|
@ -26,11 +26,13 @@ Please download %2$s from the website."</string>
|
||||
<string name="revanced_queue_manager_add_to_queue">Add to queue</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_open_queue">Add to queue and open queue</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_play_video">Add to queue and play video</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_reload_video">Add to queue and reload video</string>
|
||||
<string name="revanced_queue_manager_external_downloader">External downloader</string>
|
||||
<string name="revanced_queue_manager_open_queue">Open queue</string>
|
||||
<string name="revanced_queue_manager_queue">Queue</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue">Remove from queue</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_open_queue">Remove from queue and open queue</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_reload_video">Remove from queue and reload video</string>
|
||||
<string name="revanced_queue_manager_remove_queue">Remove queue</string>
|
||||
<string name="revanced_queue_manager_save_queue">Save queue</string>
|
||||
<string name="revanced_queue_manager_user_dialog_message">"Instead of opening an external downloader, open the queue manager dialog.
|
||||
@ -2301,8 +2303,11 @@ Click to see more information."</string>
|
||||
<string name="revanced_preference_screen_patch_information_title">Patch information</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Information about applied patches.</string>
|
||||
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Tool used</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<string name="revanced_preference_category_app_info">App info</string>
|
||||
<string name="revanced_app_name_title">App name</string>
|
||||
<string name="revanced_app_version_title">App version</string>
|
||||
<string name="revanced_patched_date_title">Patched date</string>
|
||||
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Others</string>
|
||||
@ -2314,7 +2319,6 @@ Click to see more information."</string>
|
||||
<string name="revanced_icon_revancify_blue">Revancify Blue</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Red</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">Stock</string>
|
||||
<string name="revanced_patches_excluded">Excluded</string>
|
||||
<string name="revanced_patches_included">Included</string>
|
||||
<string name="revanced_theme_default">Stock</string>
|
||||
|
@ -912,10 +912,12 @@
|
||||
|
||||
<PreferenceCategory android:title="@string/revanced_preference_category_patch_information" android:layout="@layout/revanced_settings_preferences_category"/>
|
||||
<PreferenceScreen android:title="@string/revanced_preference_screen_patch_information_title" android:key="revanced_preference_screen_patch_information" android:summary="@string/revanced_preference_screen_patch_information_summary" >
|
||||
<PreferenceCategory android:title="@string/revanced_preference_category_tool_used" android:layout="@layout/revanced_settings_preferences_category" >
|
||||
<PreferenceCategory android:title="@string/revanced_preference_category_app_info" android:layout="@layout/revanced_settings_preferences_category" >
|
||||
<Preference android:title="@string/revanced_app_name_title" android:key="revanced_app_name" android:selectable="false" />
|
||||
<Preference android:title="@string/revanced_app_version_title" android:key="revanced_app_version" android:selectable="false" />
|
||||
<Preference android:title="ReVanced Patches" android:selectable="false" android:summary="" />
|
||||
<Preference android:title="@string/revanced_patched_date_title" android:key="revanced_patched_date" android:selectable="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/revanced_preference_screen_ads_title" android:layout="@layout/revanced_settings_preferences_category">
|
||||
<Preference android:title="Hide ads" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||
</PreferenceCategory>
|
||||
@ -999,7 +1001,6 @@
|
||||
<Preference android:title="Hide Shorts dimming" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||
<Preference android:title="Hide shortcuts" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||
<Preference android:title="Icon" android:summary="@string/revanced_icon_default" android:selectable="false"/>
|
||||
<Preference android:title="Label" android:summary="@string/revanced_label_default" android:selectable="false"/>
|
||||
<Preference android:title="Return YouTube Dislike" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||
<Preference android:title="Return YouTube Username" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||
<Preference android:title="SponsorBlock" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||
|
@ -22,11 +22,13 @@
|
||||
<string name="revanced_queue_manager_add_to_queue">إضافة إلى قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_open_queue">إضافة إلى قائمة الانتظار وفتح قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_play_video">إضافة إلى قائمة الانتظار وتشغيل الفيديو</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_reload_video">إضافة إلى قائمة الانتظار وإعادة تحميل الفيديو</string>
|
||||
<string name="revanced_queue_manager_external_downloader">أداة التنزيل الخارجي</string>
|
||||
<string name="revanced_queue_manager_open_queue">فتح قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_queue">قائمة الإنتظار</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue">إزالة من قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_open_queue">إزالة من قائمة الانتظار وفتح قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_reload_video">إزالة من قائمة الانتظار وإعادة تحميل الفيديو</string>
|
||||
<string name="revanced_queue_manager_remove_queue">إزالة قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_save_queue">حفظ قائمة الانتظار</string>
|
||||
<string name="revanced_queue_manager_user_dialog_message">"بدلاً من فتح برنامج تنزيل خارجي، افتح نافذة مدير قائمة الانتظار.
|
||||
@ -203,18 +205,18 @@
|
||||
<string name="revanced_hide_get_premium_summary_on">تم إخفاء الترقية لـ YouTube Premium.</string>
|
||||
<string name="revanced_hide_get_premium_summary_off">يتم عرض الترقية لـ YouTube Premium.</string>
|
||||
<!-- PreferenceScreen: Alternative thumbnails -->
|
||||
<string name="revanced_preference_screen_alt_thumbnails_title">مُصغَّرات فيديو بديلة</string>
|
||||
<string name="revanced_preference_screen_alt_thumbnails_title">مصغرات فيديو بديلة</string>
|
||||
<string name="revanced_alt_thumbnail_home_title">علامة تبويب الصفحة الرئيسية</string>
|
||||
<string name="revanced_alt_thumbnail_player_title">قوائم تشغيل المشغل، التوصيات</string>
|
||||
<string name="revanced_alt_thumbnail_search_title">نتائج البحث</string>
|
||||
<string name="revanced_alt_thumbnail_subscriptions_title">علامة تبويب الاشتراكات</string>
|
||||
<string name="revanced_alt_thumbnail_library_title">علامة التبويب أنت</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_1">المصّغرات الأصلية</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & المصّغرات الأصلية</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_1">المصغرات الأصلية</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & المصغرات الأصلية</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & اللقطات الثابتة</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">اللقطات الثابتة</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"يوفر DeArrow مُصغَّرات فيديو تم جمعها من الجمهور لفيديوهات YouTube. غالبًا ما تكون مُصغَّرات الفيديو هذه ذات صلة أكثر من تلك التي يقدمها موقع YouTube.
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"يوفر DeArrow مصغرات فيديو تم جمعها من الجمهور لفيديوهات YouTube. غالبًا ما تكون مصغرات الفيديو هذه ذات صلة أكثر من تلك التي يقدمها موقع YouTube.
|
||||
|
||||
اذا تم تمكين هذا الخيار، سيتم إرسال عناوين URL للفيديو إلى خادم API ولن يتم إرسال أي بيانات أخرى. إذا لم يكن الفيديو يحتوي على مُصغَّرات لـ DeArrow، فسيتم عرض اللقطات الأصلية أو الثابتة.
|
||||
|
||||
@ -228,7 +230,7 @@
|
||||
<string name="revanced_alt_thumbnail_stills_about_title">لقطات الفيديو الثابتة</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_summary">يتم التقاط اللقطات الثابتة من بداية أو منتصف أو نهاية كل فيديو. هذه الصور مدمجة في YouTube ولا يتم استخدام أي واجهة برمجة تطبيقات خارجية.</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_title">استخدام اللقطات الثابتة السريعة</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_summary_on">استخدام اللقطات متوسطة الجودة. سيتم تحميل المُصغَّرات بشكل أسرع، ولكن البث المباشر أو المقاطع التي لم يتم إصدارها أو القديمة جدًا قد تعرض مُصغَّرات فارغة.</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_summary_on">استخدام اللقطات متوسطة الجودة. سيتم تحميل المصغرات بشكل أسرع، ولكن البث المباشر أو المقاطع التي لم يتم إصدارها أو القديمة جدًا قد تعرض مصغرات فارغة.</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_summary_off">استخدام لقطات الفيديو الثابتة بجودة عالية.</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_title">وقت الفيديو لأخذ اللقطات الثابتة منه</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_1">بداية الفيديو</string>
|
||||
@ -246,9 +248,9 @@
|
||||
<string name="revanced_hide_album_card_title">إخفاء بطاقات الألبوم</string>
|
||||
<string name="revanced_hide_album_card_summary_on">تم إخفاء بطاقات الألبوم.</string>
|
||||
<string name="revanced_hide_album_card_summary_off">يتم عرض بطاقات الألبوم.</string>
|
||||
<string name="revanced_hide_feed_captions_button_title">إخفاء زر التَرْجَمَة</string>
|
||||
<string name="revanced_hide_feed_captions_button_on">تم إخفاء زر التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_feed_captions_button_off">يتم عرض زر التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_feed_captions_button_title">إخفاء زر الترجمة</string>
|
||||
<string name="revanced_hide_feed_captions_button_on">تم إخفاء زر الترجمة.</string>
|
||||
<string name="revanced_hide_feed_captions_button_off">يتم عرض زر الترجمة.</string>
|
||||
<string name="revanced_hide_carousel_shelf_title">إخفاء الرفوف الدوارة</string>
|
||||
<string name="revanced_hide_carousel_shelf_summary_on">"تم إخفاء الرفوف الدوارة، مثل:
|
||||
• أخبار عاجلة
|
||||
@ -360,8 +362,8 @@
|
||||
<string name="revanced_hide_feed_flyout_menu_summary_on">تم تمكين فلتر القائمة المنبثقة بالموجز.</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_summary_off">تم تعطيل فلتر القائمة المنبثقة بالموجز.</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_type_title">نوع تصفية القائمة المنبثقة للموجز</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_type_summary_on">قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك استخدام <b>\'تشغيل التالي\'</b> أو <b>\'في قائمة المحتوى الرئيسي\'</b> ككلمات رئيسية.</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_type_summary_off">قم بالتصفية إذا كانت هناك تطابقات.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك فقط استخدام <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b> ككلمات رئيسية.</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_type_summary_on">قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك استخدام <b>\'تشغيل التالي\'</b> أو <b>\'في قائمة المحتوى الرئيسي\'</b> ككلمات مفتاحية.</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_type_summary_off">قم بالتصفية إذا كانت هناك تطابقات.<br><br>لإخفاء القائمة <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b>، يمكنك فقط استخدام <b>\'تشغيل التالي في قائمة المحتوى التالي\'</b> ككلمات مفتاحية.</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_strings_title">تصفية القائمة المنبثقة بالموجز</string>
|
||||
<string name="revanced_hide_feed_flyout_menu_filter_strings_summary">قائمة بأسماء القائمة المنبثقة المراد تصفيتها، مفصولة بسطور جديدة.</string>
|
||||
<!-- PreferenceScreen: Feed, PreferenceCategory: Feed, PreferenceScreen: Video filter -->
|
||||
@ -562,8 +564,8 @@
|
||||
<string name="revanced_hide_account_menu_summary">"إخفاء عناصر قائمة الحساب وعلامة التبويب أنت.
|
||||
قد لا يتم إخفاء بعض المكونات."</string>
|
||||
<string name="revanced_hide_account_menu_filter_type_title">نوع تصفية قائمة الحساب</string>
|
||||
<string name="revanced_hide_account_menu_filter_type_summary_on">قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك استخدام <b>\'YouTube Premium\'</b> أو <b>\'Premium\'</b> ككلمات رئيسية.</string>
|
||||
<string name="revanced_hide_account_menu_filter_type_summary_off">قم بالتصفية إذا تطابقت النتائج.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك فقط استخدام <b>\'الحصول على YouTube Premium\'</b> ككلمات رئيسية.</string>
|
||||
<string name="revanced_hide_account_menu_filter_type_summary_on">قم بالتصفية إذا كان يحتوي على.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك استخدام <b>\'YouTube Premium\'</b> أو <b>\'Premium\'</b> ككلمات مفتاحية.</string>
|
||||
<string name="revanced_hide_account_menu_filter_type_summary_off">قم بالتصفية إذا تطابقت النتائج.<br><br>لإخفاء قائمة <b>\'الحصول على YouTube Premium\'</b>، يمكنك فقط استخدام <b>\'الحصول على YouTube Premium\'</b> ككلمات مفتاحية.</string>
|
||||
<string name="revanced_hide_account_menu_filter_strings_title">تعديل فلتر قائمة الحساب</string>
|
||||
<string name="revanced_hide_account_menu_filter_strings_summary">قائمة بأسماء قائمة الحسابات المراد تصفيتها، مفصولة بسطور جديدة.</string>
|
||||
<string name="revanced_hide_handle_title">إخفاء الاسم المعرِّف</string>
|
||||
@ -981,12 +983,12 @@
|
||||
<string name="revanced_hide_player_flyout_menu_audio_track_title">إخفاء قائمة المقطع الصوتي</string>
|
||||
<string name="revanced_hide_player_flyout_menu_audio_track_summary_on">تم إخفاء قائمة المقطع الصوتي.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_audio_track_summary_off">يتم عرض قائمة المقطع الصوتي.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_title">إخفاء قائمة التَرْجَمَة</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_summary_on">تم إخفاء قائمة التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_summary_off">يتم عرض قائمة التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_footer_title">إخفاء تذييل قائمة التَرْجَمَة</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_footer_summary_on">تم إخفاء تذييل قائمة التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_footer_summary_off">يتم عرض تذييل قائمة التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_title">إخفاء قائمة الترجمة</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_summary_on">تم إخفاء قائمة الترجمة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_summary_off">يتم عرض قائمة الترجمة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_footer_title">إخفاء تذييل قائمة الترجمة</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_footer_summary_on">تم إخفاء تذييل قائمة الترجمة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_captions_footer_summary_off">يتم عرض تذييل قائمة الترجمة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_lock_screen_title">إخفاء قائمة قفل الشاشة</string>
|
||||
<string name="revanced_hide_player_flyout_menu_lock_screen_summary_on">تم إخفاء قائمة قفل الشاشة.</string>
|
||||
<string name="revanced_hide_player_flyout_menu_lock_screen_summary_off">يتم عرض قائمة قفل الشاشة.</string>
|
||||
@ -1140,7 +1142,7 @@
|
||||
<string name="revanced_miniplayer_type_title">نوع المشغل المصغر</string>
|
||||
<string name="revanced_miniplayer_type_entry_0">معطَّل</string>
|
||||
<string name="revanced_miniplayer_type_entry_1">الأصلي</string>
|
||||
<string name="revanced_miniplayer_type_entry_2">الحد الأدنى</string>
|
||||
<string name="revanced_miniplayer_type_entry_2">Minimal</string>
|
||||
<string name="revanced_miniplayer_type_entry_3">الجهاز اللوحي</string>
|
||||
<string name="revanced_miniplayer_type_entry_4">حديث 1</string>
|
||||
<string name="revanced_miniplayer_type_entry_5">حديث 2</string>
|
||||
@ -1191,9 +1193,9 @@
|
||||
<string name="revanced_hide_player_autoplay_button_title">إخفاء زر التشغيل التلقائي</string>
|
||||
<string name="revanced_hide_player_autoplay_button_summary_on">تم إخفاء زر التشغيل التلقائي.</string>
|
||||
<string name="revanced_hide_player_autoplay_button_summary_off">يتم عرض زر التشغيل التلقائي.</string>
|
||||
<string name="revanced_hide_player_captions_button_title">إخفاء زر التَرْجَمَة</string>
|
||||
<string name="revanced_hide_player_captions_button_summary_on">تم إخفاء زر التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_player_captions_button_summary_off">يتم عرض زر التَرْجَمَة.</string>
|
||||
<string name="revanced_hide_player_captions_button_title">إخفاء زر الترجمة</string>
|
||||
<string name="revanced_hide_player_captions_button_summary_on">تم إخفاء زر الترجمة.</string>
|
||||
<string name="revanced_hide_player_captions_button_summary_off">يتم عرض زر الترجمة.</string>
|
||||
<string name="revanced_hide_player_cast_button_title">إخفاء زر البث</string>
|
||||
<string name="revanced_hide_player_cast_button_summary_on">تم إخفاء زر البث.</string>
|
||||
<string name="revanced_hide_player_cast_button_summary_off">يتم عرض زر البث.</string>
|
||||
@ -1637,9 +1639,7 @@
|
||||
<string name="revanced_swipe_overlay_rect_size_summary">النسبة المئوية لمساحة الشاشة القابلة للتمرير السريع.\n\nملاحظة: سيؤدي هذا أيضًا إلى تغيير حجم مساحة الشاشة لإيماءة النقر المزدوج للتقديم أو التأخير.</string>
|
||||
<string name="revanced_swipe_overlay_rect_size_invalid_toast">لا يمكن أن يزيد حجم المنطقة القابلة للتمرير السريع عن 50.</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">مهلة واجهة إيماءة التمرير</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">مقدار الوقت الذي تظهر فيه واجهة التمرير بعد التغيير (بجزء الثانية).
|
||||
|
||||
الافتراضي:500</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">أجزاء الثانية التي تكون فيها الواجهة مرئية.</string>
|
||||
<string name="revanced_swipe_brightness_sensitivity_title">حساسية تمرير مستوى السطوع</string>
|
||||
<string name="revanced_swipe_brightness_sensitivity_summary">تكوين الحد الأدنى للمسافة لتمرير السطوع بين 1 و1000 (%).\nكلما كانت المسافة الدنيا أقصر، كلما تغيرت مستويات السطوع بشكل أسرع.</string>
|
||||
<string name="revanced_swipe_brightness_sensitivity_invalid_toast">يجب أن تكون حساسية تمرير مستوى السطوع بين 1-1000 (%).</string>
|
||||
@ -1817,7 +1817,7 @@
|
||||
<!-- PreferenceScreen: SponsorBlock -->
|
||||
<string name="revanced_preference_screen_sb_title">SponsorBlock</string>
|
||||
<string name="revanced_sb_enable_sb">تمكين SponsorBlock</string>
|
||||
<string name="revanced_sb_enable_sb_sum">SponsorBlock مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المزعجة من فيديوهات YouTube.</string>
|
||||
<string name="revanced_sb_enable_sb_sum">مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المزعجة من فيديوهات YouTube.</string>
|
||||
<!-- PreferenceScreen: SponsorBlock, PreferenceCategory: Appearance -->
|
||||
<string name="revanced_sb_appearance_category">المظهر</string>
|
||||
<string name="revanced_sb_enable_voting">عرض زر التصويت</string>
|
||||
@ -1905,7 +1905,7 @@
|
||||
<string name="revanced_sb_enable_create_segment_sum_on">يتم عرض زر إنشاء مقطع جديد.</string>
|
||||
<string name="revanced_sb_enable_create_segment_sum_off">لا يتم عرض زر إنشاء مقطع جديد.</string>
|
||||
<string name="revanced_sb_general_adjusting">تعديل تقديم او تأخير المقطع الجديد</string>
|
||||
<string name="revanced_sb_general_adjusting_sum">أجزاء الثانية في الوقت الذي تتحرك فيها أزرار ضبط الوقت عند إنشاء مقاطع جديدة.</string>
|
||||
<string name="revanced_sb_general_adjusting_sum">أجزاء الثانية في الوقت الذي تتحرك فيه أزرار ضبط الوقت عند إنشاء مقاطع جديدة.</string>
|
||||
<string name="revanced_sb_general_adjusting_invalid">يجب أن تكون القيمة رقمًا موجبًا.</string>
|
||||
<string name="revanced_sb_guidelines_preference_title">عرض الإرشادات</string>
|
||||
<string name="revanced_sb_guidelines_preference_sum">الإرشادات تحتوي على نصائح حول تقديم المقاطع.</string>
|
||||
@ -2154,8 +2154,11 @@ AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الص
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">معلومات التعديل</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">معلومات عن التعديلات المطبقة.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">الأداة المستخدمة</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<string name="revanced_preference_category_app_info">معلومات التطبيق</string>
|
||||
<string name="revanced_app_name_title">اسم التطبيق</string>
|
||||
<string name="revanced_app_version_title">إصدار التطبيق</string>
|
||||
<string name="revanced_patched_date_title">تاريخ التعديل</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">أخرى</string>
|
||||
<string name="revanced_icon_custom">مخصص</string>
|
||||
@ -2166,7 +2169,6 @@ AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الص
|
||||
<string name="revanced_icon_revancify_blue">Revancify Blue</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Red</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">الإفتراضي</string>
|
||||
<string name="revanced_patches_excluded">مستبعد</string>
|
||||
<string name="revanced_patches_included">مضمن</string>
|
||||
<string name="revanced_theme_default">الإفتراضي</string>
|
||||
|
@ -1794,8 +1794,7 @@ AVC има максимална разделителна способност о
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">Информация за корекции</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Информация за приложените корекции.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Ползвани инструменти</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Други</string>
|
||||
<string name="revanced_icon_custom">По избор</string>
|
||||
@ -1806,7 +1805,6 @@ AVC има максимална разделителна способност о
|
||||
<string name="revanced_icon_revancify_blue">Revancify синя</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Червена</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">По подразбиране</string>
|
||||
<string name="revanced_patches_excluded">Изключване</string>
|
||||
<string name="revanced_patches_included">Включване</string>
|
||||
<string name="revanced_theme_default">По подразбиране</string>
|
||||
|
@ -22,11 +22,13 @@ Bitte lade %2$s von der Webseite herunter."</string>
|
||||
<string name="revanced_queue_manager_add_to_queue">Zur Warteschlange hinzufügen</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_open_queue">Zur Warteschlange hinzufügen & öffnen</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_play_video">Zur Warteschlange hinzufügen und Video abspielen</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_reload_video">Zur Warteschlange hinzufügen und Video neu laden</string>
|
||||
<string name="revanced_queue_manager_external_downloader">Externer Downloader</string>
|
||||
<string name="revanced_queue_manager_open_queue">Warteschlange öffnen</string>
|
||||
<string name="revanced_queue_manager_queue">Warteschlange</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue">Aus der Warteschlange entfernen</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_open_queue">Aus der Warteschlange entfernen und die Warteschlange öffnen</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_reload_video">Aus der Warteschlange entfernen und Video neu laden</string>
|
||||
<string name="revanced_queue_manager_remove_queue">Warteschlange löschen</string>
|
||||
<string name="revanced_queue_manager_save_queue">Warteschlange speichern</string>
|
||||
<string name="revanced_queue_manager_user_dialog_message">"Öffnen Sie statt eines externen Downloaders den Warteschlangenmanager.
|
||||
@ -113,7 +115,7 @@ Bitte verwenden Sie sie nur zu Debugging-Zwecken."</string>
|
||||
<string name="revanced_language_ZH">""</string>
|
||||
<!-- PreferenceScreen: Ads -->
|
||||
<string name="revanced_preference_screen_ads_title">Werbung</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_title">Endbild-Banner ausblenden</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_title">Abspann Store Banner ausblenden</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_on">Store-Banner ist ausgeblendet.</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_off">Store-Banner wird angezeigt.</string>
|
||||
<string name="revanced_hide_fullscreen_ads_title">Vollbildwerbung verstecken</string>
|
||||
@ -791,7 +793,7 @@ Information:
|
||||
|
||||
Autoplay kann in den YouTube-Einstellungen geändert werden:
|
||||
Einstellungen → Autoplay → Nächstes Video automatisch abspielen"</string>
|
||||
<string name="revanced_hide_suggested_video_end_screen_summary_off">Empfohlene Video-Endbildschirm wird angezeigt.</string>
|
||||
<string name="revanced_hide_suggested_video_end_screen_summary_off">Empfohlene Videos Endbildschirm wird angezeigt.</string>
|
||||
<string name="revanced_skip_autoplay_countdown_title">Autotoplay Countdown überspringen</string>
|
||||
<string name="revanced_skip_autoplay_countdown_summary_on">Ist Autoplay aktiviert, wird das nächste Video sofort abgespielt.</string>
|
||||
<string name="revanced_skip_autoplay_countdown_summary_off">Wenn Autoplay aktiviert ist, wird das nächste Video nach dem Countdown abgespielt.</string>
|
||||
@ -1490,6 +1492,10 @@ Drücken und halten Sie die Schaltfläche Mehr, um den Dialog benutzerdefinierte
|
||||
<string name="revanced_shorts_custom_actions_open_video_title">Zeige geöffnetes Video-Menü</string>
|
||||
<string name="revanced_shorts_custom_actions_open_video_summary_on">Video-Menü wird angezeigt.</string>
|
||||
<string name="revanced_shorts_custom_actions_open_video_summary_off">Video-Menü öffnen ist ausgeblendet.</string>
|
||||
<string name="revanced_shorts_custom_actions_speed_dialog_label">Geschwindigkeitsdialog</string>
|
||||
<string name="revanced_shorts_custom_actions_speed_dialog_title">Menü „Geschwindigkeitsdialog anzeigen“</string>
|
||||
<string name="revanced_shorts_custom_actions_speed_dialog_summary_on">Das Dialogmenü „Geschwindigkeit“ wird angezeigt.</string>
|
||||
<string name="revanced_shorts_custom_actions_speed_dialog_summary_off">Das „Geschwindigkeitsdialogmenü“ ist ausgeblendet.</string>
|
||||
<string name="revanced_shorts_custom_actions_repeat_state_label">Status wiederholen</string>
|
||||
<string name="revanced_shorts_custom_actions_repeat_state_title">Zeige Wiederholungsstatus Menü</string>
|
||||
<string name="revanced_shorts_custom_actions_repeat_state_summary_on">Das Statusmenü wird angezeigt.</string>
|
||||
@ -1594,6 +1600,7 @@ Keine Ränder oben und unten des Spielers."</string>
|
||||
<!-- PreferenceScreen: Video -->
|
||||
<string name="revanced_preference_screen_video_title">Video</string>
|
||||
<!-- PreferenceScreen: Video, PreferenceCategory: Codec -->
|
||||
<string name="revanced_preference_category_codec">Codec</string>
|
||||
<string name="revanced_disable_hdr_video_title">HDR-Video deaktivieren</string>
|
||||
<string name="revanced_disable_hdr_video_summary_on">HDR-Video ist deaktiviert</string>
|
||||
<string name="revanced_disable_hdr_video_summary_off">HDR-Video ist aktiviert</string>
|
||||
@ -1607,6 +1614,7 @@ Keine Ränder oben und unten des Spielers."</string>
|
||||
<string name="revanced_replace_av1_codec_title">Replace software AV1 codec</string>
|
||||
<string name="revanced_replace_av1_codec_summary">Replaces the software AV1 codec with the VP9 codec.</string>
|
||||
<!-- PreferenceScreen: Video, PreferenceCategory: Playback speed -->
|
||||
<string name="revanced_preference_category_playback_speed">Wiedergabegeschwindigkeit</string>
|
||||
<string name="revanced_default_playback_speed_title">Standard Wiedergabegeschwindigkeit</string>
|
||||
<string name="revanced_remember_playback_speed_last_selected_title">Remember playback speed changes</string>
|
||||
<string name="revanced_remember_playback_speed_last_selected_summary_on">Playback speed changes apply to all videos.</string>
|
||||
@ -1614,6 +1622,7 @@ Keine Ränder oben und unten des Spielers."</string>
|
||||
<string name="revanced_remember_playback_speed_last_selected_toast_title">Toast anzeigen</string>
|
||||
<string name="revanced_remember_playback_speed_last_selected_toast_summary_on">Beim Ändern der Standard-Wiedergabegeschwindigkeit wird ein Toast angezeigt.</string>
|
||||
<string name="revanced_remember_playback_speed_last_selected_toast_summary_off">Beim Ändern der Standard-Wiedergabegeschwindigkeit wird kein Toast angezeigt.</string>
|
||||
<string name="revanced_default_playback_speed_shorts_title">Standard Wiedergabegeschwindigkeit bei Shorts</string>
|
||||
<string name="revanced_enable_custom_playback_speed_title">Benutzerdefinierte Wiedergabegeschwindigkeit aktivieren</string>
|
||||
<string name="revanced_enable_custom_playback_speed_summary_on">Benutzerdefinierte Wiedergabegeschwindigkeit ist aktiviert</string>
|
||||
<string name="revanced_enable_custom_playback_speed_summary_off">Benutzerdefinierte Wiedergabegeschwindigkeit ist deaktiviert</string>
|
||||
@ -1625,6 +1634,7 @@ Keine Ränder oben und unten des Spielers."</string>
|
||||
<string name="revanced_custom_playback_speeds_invalid">Ungültige benutzerdefinierte Wiedergabegeschwindigkeiten. Auf Standardwerte zurücksetzen.</string>
|
||||
<string name="revanced_custom_playback_speeds_parse_exception">Ungültige benutzerdefinierte Wiedergabegeschwindigkeiten. Auf Standardwerte zurücksetzen.</string>
|
||||
<!-- PreferenceScreen: Video, PreferenceCategory: Video quality -->
|
||||
<string name="revanced_preference_category_video_quality">Videoqualität</string>
|
||||
<string name="revanced_default_video_quality_mobile_title">Standard Videoqualität im Mobilfunk</string>
|
||||
<string name="revanced_default_video_quality_wifi_title">Standard-Videoqualität im Wlan</string>
|
||||
<string name="revanced_remember_video_quality_last_selected_title">Qualitätseinstellungen merken</string>
|
||||
@ -1897,7 +1907,7 @@ Klicken Sie hier, um zu sehen, wie Sie einen API-Schlüssel ausgeben."</string>
|
||||
<string name="revanced_sb_about_api">sponsor.ajay.app</string>
|
||||
<string name="revanced_sb_about_api_sum">Die Daten werden von der SponsorBlock API bereitgestellt. Tippen Sie hier, um mehr zu erfahren und Downloads für andere Plattformen zu sehen</string>
|
||||
<!-- PreferenceScreen: Miscellaneous -->
|
||||
<string name="revanced_preference_screen_misc_title">PreferenceScreen: Sonstiges</string>
|
||||
<string name="revanced_preference_screen_misc_title">Sonstiges</string>
|
||||
<string name="revanced_bypass_url_redirects_title">URL-Weiterleitungen umgehen</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">URL-Weiterleitungen werden umgangen.</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_off">URL-Umleitungen werden nicht umgangen.</string>
|
||||
@ -1937,7 +1947,7 @@ Drücke Weiter und deaktiviere Akku-Optimierungen."</string>
|
||||
<string name="revanced_enable_opus_codec_summary">Aktiviere den OPUS-Codec, wenn die Antwort des Players den OPUS-Codec enthält.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Import / Export settings -->
|
||||
<string name="revanced_preference_screen_import_export_title">Einstellungen importieren / exportieren</string>
|
||||
<string name="revanced_preference_screen_import_export_summary">Einstellungen importieren / exportieren</string>
|
||||
<string name="revanced_preference_screen_import_export_summary">Importieren / Exportieren der RVX Einstellungen.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Import / Export settings, PreferenceCategory: Import / Export as file -->
|
||||
<string name="revanced_preference_category_import_export_as_file">Als Datei importieren / exportieren</string>
|
||||
<string name="revanced_extended_settings_export_title">Einstellungen exportieren</string>
|
||||
@ -2042,8 +2052,11 @@ Klicken Sie hier, um weitere Informationen zu sehen."</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">Patch-Informationen</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Informationen über angewandte Patches</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Werkzeug verwendet</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<string name="revanced_preference_category_app_info">App Info</string>
|
||||
<string name="revanced_app_name_title">App Name</string>
|
||||
<string name="revanced_app_version_title">App Version</string>
|
||||
<string name="revanced_patched_date_title">Patch Datum</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Andere</string>
|
||||
<string name="revanced_icon_custom">Benutzerdefiniert</string>
|
||||
@ -2054,7 +2067,6 @@ Klicken Sie hier, um weitere Informationen zu sehen."</string>
|
||||
<string name="revanced_icon_revancify_blue">Revancify Blau</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Rot</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">Stock</string>
|
||||
<string name="revanced_patches_excluded">ausgeschlossen</string>
|
||||
<string name="revanced_patches_included">Enthalten</string>
|
||||
<string name="revanced_theme_default">Stock</string>
|
||||
|
@ -22,11 +22,13 @@
|
||||
<string name="revanced_queue_manager_add_to_queue">Προσθήκη στην ουρά</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_open_queue">Προσθήκη στην ουρά και άνοιγμα ουράς</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_play_video">Προσθήκη στην ουρά και αναπαραγωγή βίντεο</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_reload_video">Προσθήκη στην ουρά και επαναφόρτωση του βίντεο</string>
|
||||
<string name="revanced_queue_manager_external_downloader">Εξωτερικό πρόγραμμα λήψης</string>
|
||||
<string name="revanced_queue_manager_open_queue">Άνοιγμα ουράς</string>
|
||||
<string name="revanced_queue_manager_queue">Ουρά</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue">Αφαίρεση από την ουρά</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_open_queue">Αφαίρεση από την ουρά και άνοιγμα ουράς</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_reload_video">Αφαίρεση από την ουρά και επαναφόρτωση του βίντεο</string>
|
||||
<string name="revanced_queue_manager_remove_queue">Κατάργηση ουράς</string>
|
||||
<string name="revanced_queue_manager_save_queue">Αποθήκευση ουράς</string>
|
||||
<string name="revanced_queue_manager_user_dialog_message">"Αντί να ανοίξετε ένα εξωτερικό πρόγραμμα λήψης, ανοίξτε το παράθυρο διαχείρισης ουράς.
|
||||
@ -2162,8 +2164,11 @@ Playlists
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">Πληροφορίες τροποποίησης</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Πληροφορίες σχετικά με τις εφαρμοσμένες τροποποιήσεις.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Χρησιμοποιούμενο εργαλείο</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<string name="revanced_preference_category_app_info">Πληροφορίες εφαρμογής</string>
|
||||
<string name="revanced_app_name_title">Όνομα εφαρμογής</string>
|
||||
<string name="revanced_app_version_title">Έκδοση εφαρμογής</string>
|
||||
<string name="revanced_patched_date_title">Ημερομηνία τροποποίησης</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Άλλα</string>
|
||||
<string name="revanced_icon_custom">Προσαρμοσμένο</string>
|
||||
@ -2174,7 +2179,6 @@ Playlists
|
||||
<string name="revanced_icon_revancify_blue">Revancify Blue</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Red</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">Προεπιλογή</string>
|
||||
<string name="revanced_patches_excluded">Εξαιρέθηκε</string>
|
||||
<string name="revanced_patches_included">Συμπεριλήφθηκε</string>
|
||||
<string name="revanced_theme_default">Προεπιλογή</string>
|
||||
|
@ -22,11 +22,13 @@ Por favor, descarga %2$s desde el sitio web."</string>
|
||||
<string name="revanced_queue_manager_add_to_queue">Añadir a la cola</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_open_queue">Añadir a la cola y abrir la cola</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_play_video">Añadir a la cola y reproducir vídeo</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_reload_video">Añadir a la cola y recargar vídeo</string>
|
||||
<string name="revanced_queue_manager_external_downloader">Descargador externo</string>
|
||||
<string name="revanced_queue_manager_open_queue">Abrir cola</string>
|
||||
<string name="revanced_queue_manager_queue">Cola</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue">Eliminar de la cola</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_open_queue">Eliminar de la cola y abrir la cola</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_reload_video">Eliminar de la cola y recargar vídeo</string>
|
||||
<string name="revanced_queue_manager_remove_queue">Eliminar cola</string>
|
||||
<string name="revanced_queue_manager_save_queue">Guardar cola</string>
|
||||
<string name="revanced_queue_manager_user_dialog_message">"En lugar de abrir un descargador externo, abre el diálogo del gestor de colas.
|
||||
@ -2133,8 +2135,11 @@ Pulsa aquí para ver más información."</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">Información de parches</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Información sobre los parches aplicados.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Herramientas utilizadas</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<string name="revanced_preference_category_app_info">Información de la app</string>
|
||||
<string name="revanced_app_name_title">Nombre de la app</string>
|
||||
<string name="revanced_app_version_title">Versión de la app</string>
|
||||
<string name="revanced_patched_date_title">Fecha de parcheado</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Otros</string>
|
||||
<string name="revanced_icon_custom">Personalizado</string>
|
||||
@ -2145,7 +2150,6 @@ Pulsa aquí para ver más información."</string>
|
||||
<string name="revanced_icon_revancify_blue">Revancify Blue</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Red</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">Predeterminada</string>
|
||||
<string name="revanced_patches_excluded">Excluidos</string>
|
||||
<string name="revanced_patches_included">Incluidos</string>
|
||||
<string name="revanced_theme_default">Predeterminado</string>
|
||||
|
@ -22,11 +22,13 @@ Veuillez télécharger %2$s à partir du site web."</string>
|
||||
<string name="revanced_queue_manager_add_to_queue">Ajouter à la file d\'attente</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_open_queue">Ajouter à la file d\'attente et ouvrir la file d\'attente</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_play_video">Ajouter à la file d\'attente et lire la vidéo</string>
|
||||
<string name="revanced_queue_manager_add_to_queue_and_reload_video">Ajouter à la file d\'attente et recharger la vidéo</string>
|
||||
<string name="revanced_queue_manager_external_downloader">Téléchargeur externe</string>
|
||||
<string name="revanced_queue_manager_open_queue">Ouvrir la file d\'attente</string>
|
||||
<string name="revanced_queue_manager_queue">File d\'attente</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue">Retirer de la file d\'attente</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_open_queue">Retirer de la file d\'attente et ouvrir la file d\'attente</string>
|
||||
<string name="revanced_queue_manager_remove_from_queue_and_reload_video">Supprimer de la file d\'attente et recharger la vidéo</string>
|
||||
<string name="revanced_queue_manager_remove_queue">Retirer de la file d\'attente</string>
|
||||
<string name="revanced_queue_manager_save_queue">Enregistrer la file d’attente</string>
|
||||
<string name="revanced_queue_manager_user_dialog_message">"Au lieu d’ouvrir un téléchargeur externe, ouvrez la boîte de dialogue du gestionnaire de file d’attente.
|
||||
@ -2148,8 +2150,11 @@ Cliquez pour plus d'informations."</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">Informations sur les patchs</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Informations sur les patchs appliqués.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Outil utilisé</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<string name="revanced_preference_category_app_info">Info sur l\'app</string>
|
||||
<string name="revanced_app_name_title">Nom de l\'application</string>
|
||||
<string name="revanced_app_version_title">Version de l\'appli</string>
|
||||
<string name="revanced_patched_date_title">Date du patch</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Autres</string>
|
||||
<string name="revanced_icon_custom">Personnalisé</string>
|
||||
@ -2160,7 +2165,6 @@ Cliquez pour plus d'informations."</string>
|
||||
<string name="revanced_icon_revancify_blue">Revancify Bleu</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Rouge</string>
|
||||
<string name="revanced_icon_youtube">Youtube</string>
|
||||
<string name="revanced_label_default">Officiel</string>
|
||||
<string name="revanced_patches_excluded">Exclus</string>
|
||||
<string name="revanced_patches_included">Appliqué</string>
|
||||
<string name="revanced_theme_default">Officiel</string>
|
||||
|
@ -1914,8 +1914,7 @@ Kattintson a további információkért."</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<string name="revanced_preference_screen_patch_information_title">Patch információ</string>
|
||||
<string name="revanced_preference_screen_patch_information_summary">Információ az alkalmazott patchekről.</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<string name="revanced_preference_category_tool_used">Használt eszköz</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
<string name="revanced_preference_category_others">Egyebek</string>
|
||||
<string name="revanced_icon_custom">Egyéni</string>
|
||||
@ -1926,7 +1925,6 @@ Kattintson a további információkért."</string>
|
||||
<string name="revanced_icon_revancify_blue">Revancify Kék</string>
|
||||
<string name="revanced_icon_revancify_red">Revancify Piros</string>
|
||||
<string name="revanced_icon_youtube">YouTube</string>
|
||||
<string name="revanced_label_default">Alap</string>
|
||||
<string name="revanced_patches_excluded">Kizárva</string>
|
||||
<string name="revanced_patches_included">Beleértve</string>
|
||||
<string name="revanced_theme_default">Alap</string>
|
||||
|
@ -400,6 +400,6 @@ Keterbatasan: Tombol Kembali pada bilah alat mungkin tidak berfungsi."</string>
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Watch history -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceCategory: Patch information -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Tool used -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: App info -->
|
||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Patch information, PreferenceCategory: Others -->
|
||||
</resources>
|
||||
|
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