mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-08 02:13:55 +01:00
Compare commits
102 Commits
v5.2.3-dev
...
v5.6.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c603fac2d | ||
|
|
20a7ad4715 | ||
|
|
25a60e305e | ||
|
|
c7f42d9a3c | ||
|
|
670f100a29 | ||
|
|
19140e5918 | ||
|
|
1dde485013 | ||
|
|
5efcdd31c8 | ||
|
|
e6529837cb | ||
|
|
fe07033444 | ||
|
|
246333f3dc | ||
|
|
d82b02e4f5 | ||
|
|
44995a9f15 | ||
|
|
c87c788a26 | ||
|
|
4ef30618d1 | ||
|
|
b23e6c39fc | ||
|
|
de26766543 | ||
|
|
9168b5eaaf | ||
|
|
c43b9b3b03 | ||
|
|
5e8dfed3e8 | ||
|
|
d67dbba76f | ||
|
|
5dc93156e0 | ||
|
|
5275413ab7 | ||
|
|
248c05b670 | ||
|
|
9e6669d962 | ||
|
|
9c81d01cc8 | ||
|
|
59654788fc | ||
|
|
4c44982cde | ||
|
|
a7aab9aeca | ||
|
|
7a8486f562 | ||
|
|
ccb6a7f161 | ||
|
|
c792edfb77 | ||
|
|
339cd6cc70 | ||
|
|
68304fd96a | ||
|
|
4033048c9b | ||
|
|
9525137800 | ||
|
|
0cf05fa2b0 | ||
|
|
a9bfaf44e2 | ||
|
|
7b08051371 | ||
|
|
b217ca9f9d | ||
|
|
9482092579 | ||
|
|
134c2e52bd | ||
|
|
c348b10a35 | ||
|
|
9a9ec7ef18 | ||
|
|
e746507339 | ||
|
|
862ca077db | ||
|
|
138d43b34b | ||
|
|
8d06a4a8ad | ||
|
|
d7ca7c1733 | ||
|
|
8e0b7db82a | ||
|
|
b9d7867cee | ||
|
|
11216cd942 | ||
|
|
b163e5f64d | ||
|
|
5c2bbd0671 | ||
|
|
2062660d60 | ||
|
|
2d9f08a08e | ||
|
|
78c51182f2 | ||
|
|
feac2ab439 | ||
|
|
32be03c28d | ||
|
|
6a345eee37 | ||
|
|
61be7731e3 | ||
|
|
8295356f88 | ||
|
|
3ec25778eb | ||
|
|
3faf0ac160 | ||
|
|
3ff559878b | ||
|
|
ed9c78da1e | ||
|
|
eefb59020e | ||
|
|
18f18849f3 | ||
|
|
b172c38284 | ||
|
|
5b15602896 | ||
|
|
89c45afcc6 | ||
|
|
3c47bfff1a | ||
|
|
6af8e1b625 | ||
|
|
c44a4af406 | ||
|
|
cb857b0fce | ||
|
|
e0322afbf0 | ||
|
|
5f02f583be | ||
|
|
6462fb8cba | ||
|
|
f9dcce927e | ||
|
|
69f9ab8345 | ||
|
|
dd400ac2a0 | ||
|
|
538ed6d876 | ||
|
|
5ff94dc34a | ||
|
|
b04a11a885 | ||
|
|
4983e021f9 | ||
|
|
bee917f4ed | ||
|
|
c94376bc4c | ||
|
|
87fe83aacf | ||
|
|
92d282e963 | ||
|
|
4a88f650c2 | ||
|
|
8b67716506 | ||
|
|
95d56b1529 | ||
|
|
b1f3b12fa1 | ||
|
|
cf4456c2ba | ||
|
|
d509a3f397 | ||
|
|
d1ae1f1da7 | ||
|
|
9c1c90864c | ||
|
|
5ae76f4df8 | ||
|
|
87eaf61ef1 | ||
|
|
35594d0a20 | ||
|
|
e3c54d8a64 | ||
|
|
06202c8807 |
8
.github/workflows/build_pull_request.yml
vendored
8
.github/workflows/build_pull_request.yml
vendored
@@ -28,4 +28,10 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew build --no-daemon
|
run: ./gradlew :patches:buildAndroid --no-daemon
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: revanced-patches
|
||||||
|
path: patches/build/libs
|
||||||
|
|||||||
20
.github/workflows/pull_strings.yml
vendored
20
.github/workflows/pull_strings.yml
vendored
@@ -1,6 +1,8 @@
|
|||||||
name: Pull strings
|
name: Pull strings
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 */8 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -21,16 +23,20 @@ jobs:
|
|||||||
uses: crowdin/github-action@v2
|
uses: crowdin/github-action@v2
|
||||||
with:
|
with:
|
||||||
config: crowdin.yml
|
config: crowdin.yml
|
||||||
|
upload_sources: false
|
||||||
download_translations: true
|
download_translations: true
|
||||||
localization_branch_name: feat/translations
|
localization_branch_name: feat/translations
|
||||||
create_pull_request: true
|
create_pull_request: false
|
||||||
pull_request_title: "chore: Sync translations"
|
|
||||||
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
|
||||||
pull_request_base_branch_name: "dev"
|
|
||||||
commit_message: "chore: Sync translations"
|
|
||||||
github_user_name: revanced-bot
|
|
||||||
github_user_email: github@revanced.app
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||||
|
|
||||||
|
- name: Open pull request
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
uses: repo-sync/pull-request@v2
|
||||||
|
with:
|
||||||
|
source_branch: feat/translations
|
||||||
|
destination_branch: dev
|
||||||
|
pr_title: "chore: Sync translations"
|
||||||
|
pr_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew build clean
|
run: ./gradlew :patches:buildAndroid clean
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
|
|||||||
317
CHANGELOG.md
317
CHANGELOG.md
@@ -1,3 +1,320 @@
|
|||||||
|
# [5.6.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.2...v5.6.0-dev.3) (2024-12-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitter - Change link sharing domain:** Use correct extension package ([ad7fab6](https://github.com/ReVanced/revanced-patches/commit/ad7fab67319ba23f267d27da9b74266965fc4be3))
|
||||||
|
|
||||||
|
# [5.6.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.1...v5.6.0-dev.2) (2024-12-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([894e366](https://github.com/ReVanced/revanced-patches/commit/894e36665d17d5a3a5728961d424dffc55faa50b))
|
||||||
|
|
||||||
|
# [5.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.2...v5.6.0-dev.1) (2024-12-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([c7c5e5b](https://github.com/ReVanced/revanced-patches/commit/c7c5e5b2b9cf63d8225bb6bd5e735ddf945b6c29))
|
||||||
|
|
||||||
|
## [5.5.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.1...v5.5.2-dev.2) (2024-12-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([08f68cb](https://github.com/ReVanced/revanced-patches/commit/08f68cb5d33f2cfe656d2f93d159c69981f31418))
|
||||||
|
|
||||||
|
## [5.5.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.5.2-dev.1) (2024-12-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([2694158](https://github.com/ReVanced/revanced-patches/commit/2694158c3c9935ede21c96832533222f850068df))
|
||||||
|
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([5870906](https://github.com/ReVanced/revanced-patches/commit/587090636dfff0b358b15026cf7d47c65a4296dc))
|
||||||
|
|
||||||
|
## [5.5.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
|
||||||
|
|
||||||
|
## [5.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1-dev.1) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
|
||||||
|
|
||||||
|
# [5.5.0](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.5.0) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
|
||||||
|
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
|
||||||
|
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
|
||||||
|
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
|
||||||
|
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
|
||||||
|
|
||||||
|
# [5.5.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.4...v5.5.0-dev.5) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
|
||||||
|
|
||||||
|
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
|
||||||
|
|
||||||
|
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
|
||||||
|
|
||||||
|
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
|
||||||
|
|
||||||
|
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
|
||||||
|
|
||||||
|
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
|
||||||
|
|
||||||
|
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
|
||||||
|
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
|
||||||
|
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
|
||||||
|
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
|
||||||
|
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
|
||||||
|
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
|
||||||
|
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
|
||||||
|
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
|
||||||
|
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
|
||||||
|
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
|
||||||
|
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
|
||||||
|
|
||||||
|
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
|
||||||
|
|
||||||
|
# [5.4.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.9...v5.4.0-dev.10) (2024-12-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
|
||||||
|
|
||||||
|
# [5.4.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.8...v5.4.0-dev.9) (2024-12-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
|
||||||
|
|
||||||
|
# [5.4.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.7...v5.4.0-dev.8) (2024-12-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
|
||||||
|
|
||||||
|
# [5.4.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.6...v5.4.0-dev.7) (2024-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
|
||||||
|
|
||||||
|
# [5.4.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.5...v5.4.0-dev.6) (2024-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
|
||||||
|
|
||||||
|
# [5.4.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.4...v5.4.0-dev.5) (2024-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
|
||||||
|
|
||||||
|
# [5.4.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.3...v5.4.0-dev.4) (2024-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
|
||||||
|
|
||||||
|
# [5.4.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.2...v5.4.0-dev.3) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
|
||||||
|
|
||||||
|
# [5.4.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.1...v5.4.0-dev.2) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
|
||||||
|
|
||||||
|
# [5.4.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0-dev.1) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
|
||||||
|
|
||||||
|
# [5.3.0](https://github.com/ReVanced/revanced-patches/compare/v5.2.3...v5.3.0) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([44936e7](https://github.com/ReVanced/revanced-patches/commit/44936e71e846f72f7279950232a5dba37765ceb3))
|
||||||
|
* **Reddit:** Fix patches by using correct extension class ([70bdc68](https://github.com/ReVanced/revanced-patches/commit/70bdc6840d465399625aa1ae0259f49e72711955))
|
||||||
|
* **Sync for Reddit:** Fix patches by using correct extension name ([030093e](https://github.com/ReVanced/revanced-patches/commit/030093e913aab3fab43935eedbaeba0f6c0491bb))
|
||||||
|
* **Twitter:** Merge correct extension by depending on correct extension patch ([8281cf6](https://github.com/ReVanced/revanced-patches/commit/8281cf6a3eead8cc25a277371e0b0ab2be982497))
|
||||||
|
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([630633c](https://github.com/ReVanced/revanced-patches/commit/630633cf57c65c65e5578046413e17670ae336e8))
|
||||||
|
* **YouTube - Spoof video streams:** Enable opus codec by updating iOS client version ([#4063](https://github.com/ReVanced/revanced-patches/issues/4063)) ([0af156f](https://github.com/ReVanced/revanced-patches/commit/0af156f18972c5f089af4bb69824968d2a47d18f))
|
||||||
|
* **YouTube - Spoof video streams:** Update `Force AVC` client data ([#4064](https://github.com/ReVanced/revanced-patches/issues/4064)) ([7d537dd](https://github.com/ReVanced/revanced-patches/commit/7d537ddff4bb5421fa320741275131a66ef5c7bb))
|
||||||
|
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([fbc6ab6](https://github.com/ReVanced/revanced-patches/commit/fbc6ab6a357b351f02d4d486ddc2072cf53199c3))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Nyx:** Remove broken `Unlock pro` patch ([1fe8b16](https://github.com/ReVanced/revanced-patches/commit/1fe8b164eab0c4fa80ab2da2581977f5111a2858))
|
||||||
|
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([ede666b](https://github.com/ReVanced/revanced-patches/commit/ede666b5cb64fcbaa1334ad8bef79e2634ced113))
|
||||||
|
* **YouTube Music:** Add `Spoof video streams` patch to fix playback ([#4065](https://github.com/ReVanced/revanced-patches/issues/4065)) ([cf3116a](https://github.com/ReVanced/revanced-patches/commit/cf3116a7583d09c25c798a85687a056f143656f0))
|
||||||
|
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([296d63b](https://github.com/ReVanced/revanced-patches/commit/296d63bd42c338a01efbcb2df702e5822d05a5f1))
|
||||||
|
|
||||||
|
# [5.3.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.6...v5.3.0-dev.7) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([630633c](https://github.com/ReVanced/revanced-patches/commit/630633cf57c65c65e5578046413e17670ae336e8))
|
||||||
|
|
||||||
|
# [5.3.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.5...v5.3.0-dev.6) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([ede666b](https://github.com/ReVanced/revanced-patches/commit/ede666b5cb64fcbaa1334ad8bef79e2634ced113))
|
||||||
|
|
||||||
|
# [5.3.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.4...v5.3.0-dev.5) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([44936e7](https://github.com/ReVanced/revanced-patches/commit/44936e71e846f72f7279950232a5dba37765ceb3))
|
||||||
|
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([fbc6ab6](https://github.com/ReVanced/revanced-patches/commit/fbc6ab6a357b351f02d4d486ddc2072cf53199c3))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([296d63b](https://github.com/ReVanced/revanced-patches/commit/296d63bd42c338a01efbcb2df702e5822d05a5f1))
|
||||||
|
|
||||||
|
# [5.3.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.3...v5.3.0-dev.4) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Nyx:** Remove broken `Unlock pro` patch ([1fe8b16](https://github.com/ReVanced/revanced-patches/commit/1fe8b164eab0c4fa80ab2da2581977f5111a2858))
|
||||||
|
|
||||||
|
# [5.3.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.2...v5.3.0-dev.3) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Update `Force AVC` client data ([#4064](https://github.com/ReVanced/revanced-patches/issues/4064)) ([7d537dd](https://github.com/ReVanced/revanced-patches/commit/7d537ddff4bb5421fa320741275131a66ef5c7bb))
|
||||||
|
|
||||||
|
# [5.3.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.1...v5.3.0-dev.2) (2024-12-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Reddit:** Fix patches by using correct extension class ([70bdc68](https://github.com/ReVanced/revanced-patches/commit/70bdc6840d465399625aa1ae0259f49e72711955))
|
||||||
|
|
||||||
|
# [5.3.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.4-dev.3...v5.3.0-dev.1) (2024-12-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube Music:** Add `Spoof video streams` patch to fix playback ([#4065](https://github.com/ReVanced/revanced-patches/issues/4065)) ([cf3116a](https://github.com/ReVanced/revanced-patches/commit/cf3116a7583d09c25c798a85687a056f143656f0))
|
||||||
|
|
||||||
|
## [5.2.4-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.2.4-dev.2...v5.2.4-dev.3) (2024-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Enable opus codec by updating iOS client version ([#4063](https://github.com/ReVanced/revanced-patches/issues/4063)) ([0af156f](https://github.com/ReVanced/revanced-patches/commit/0af156f18972c5f089af4bb69824968d2a47d18f))
|
||||||
|
|
||||||
|
## [5.2.4-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.2.4-dev.1...v5.2.4-dev.2) (2024-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Sync for Reddit:** Fix patches by using correct extension name ([030093e](https://github.com/ReVanced/revanced-patches/commit/030093e913aab3fab43935eedbaeba0f6c0491bb))
|
||||||
|
|
||||||
|
## [5.2.4-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.3...v5.2.4-dev.1) (2024-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitter:** Merge correct extension by depending on correct extension patch ([8281cf6](https://github.com/ReVanced/revanced-patches/commit/8281cf6a3eead8cc25a277371e0b0ab2be982497))
|
||||||
|
|
||||||
|
## [5.2.3](https://github.com/ReVanced/revanced-patches/compare/v5.2.2...v5.2.3) (2024-12-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music - GmsCore support:** Resolve patching errors ([#4056](https://github.com/ReVanced/revanced-patches/issues/4056)) ([38a4bad](https://github.com/ReVanced/revanced-patches/commit/38a4bad5b890e3906d77d22efeabd8f38653508b))
|
||||||
|
|
||||||
## [5.2.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.2...v5.2.3-dev.1) (2024-12-06)
|
## [5.2.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.2...v5.2.3-dev.1) (2024-12-06)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
package app.revanced.extension.all.misc.directory.documentsprovider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.ProviderInfo;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.DocumentsProvider;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.system.Os;
|
||||||
|
import android.system.StructStat;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DocumentsProvider that allows access to the app's internal data directory.
|
||||||
|
*/
|
||||||
|
public class InternalDataDocumentsProvider extends DocumentsProvider {
|
||||||
|
private static final String[] rootColumns =
|
||||||
|
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};
|
||||||
|
private static final String[] directoryColumns =
|
||||||
|
{"document_id", "mime_type", "_display_name", "last_modified", "flags",
|
||||||
|
"_size", "full_path", "lstat_info"};
|
||||||
|
private static final int S_IFLNK = 0x8000;
|
||||||
|
|
||||||
|
private String packageName;
|
||||||
|
private File dataDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively delete a file or directory and all its children.
|
||||||
|
*
|
||||||
|
* @param root The file or directory to delete.
|
||||||
|
* @return True if the file or directory and all its children were successfully deleted.
|
||||||
|
*/
|
||||||
|
private static boolean deleteRecursively(File root) {
|
||||||
|
// If root is a directory, delete all children first
|
||||||
|
if (root.isDirectory()) {
|
||||||
|
try {
|
||||||
|
// Only delete recursively if the directory is not a symlink
|
||||||
|
if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) {
|
||||||
|
File[] files = root.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (!deleteRecursively(file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
Log.e("InternalDocumentsProvider", "Failed to lstat " + root.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete file or empty directory
|
||||||
|
return root.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the MIME type of a file based on its extension.
|
||||||
|
*
|
||||||
|
* @param file The file to resolve the MIME type for.
|
||||||
|
* @return The MIME type of the file.
|
||||||
|
*/
|
||||||
|
private static String resolveMimeType(File file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
return DocumentsContract.Document.MIME_TYPE_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = file.getName();
|
||||||
|
int indexOfExtDot = name.lastIndexOf('.');
|
||||||
|
if (indexOfExtDot < 0) {
|
||||||
|
// No extension
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
String extension = name.substring(indexOfExtDot + 1).toLowerCase();
|
||||||
|
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||||
|
return mimeType != null ? mimeType : "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void attachInfo(Context context, ProviderInfo providerInfo) {
|
||||||
|
super.attachInfo(context, providerInfo);
|
||||||
|
|
||||||
|
this.packageName = context.getPackageName();
|
||||||
|
this.dataDirectory = context.getFilesDir().getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException {
|
||||||
|
File directory = resolveDocumentId(parentDocumentId);
|
||||||
|
File file = new File(directory, displayName);
|
||||||
|
|
||||||
|
// If file already exists, append a number to the name
|
||||||
|
int i = 2;
|
||||||
|
while (file.exists()) {
|
||||||
|
file = new File(directory, displayName + " (" + i + ")");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create the file or directory
|
||||||
|
if (mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR) ? file.mkdir() : file.createNewFile()) {
|
||||||
|
// Return the document ID of the new entity
|
||||||
|
if (!parentDocumentId.endsWith("/")) {
|
||||||
|
parentDocumentId = parentDocumentId + "/";
|
||||||
|
}
|
||||||
|
return parentDocumentId + file.getName();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Do nothing. We are throwing a FileNotFoundException later if the file could not be created.
|
||||||
|
}
|
||||||
|
throw new FileNotFoundException("Failed to create document in " + parentDocumentId + " with name " + displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void deleteDocument(String documentId) throws FileNotFoundException {
|
||||||
|
File file = resolveDocumentId(documentId);
|
||||||
|
if (!deleteRecursively(file)) {
|
||||||
|
throw new FileNotFoundException("Failed to delete document " + documentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getDocumentType(String documentId) throws FileNotFoundException {
|
||||||
|
return resolveMimeType(resolveDocumentId(documentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isChildDocument(String parentDocumentId, String documentId) {
|
||||||
|
return documentId.startsWith(parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String moveDocument(String sourceDocumentId, String sourceParentDocumentId, String targetParentDocumentId) throws FileNotFoundException {
|
||||||
|
File source = resolveDocumentId(sourceDocumentId);
|
||||||
|
File dest = resolveDocumentId(targetParentDocumentId);
|
||||||
|
|
||||||
|
File file = new File(dest, source.getName());
|
||||||
|
if (!file.exists() && source.renameTo(file)) {
|
||||||
|
// Return the new document ID
|
||||||
|
if (targetParentDocumentId.endsWith("/")) {
|
||||||
|
return targetParentDocumentId + file.getName();
|
||||||
|
}
|
||||||
|
return targetParentDocumentId + "/" + file.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FileNotFoundException("Failed to move document from " + sourceDocumentId + " to " + targetParentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException {
|
||||||
|
File file = resolveDocumentId(documentId);
|
||||||
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException {
|
||||||
|
if (parentDocumentId.endsWith("/")) {
|
||||||
|
parentDocumentId = parentDocumentId.substring(0, parentDocumentId.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projection == null) {
|
||||||
|
projection = directoryColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
File children = resolveDocumentId(parentDocumentId);
|
||||||
|
|
||||||
|
// Collect all children
|
||||||
|
File[] files = children.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
addRowForDocument(cursor, parentDocumentId + "/" + file.getName(), file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
|
||||||
|
if (projection == null) {
|
||||||
|
projection = directoryColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
addRowForDocument(cursor, documentId, null);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Cursor queryRoots(String[] projection) {
|
||||||
|
ApplicationInfo info = Objects.requireNonNull(getContext()).getApplicationInfo();
|
||||||
|
String appName = info.loadLabel(getContext().getPackageManager()).toString();
|
||||||
|
|
||||||
|
if (projection == null) {
|
||||||
|
projection = rootColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
MatrixCursor.RowBuilder row = cursor.newRow();
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_ROOT_ID, this.packageName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, this.packageName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_SUMMARY, this.packageName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_FLAGS,
|
||||||
|
DocumentsContract.Root.FLAG_LOCAL_ONLY |
|
||||||
|
DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_TITLE, appName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*");
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_ICON, info.icon);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void removeDocument(String documentId, String parentDocumentId) throws FileNotFoundException {
|
||||||
|
deleteDocument(documentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String renameDocument(String documentId, String displayName) throws FileNotFoundException {
|
||||||
|
File file = resolveDocumentId(documentId);
|
||||||
|
if (!file.renameTo(new File(file.getParentFile(), displayName))) {
|
||||||
|
throw new FileNotFoundException("Failed to rename document from " + documentId + " to " + displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the new document ID
|
||||||
|
return documentId.substring(0, documentId.lastIndexOf('/', documentId.length() - 2)) + "/" + displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a file instance for a given document ID.
|
||||||
|
*
|
||||||
|
* @param fullContentPath The document ID to resolve.
|
||||||
|
* @return File object for the given document ID.
|
||||||
|
* @throws FileNotFoundException If the document ID is invalid or the file does not exist.
|
||||||
|
*/
|
||||||
|
private File resolveDocumentId(String fullContentPath) throws FileNotFoundException {
|
||||||
|
if (!fullContentPath.startsWith(this.packageName)) {
|
||||||
|
throw new FileNotFoundException(fullContentPath + " not found");
|
||||||
|
}
|
||||||
|
String path = fullContentPath.substring(this.packageName.length());
|
||||||
|
|
||||||
|
// Resolve the relative path within /data/data/{PKG}
|
||||||
|
File file;
|
||||||
|
if (path.equals("/") || path.isEmpty()) {
|
||||||
|
file = this.dataDirectory;
|
||||||
|
} else {
|
||||||
|
// Remove leading slash
|
||||||
|
String relativePath = path.substring(1);
|
||||||
|
file = new File(this.dataDirectory, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new FileNotFoundException(fullContentPath + " not found");
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a row containing all file properties to a MatrixCursor for a given document ID.
|
||||||
|
*
|
||||||
|
* @param cursor The cursor to add the row to.
|
||||||
|
* @param documentId The document ID to add the row for.
|
||||||
|
* @param file The file to add the row for. If null, the file will be resolved from the document ID.
|
||||||
|
* @throws FileNotFoundException If the file does not exist.
|
||||||
|
*/
|
||||||
|
private void addRowForDocument(MatrixCursor cursor, String documentId, File file) throws FileNotFoundException {
|
||||||
|
if (file == null) {
|
||||||
|
file = resolveDocumentId(documentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int flags = 0;
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
// Prefer list view for directories
|
||||||
|
flags = flags | DocumentsContract.Document.FLAG_DIR_PREFERS_LAST_MODIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.canWrite()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
flags = flags | DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
|
||||||
|
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
|
||||||
|
DocumentsContract.Document.FLAG_SUPPORTS_RENAME |
|
||||||
|
DocumentsContract.Document.FLAG_SUPPORTS_MOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor.RowBuilder row = cursor.newRow();
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, documentId);
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, file.getName());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_SIZE, file.length());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_MIME_TYPE, resolveMimeType(file));
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_FLAGS, flags);
|
||||||
|
|
||||||
|
// Custom columns
|
||||||
|
row.add("full_path", file.getAbsolutePath());
|
||||||
|
|
||||||
|
// Add lstat column
|
||||||
|
String path = file.getPath();
|
||||||
|
try {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
StructStat lstat = Os.lstat(path);
|
||||||
|
sb.append(lstat.st_mode);
|
||||||
|
sb.append(";");
|
||||||
|
sb.append(lstat.st_uid);
|
||||||
|
sb.append(";");
|
||||||
|
sb.append(lstat.st_gid);
|
||||||
|
// Append symlink target if it is a symlink
|
||||||
|
if ((lstat.st_mode & S_IFLNK) == S_IFLNK) {
|
||||||
|
sb.append(";");
|
||||||
|
sb.append(Os.readlink(path));
|
||||||
|
}
|
||||||
|
row.add("lstat_info", sb.toString());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.e("InternalDocumentsProvider", "Failed to get lstat info for " + path, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
extensions/music/build.gradle.kts
Normal file
1
extensions/music/build.gradle.kts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// Do not remove. Necessary for the extension plugin to be applied to the project.
|
||||||
1
extensions/music/src/main/AndroidManifest.xml
Normal file
1
extensions/music/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package app.revanced.extension.music.spoof;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @noinspection unused
|
||||||
|
*/
|
||||||
|
public class SpoofClientPatch {
|
||||||
|
private static final int CLIENT_TYPE_ID = 26;
|
||||||
|
private static final String CLIENT_VERSION = "6.21";
|
||||||
|
private static final String DEVICE_MODEL = "iPhone16,2";
|
||||||
|
private static final String OS_VERSION = "17.7.2.21H221";
|
||||||
|
|
||||||
|
public static int getClientId() {
|
||||||
|
return CLIENT_TYPE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientVersion() {
|
||||||
|
return CLIENT_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientModel() {
|
||||||
|
return DEVICE_MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getOsVersion() {
|
||||||
|
return OS_VERSION;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
package app.revanced.extension.patches;
|
package app.revanced.extension.reddit.patches;
|
||||||
|
|
||||||
import com.reddit.domain.model.ILink;
|
import com.reddit.domain.model.ILink;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public final class FilterPromotedLinksPatch {
|
public final class FilterPromotedLinksPatch {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*
|
||||||
* Filters list from promoted links.
|
* Filters list from promoted links.
|
||||||
**/
|
**/
|
||||||
public static List<?> filterChildren(final Iterable<?> links) {
|
public static List<?> filterChildren(final Iterable<?> links) {
|
||||||
@@ -24,7 +24,9 @@ import java.net.URL;
|
|||||||
* @noinspection unused
|
* @noinspection unused
|
||||||
*/
|
*/
|
||||||
public class GmsCoreSupport {
|
public class GmsCoreSupport {
|
||||||
public static final String ORIGINAL_UNPATCHED_PACKAGE_NAME = "com.google.android.youtube";
|
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
|
||||||
|
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";
|
||||||
|
|
||||||
private static final String GMS_CORE_PACKAGE_NAME
|
private static final String GMS_CORE_PACKAGE_NAME
|
||||||
= getGmsCoreVendorGroupId() + ".android.gms";
|
= getGmsCoreVendorGroupId() + ".android.gms";
|
||||||
private static final Uri GMS_CORE_PROVIDER
|
private static final Uri GMS_CORE_PROVIDER
|
||||||
@@ -52,17 +54,20 @@ public class GmsCoreSupport {
|
|||||||
|
|
||||||
private static void showBatteryOptimizationDialog(Activity context,
|
private static void showBatteryOptimizationDialog(Activity context,
|
||||||
String dialogMessageRef,
|
String dialogMessageRef,
|
||||||
String positiveButtonStringRef,
|
String positiveButtonTextRef,
|
||||||
DialogInterface.OnClickListener onPositiveClickListener) {
|
DialogInterface.OnClickListener onPositiveClickListener) {
|
||||||
// Do not set cancelable to false, to allow using back button to skip the action,
|
// Use a delay to allow the activity to finish initializing.
|
||||||
// just in case the check can never be satisfied.
|
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
|
||||||
var dialog = new AlertDialog.Builder(context)
|
Utils.runOnMainThreadDelayed(() -> {
|
||||||
.setIconAttribute(android.R.attr.alertDialogIcon)
|
// Do not set cancelable to false, to allow using back button to skip the action,
|
||||||
.setTitle(str("gms_core_dialog_title"))
|
// just in case the battery change can never be satisfied.
|
||||||
.setMessage(str(dialogMessageRef))
|
var dialog = new AlertDialog.Builder(context)
|
||||||
.setPositiveButton(str(positiveButtonStringRef), onPositiveClickListener)
|
.setTitle(str("gms_core_dialog_title"))
|
||||||
.create();
|
.setMessage(str(dialogMessageRef))
|
||||||
Utils.showDialog(context, dialog);
|
.setPositiveButton(str(positiveButtonTextRef), onPositiveClickListener)
|
||||||
|
.create();
|
||||||
|
Utils.showDialog(context, dialog);
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,7 +79,8 @@ public class GmsCoreSupport {
|
|||||||
// Verify the user has not included GmsCore for a root installation.
|
// Verify the user has not included GmsCore for a root installation.
|
||||||
// GmsCore Support changes the package name, but with a mounted installation
|
// GmsCore Support changes the package name, but with a mounted installation
|
||||||
// all manifest changes are ignored and the original package name is used.
|
// all manifest changes are ignored and the original package name is used.
|
||||||
if (context.getPackageName().equals(ORIGINAL_UNPATCHED_PACKAGE_NAME)) {
|
String packageName = context.getPackageName();
|
||||||
|
if (packageName.equals(PACKAGE_NAME_YOUTUBE) || packageName.equals(PACKAGE_NAME_YOUTUBE_MUSIC)) {
|
||||||
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
||||||
// Cannot use localize text here, since the app will load
|
// Cannot use localize text here, since the app will load
|
||||||
// resources from the unpatched app and all patch strings are missing.
|
// resources from the unpatched app and all patch strings are missing.
|
||||||
@@ -99,7 +105,18 @@ public class GmsCoreSupport {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if GmsCore is running in the background.
|
// Check if GmsCore is whitelisted from battery optimizations.
|
||||||
|
if (batteryOptimizationsEnabled(context)) {
|
||||||
|
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
|
||||||
|
|
||||||
|
showBatteryOptimizationDialog(context,
|
||||||
|
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
|
||||||
|
"gms_core_dialog_continue_text",
|
||||||
|
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if GmsCore is currently running in the background.
|
||||||
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
|
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
Logger.printInfo(() -> "GmsCore is not running in the background");
|
Logger.printInfo(() -> "GmsCore is not running in the background");
|
||||||
@@ -108,18 +125,8 @@ public class GmsCoreSupport {
|
|||||||
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
||||||
"gms_core_dialog_open_website_text",
|
"gms_core_dialog_open_website_text",
|
||||||
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
|
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if GmsCore is whitelisted from battery optimizations.
|
|
||||||
if (batteryOptimizationsEnabled(context)) {
|
|
||||||
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
|
|
||||||
showBatteryOptimizationDialog(context,
|
|
||||||
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
|
|
||||||
"gms_core_dialog_continue_text",
|
|
||||||
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "checkGmsCore failure", ex);
|
Logger.printException(() -> "checkGmsCore failure", ex);
|
||||||
}
|
}
|
||||||
@@ -143,12 +150,10 @@ public class GmsCoreSupport {
|
|||||||
private static String getGmsCoreDownload() {
|
private static String getGmsCoreDownload() {
|
||||||
final var vendorGroupId = getGmsCoreVendorGroupId();
|
final var vendorGroupId = getGmsCoreVendorGroupId();
|
||||||
//noinspection SwitchStatementWithTooFewBranches
|
//noinspection SwitchStatementWithTooFewBranches
|
||||||
switch (vendorGroupId) {
|
return switch (vendorGroupId) {
|
||||||
case "app.revanced":
|
case "app.revanced" -> "https://github.com/revanced/gmscore/releases/latest";
|
||||||
return "https://github.com/revanced/gmscore/releases/latest";
|
default -> vendorGroupId + ".android.gms";
|
||||||
default:
|
};
|
||||||
return vendorGroupId + ".android.gms";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modified by a patch. Do not touch.
|
// Modified by a patch. Do not touch.
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import android.annotation.SuppressLint;
|
|||||||
import android.app.*;
|
import android.app.*;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -47,6 +49,7 @@ public class Utils {
|
|||||||
private static Context context;
|
private static Context context;
|
||||||
|
|
||||||
private static String versionName;
|
private static String versionName;
|
||||||
|
private static String applicationLabel;
|
||||||
|
|
||||||
private Utils() {
|
private Utils() {
|
||||||
} // utility class
|
} // utility class
|
||||||
@@ -61,28 +64,30 @@ public class Utils {
|
|||||||
return ""; // Value is replaced during patching.
|
return ""; // Value is replaced during patching.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PackageInfo getPackageInfo() throws PackageManager.NameNotFoundException {
|
||||||
|
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
|
||||||
|
|
||||||
|
PackageManager packageManager = context.getPackageManager();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
return packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.PackageInfoFlags.of(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The version name of the app, such as 19.11.43
|
* @return The version name of the app, such as 19.11.43
|
||||||
*/
|
*/
|
||||||
public static String getAppVersionName() {
|
public static String getAppVersionName() {
|
||||||
if (versionName == null) {
|
if (versionName == null) {
|
||||||
try {
|
try {
|
||||||
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
|
versionName = getPackageInfo().versionName;
|
||||||
|
|
||||||
PackageManager packageManager = context.getPackageManager();
|
|
||||||
PackageInfo packageInfo;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
packageInfo = packageManager.getPackageInfo(
|
|
||||||
packageName,
|
|
||||||
PackageManager.PackageInfoFlags.of(0)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
packageInfo = packageManager.getPackageInfo(
|
|
||||||
packageName,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
versionName = packageInfo.versionName;
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "Failed to get package info", ex);
|
Logger.printException(() -> "Failed to get package info", ex);
|
||||||
versionName = "Unknown";
|
versionName = "Unknown";
|
||||||
@@ -92,6 +97,19 @@ public class Utils {
|
|||||||
return versionName;
|
return versionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getApplicationName() {
|
||||||
|
if (applicationLabel == null) {
|
||||||
|
try {
|
||||||
|
ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
|
||||||
|
applicationLabel = (String) applicationInfo.loadLabel(context.getPackageManager());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Failed to get application name", ex);
|
||||||
|
applicationLabel = "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return applicationLabel;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide a view by setting its layout height and width to 1dp.
|
* Hide a view by setting its layout height and width to 1dp.
|
||||||
@@ -325,7 +343,7 @@ public class Utils {
|
|||||||
|
|
||||||
public static void restartApp(@NonNull Context context) {
|
public static void restartApp(@NonNull Context context) {
|
||||||
String packageName = context.getPackageName();
|
String packageName = context.getPackageName();
|
||||||
Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
Intent intent = Objects.requireNonNull(context.getPackageManager().getLaunchIntentForPackage(packageName));
|
||||||
Intent mainIntent = Intent.makeRestartActivityTask(intent.getComponent());
|
Intent mainIntent = Intent.makeRestartActivityTask(intent.getComponent());
|
||||||
// Required for API 34 and later
|
// Required for API 34 and later
|
||||||
// Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
|
// Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
|
||||||
@@ -499,6 +517,12 @@ public class Utils {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isDarkModeEnabled(Context context) {
|
||||||
|
Configuration config = context.getResources().getConfiguration();
|
||||||
|
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically logs any exceptions the runnable throws.
|
* Automatically logs any exceptions the runnable throws.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ package app.revanced.extension.shared.settings;
|
|||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
|
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
|
||||||
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings shared across multiple apps.
|
* Settings shared across multiple apps.
|
||||||
*
|
* <p>
|
||||||
* To ensure this class is loaded when the UI is created, app specific setting bundles should extend
|
* To ensure this class is loaded when the UI is created, app specific setting bundles should extend
|
||||||
* or reference this class.
|
* or reference this class.
|
||||||
*/
|
*/
|
||||||
@@ -16,4 +20,11 @@ public class BaseSettings {
|
|||||||
public static final BooleanSetting DEBUG_TOAST_ON_ERROR = new BooleanSetting("revanced_debug_toast_on_error", TRUE, "revanced_debug_toast_on_error_user_dialog_message");
|
public static final BooleanSetting DEBUG_TOAST_ON_ERROR = new BooleanSetting("revanced_debug_toast_on_error", TRUE, "revanced_debug_toast_on_error_user_dialog_message");
|
||||||
|
|
||||||
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
|
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
|
||||||
|
|
||||||
|
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
|
||||||
|
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new SpoofiOSAvailability());
|
||||||
|
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
|
||||||
|
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
|
||||||
|
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import app.revanced.extension.shared.Logger;
|
|||||||
import app.revanced.extension.shared.StringRef;
|
import app.revanced.extension.shared.StringRef;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@@ -330,7 +329,7 @@ public abstract class Setting<T> {
|
|||||||
return value.equals(defaultValue);
|
return value.equals(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return key + "=" + get();
|
return key + "=" + get();
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ public class ReVancedAboutPreference extends Preference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDarkModeEnabled() {
|
protected boolean isDarkModeEnabled() {
|
||||||
Configuration config = getContext().getResources().getConfiguration();
|
return Utils.isDarkModeEnabled(getContext());
|
||||||
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
|
||||||
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package app.revanced.extension.shared.spoof;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public enum AudioStreamLanguage {
|
||||||
|
/**
|
||||||
|
* YouTube default.
|
||||||
|
* Can be the original language or can be app language,
|
||||||
|
* depending on what YouTube decides to pick as the default.
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
// Language codes found in locale_config.xml
|
||||||
|
// Region specific variants of Chinese/English/Spanish/French have been removed.
|
||||||
|
AF,
|
||||||
|
AM,
|
||||||
|
AR,
|
||||||
|
AS,
|
||||||
|
AZ,
|
||||||
|
BE,
|
||||||
|
BG,
|
||||||
|
BN,
|
||||||
|
BS,
|
||||||
|
CA,
|
||||||
|
CS,
|
||||||
|
DA,
|
||||||
|
DE,
|
||||||
|
EL,
|
||||||
|
EN,
|
||||||
|
ES,
|
||||||
|
ET,
|
||||||
|
EU,
|
||||||
|
FA,
|
||||||
|
FI,
|
||||||
|
FR,
|
||||||
|
GL,
|
||||||
|
GU,
|
||||||
|
HI,
|
||||||
|
HE, // App uses obsolete 'IW' and 'HE' is modern ISO code.
|
||||||
|
HR,
|
||||||
|
HU,
|
||||||
|
HY,
|
||||||
|
ID,
|
||||||
|
IS,
|
||||||
|
IT,
|
||||||
|
JA,
|
||||||
|
KA,
|
||||||
|
KK,
|
||||||
|
KM,
|
||||||
|
KN,
|
||||||
|
KO,
|
||||||
|
KY,
|
||||||
|
LO,
|
||||||
|
LT,
|
||||||
|
LV,
|
||||||
|
MK,
|
||||||
|
ML,
|
||||||
|
MN,
|
||||||
|
MR,
|
||||||
|
MS,
|
||||||
|
MY,
|
||||||
|
NE,
|
||||||
|
NL,
|
||||||
|
NB,
|
||||||
|
OR,
|
||||||
|
PA,
|
||||||
|
PL,
|
||||||
|
PT_BR,
|
||||||
|
PT_PT,
|
||||||
|
RO,
|
||||||
|
RU,
|
||||||
|
SI,
|
||||||
|
SK,
|
||||||
|
SL,
|
||||||
|
SQ,
|
||||||
|
SR,
|
||||||
|
SV,
|
||||||
|
SW,
|
||||||
|
TA,
|
||||||
|
TE,
|
||||||
|
TH,
|
||||||
|
TL,
|
||||||
|
TR,
|
||||||
|
UK,
|
||||||
|
UR,
|
||||||
|
UZ,
|
||||||
|
VI,
|
||||||
|
ZH,
|
||||||
|
ZU;
|
||||||
|
|
||||||
|
private final String iso639_1;
|
||||||
|
|
||||||
|
AudioStreamLanguage() {
|
||||||
|
String name = name();
|
||||||
|
final int regionSeparatorIndex = name.indexOf('_');
|
||||||
|
if (regionSeparatorIndex >= 0) {
|
||||||
|
iso639_1 = name.substring(0, regionSeparatorIndex).toLowerCase(Locale.US)
|
||||||
|
+ name.substring(regionSeparatorIndex);
|
||||||
|
} else {
|
||||||
|
iso639_1 = name().toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIso639_1() {
|
||||||
|
// Changing the app language does not force the app to completely restart,
|
||||||
|
// so the default needs to be the current language and not a static field.
|
||||||
|
if (this == DEFAULT) {
|
||||||
|
return Locale.getDefault().toLanguageTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
return iso639_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +1,60 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof;
|
package app.revanced.extension.shared.spoof;
|
||||||
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.DeviceHardwareSupport.allowAV1;
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.DeviceHardwareSupport.allowVP9;
|
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
|
||||||
public enum ClientType {
|
public enum ClientType {
|
||||||
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
|
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
|
||||||
|
// https://dumps.tadiphone.dev/dumps/oculus/eureka
|
||||||
ANDROID_VR(28,
|
ANDROID_VR(28,
|
||||||
|
"ANDROID_VR",
|
||||||
"Quest 3",
|
"Quest 3",
|
||||||
"12",
|
"12",
|
||||||
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
|
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
|
||||||
"32", // Android 12.1
|
"32", // Android 12.1
|
||||||
"1.56.21",
|
"1.56.21",
|
||||||
"ANDROID_VR",
|
true,
|
||||||
true
|
false),
|
||||||
),
|
|
||||||
// Specific for kids videos.
|
// Specific for kids videos.
|
||||||
// https://dumps.tadiphone.dev/dumps/oculus/eureka
|
|
||||||
IOS(5,
|
IOS(5,
|
||||||
// iPhone 15 supports AV1 hardware decoding.
|
"IOS",
|
||||||
// Only use if this Android device also has hardware decoding.
|
forceAVC()
|
||||||
allowAV1()
|
? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
|
||||||
? "iPhone16,2" // 15 Pro Max
|
: "iPhone16,2", // 15 Pro Max
|
||||||
: "iPhone11,4", // XS Max
|
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
|
||||||
// iOS 14+ forces VP9.
|
forceAVC()
|
||||||
allowVP9()
|
? "13.7.17H35" // Last release of iOS 13.
|
||||||
? "17.5.1.21F90"
|
: "17.5.1.21F90",
|
||||||
: "13.7.17H35",
|
forceAVC()
|
||||||
allowVP9()
|
? "com.google.ios.youtube/17.40.5 (iPhone; U; CPU iOS 13_7 like Mac OS X)"
|
||||||
? "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)"
|
: "com.google.ios.youtube/19.47.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)",
|
||||||
: "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 13_7 like Mac OS X)",
|
|
||||||
null,
|
null,
|
||||||
// Version number should be a valid iOS release.
|
// Version number should be a valid iOS release.
|
||||||
// https://www.ipa4fun.com/history/185230
|
// https://www.ipa4fun.com/history/185230
|
||||||
"19.10.7",
|
forceAVC()
|
||||||
"IOS",
|
// Some newer versions can also force AVC,
|
||||||
false
|
// but 17.40 is the last version that supports iOS 13.
|
||||||
|
? "17.40.5"
|
||||||
|
: "19.47.7",
|
||||||
|
false,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static boolean forceAVC() {
|
||||||
|
return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* YouTube
|
* YouTube
|
||||||
* <a href="https://github.com/zerodytrash/YouTube-Internal-Clients?tab=readme-ov-file#clients">client type</a>
|
* <a href="https://github.com/zerodytrash/YouTube-Internal-Clients?tab=readme-ov-file#clients">client type</a>
|
||||||
*/
|
*/
|
||||||
public final int id;
|
public final int id;
|
||||||
|
|
||||||
|
public final String clientName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
|
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
|
||||||
*/
|
*/
|
||||||
@@ -69,11 +77,6 @@ public enum ClientType {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public final String androidSdkVersion;
|
public final String androidSdkVersion;
|
||||||
|
|
||||||
/**
|
|
||||||
* Client name.
|
|
||||||
*/
|
|
||||||
public final String clientName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App version.
|
* App version.
|
||||||
*/
|
*/
|
||||||
@@ -84,22 +87,28 @@ public enum ClientType {
|
|||||||
*/
|
*/
|
||||||
public final boolean canLogin;
|
public final boolean canLogin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a language code should be used.
|
||||||
|
*/
|
||||||
|
public final boolean useLanguageCode;
|
||||||
|
|
||||||
ClientType(int id,
|
ClientType(int id,
|
||||||
|
String clientName,
|
||||||
String deviceModel,
|
String deviceModel,
|
||||||
String osVersion,
|
String osVersion,
|
||||||
String userAgent,
|
String userAgent,
|
||||||
@Nullable String androidSdkVersion,
|
@Nullable String androidSdkVersion,
|
||||||
String clientVersion,
|
String clientVersion,
|
||||||
String clientName,
|
boolean canLogin,
|
||||||
boolean canLogin
|
boolean useLanguageCode) {
|
||||||
) {
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.clientName = clientName;
|
||||||
this.deviceModel = deviceModel;
|
this.deviceModel = deviceModel;
|
||||||
this.osVersion = osVersion;
|
this.osVersion = osVersion;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.androidSdkVersion = androidSdkVersion;
|
this.androidSdkVersion = androidSdkVersion;
|
||||||
this.clientVersion = clientVersion;
|
this.clientVersion = clientVersion;
|
||||||
this.clientName = clientName;
|
|
||||||
this.canLogin = canLogin;
|
this.canLogin = canLogin;
|
||||||
|
this.useLanguageCode = useLanguageCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof;
|
package app.revanced.extension.shared.spoof;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@@ -6,25 +6,19 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.youtube.patches.spoof.requests.StreamingDataRequest;
|
import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class SpoofVideoStreamsPatch {
|
public class SpoofVideoStreamsPatch {
|
||||||
public static final class ForceiOSAVCAvailability implements Setting.Availability {
|
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return Settings.SPOOF_VIDEO_STREAMS.get() && Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final boolean SPOOF_STREAMING_DATA = Settings.SPOOF_VIDEO_STREAMS.get();
|
private static final boolean FIX_HLS_CURRENT_TIME = SPOOF_STREAMING_DATA
|
||||||
|
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any unreachable ip address. Used to intentionally fail requests.
|
* Any unreachable ip address. Used to intentionally fail requests.
|
||||||
@@ -96,10 +90,19 @@ public class SpoofVideoStreamsPatch {
|
|||||||
try {
|
try {
|
||||||
Uri uri = Uri.parse(url);
|
Uri uri = Uri.parse(url);
|
||||||
String path = uri.getPath();
|
String path = uri.getPath();
|
||||||
|
|
||||||
// 'heartbeat' has no video id and appears to be only after playback has started.
|
// 'heartbeat' has no video id and appears to be only after playback has started.
|
||||||
if (path != null && path.contains("player") && !path.contains("heartbeat")) {
|
// 'refresh' has no video id and appears to happen when waiting for a livestream to start.
|
||||||
String videoId = Objects.requireNonNull(uri.getQueryParameter("id"));
|
if (path != null && path.contains("player") && !path.contains("heartbeat")
|
||||||
StreamingDataRequest.fetchRequest(videoId, requestHeaders);
|
&& !path.contains("refresh")) {
|
||||||
|
String id = uri.getQueryParameter("id");
|
||||||
|
if (id == null) {
|
||||||
|
Logger.printException(() -> "Ignoring request that has no video id." +
|
||||||
|
" Url: " + url + " headers: " + requestHeaders);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamingDataRequest.fetchRequest(id, requestHeaders);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "buildRequest failure", ex);
|
Logger.printException(() -> "buildRequest failure", ex);
|
||||||
@@ -165,4 +168,25 @@ public class SpoofVideoStreamsPatch {
|
|||||||
|
|
||||||
return postData;
|
return postData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*
|
||||||
|
* Fixes iOS livestreams starting from the beginning.
|
||||||
|
*/
|
||||||
|
public static boolean fixHLSCurrentTime(boolean original) {
|
||||||
|
if (FIX_HLS_CURRENT_TIME) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SpoofiOSAvailability implements Setting.Availability {
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
|
||||||
|
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof.requests;
|
package app.revanced.extension.shared.spoof.requests;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -7,10 +7,10 @@ import java.io.IOException;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
|
||||||
import app.revanced.extension.shared.requests.Requester;
|
import app.revanced.extension.shared.requests.Requester;
|
||||||
import app.revanced.extension.shared.requests.Route;
|
import app.revanced.extension.shared.requests.Route;
|
||||||
import app.revanced.extension.youtube.patches.spoof.ClientType;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
final class PlayerRoutes {
|
final class PlayerRoutes {
|
||||||
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
|
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
|
||||||
@@ -19,15 +19,14 @@ final class PlayerRoutes {
|
|||||||
"?fields=streamingData" +
|
"?fields=streamingData" +
|
||||||
"&alt=proto"
|
"&alt=proto"
|
||||||
).compile();
|
).compile();
|
||||||
|
|
||||||
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
|
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TCP connection and HTTP read timeout
|
* TCP connection and HTTP read timeout
|
||||||
*/
|
*/
|
||||||
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 10 * 1000; // 10 Seconds.
|
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 10 * 1000; // 10 Seconds.
|
||||||
|
|
||||||
private static final String LOCALE_LANGUAGE = Utils.getContext().getResources()
|
|
||||||
.getConfiguration().locale.getLanguage();
|
|
||||||
|
|
||||||
private PlayerRoutes() {
|
private PlayerRoutes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,9 +37,10 @@ final class PlayerRoutes {
|
|||||||
JSONObject context = new JSONObject();
|
JSONObject context = new JSONObject();
|
||||||
|
|
||||||
JSONObject client = new JSONObject();
|
JSONObject client = new JSONObject();
|
||||||
// Required to use correct default audio channel with iOS.
|
if (clientType.useLanguageCode) {
|
||||||
client.put("hl", LOCALE_LANGUAGE);
|
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1());
|
||||||
client.put("clientName", clientType.name());
|
}
|
||||||
|
client.put("clientName", clientType.clientName);
|
||||||
client.put("clientVersion", clientType.clientVersion);
|
client.put("clientVersion", clientType.clientVersion);
|
||||||
client.put("deviceModel", clientType.deviceModel);
|
client.put("deviceModel", clientType.deviceModel);
|
||||||
client.put("osVersion", clientType.osVersion);
|
client.put("osVersion", clientType.osVersion);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof.requests;
|
package app.revanced.extension.shared.spoof.requests;
|
||||||
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -22,8 +22,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.patches.spoof.ClientType;
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Video streaming data. Fetching is tied to the behavior YT uses,
|
* Video streaming data. Fetching is tied to the behavior YT uses,
|
||||||
@@ -70,7 +69,7 @@ public class StreamingDataRequest {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
ClientType[] allClientTypes = ClientType.values();
|
ClientType[] allClientTypes = ClientType.values();
|
||||||
ClientType preferredClient = Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
||||||
|
|
||||||
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
||||||
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
||||||
@@ -116,8 +115,7 @@ public class StreamingDataRequest {
|
|||||||
Objects.requireNonNull(playerHeaders);
|
Objects.requireNonNull(playerHeaders);
|
||||||
|
|
||||||
final long startTime = System.currentTimeMillis();
|
final long startTime = System.currentTimeMillis();
|
||||||
String clientTypeName = clientType.name();
|
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType);
|
||||||
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType.name());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
|
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
|
||||||
@@ -125,12 +123,16 @@ public class StreamingDataRequest {
|
|||||||
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
|
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
|
||||||
|
|
||||||
for (String key : REQUEST_HEADER_KEYS) {
|
for (String key : REQUEST_HEADER_KEYS) {
|
||||||
if (!clientType.canLogin && key.equals(AUTHORIZATION_HEADER)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String value = playerHeaders.get(key);
|
String value = playerHeaders.get(key);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
if (key.equals(AUTHORIZATION_HEADER)) {
|
||||||
|
if (!clientType.canLogin) {
|
||||||
|
Logger.printDebug(() -> "Not including request header: " + key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.printDebug(() -> "Including request header: " + key);
|
||||||
connection.setRequestProperty(key, value);
|
connection.setRequestProperty(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,8 +145,10 @@ public class StreamingDataRequest {
|
|||||||
final int responseCode = connection.getResponseCode();
|
final int responseCode = connection.getResponseCode();
|
||||||
if (responseCode == 200) return connection;
|
if (responseCode == 200) return connection;
|
||||||
|
|
||||||
handleConnectionError(clientTypeName + " not available with response code: "
|
// This situation likely means the patches are outdated.
|
||||||
+ responseCode + " message: " + connection.getResponseMessage(),
|
// Use a toast message that suggests updating.
|
||||||
|
handleConnectionError("Playback error (App is outdated?) " + clientType + ": "
|
||||||
|
+ responseCode + " response: " + connection.getResponseMessage(),
|
||||||
null, showErrorToasts);
|
null, showErrorToasts);
|
||||||
} catch (SocketTimeoutException ex) {
|
} catch (SocketTimeoutException ex) {
|
||||||
handleConnectionError("Connection timeout", ex, showErrorToasts);
|
handleConnectionError("Connection timeout", ex, showErrorToasts);
|
||||||
@@ -173,17 +177,19 @@ public class StreamingDataRequest {
|
|||||||
try {
|
try {
|
||||||
// gzip encoding doesn't response with content length (-1),
|
// gzip encoding doesn't response with content length (-1),
|
||||||
// but empty response body does.
|
// but empty response body does.
|
||||||
if (connection.getContentLength() != 0) {
|
if (connection.getContentLength() == 0) {
|
||||||
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream())) {
|
Logger.printDebug(() -> "Received empty response for video: " + videoId);
|
||||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
} else {
|
||||||
byte[] buffer = new byte[2048];
|
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
||||||
int bytesRead;
|
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||||
while ((bytesRead = inputStream.read(buffer)) >= 0) {
|
|
||||||
baos.write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ByteBuffer.wrap(baos.toByteArray());
|
byte[] buffer = new byte[2048];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) >= 0) {
|
||||||
|
baos.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ByteBuffer.wrap(baos.toByteArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
@@ -1,9 +1,20 @@
|
|||||||
package app.revanced.extension.tiktok;
|
package app.revanced.extension.tiktok;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.Utils.isDarkModeEnabled;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
|
private static final long[] DEFAULT_MIN_MAX_VALUES = {0L, Long.MAX_VALUE};
|
||||||
|
|
||||||
// Edit: This could be handled using a custom Setting<Long[]> class
|
// Edit: This could be handled using a custom Setting<Long[]> class
|
||||||
// that saves its value to preferences and JSON using the formatted String created here.
|
// that saves its value to preferences and JSON using the formatted String created here.
|
||||||
public static long[] parseMinMax(StringSetting setting) {
|
public static long[] parseMinMax(StringSetting setting) {
|
||||||
@@ -20,6 +31,29 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setting.save("0-" + Long.MAX_VALUE);
|
setting.save("0-" + Long.MAX_VALUE);
|
||||||
return new long[]{0L, Long.MAX_VALUE};
|
return DEFAULT_MIN_MAX_VALUES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Colors picked by hand. These should be replaced with the styled resources TikTok uses.
|
||||||
|
private static final @ColorInt int TEXT_DARK_MODE_TITLE = Color.WHITE;
|
||||||
|
private static final @ColorInt int TEXT_DARK_MODE_SUMMARY
|
||||||
|
= Color.argb(255, 170, 170, 170);
|
||||||
|
|
||||||
|
private static final @ColorInt int TEXT_LIGHT_MODE_TITLE = Color.BLACK;
|
||||||
|
private static final @ColorInt int TEXT_LIGHT_MODE_SUMMARY
|
||||||
|
= Color.argb(255, 80, 80, 80);
|
||||||
|
|
||||||
|
public static void setTitleAndSummaryColor(Context context, View view) {
|
||||||
|
final boolean darkModeEnabled = isDarkModeEnabled(context);
|
||||||
|
|
||||||
|
TextView title = view.findViewById(android.R.id.title);
|
||||||
|
title.setTextColor(darkModeEnabled
|
||||||
|
? TEXT_DARK_MODE_TITLE
|
||||||
|
: TEXT_LIGHT_MODE_TITLE);
|
||||||
|
|
||||||
|
TextView summary = view.findViewById(android.R.id.summary);
|
||||||
|
summary.setTextColor(darkModeEnabled
|
||||||
|
? TEXT_DARK_MODE_SUMMARY
|
||||||
|
: TEXT_LIGHT_MODE_SUMMARY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package app.revanced.extension.tiktok.feedfilter;
|
package app.revanced.extension.tiktok.feedfilter;
|
||||||
|
|
||||||
import app.revanced.extension.tiktok.settings.Settings;
|
|
||||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||||
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
||||||
|
|
||||||
import static app.revanced.extension.tiktok.Utils.parseMinMax;
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
import app.revanced.extension.tiktok.settings.Settings;
|
||||||
|
|
||||||
public final class LikeCountFilter implements IFilter {
|
public final class LikeCountFilter implements IFilter {
|
||||||
|
|
||||||
final long minLike;
|
final long minLike;
|
||||||
final long maxLike;
|
final long maxLike;
|
||||||
|
|
||||||
LikeCountFilter() {
|
LikeCountFilter() {
|
||||||
long[] minMax = parseMinMax(Settings.MIN_MAX_LIKES);
|
long[] minMax = Utils.parseMinMax(Settings.MIN_MAX_LIKES);
|
||||||
minLike = minMax[0];
|
minLike = minMax[0];
|
||||||
maxLike = minMax[1];
|
maxLike = minMax[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package app.revanced.extension.tiktok.feedfilter;
|
package app.revanced.extension.tiktok.feedfilter;
|
||||||
|
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
import app.revanced.extension.tiktok.settings.Settings;
|
import app.revanced.extension.tiktok.settings.Settings;
|
||||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||||
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
||||||
|
|
||||||
import static app.revanced.extension.tiktok.Utils.parseMinMax;
|
|
||||||
|
|
||||||
public class ViewCountFilter implements IFilter {
|
public class ViewCountFilter implements IFilter {
|
||||||
final long minView;
|
final long minView;
|
||||||
final long maxView;
|
final long maxView;
|
||||||
|
|
||||||
ViewCountFilter() {
|
ViewCountFilter() {
|
||||||
long[] minMax = parseMinMax(Settings.MIN_MAX_VIEWS);
|
long[] minMax = Utils.parseMinMax(Settings.MIN_MAX_VIEWS);
|
||||||
minView = minMax[0];
|
minView = minMax[0];
|
||||||
maxView = minMax[1];
|
maxView = minMax[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting DOWNLOAD_WATERMARK = new BooleanSetting("down_watermark", TRUE);
|
public static final BooleanSetting DOWNLOAD_WATERMARK = new BooleanSetting("down_watermark", TRUE);
|
||||||
public static final BooleanSetting CLEAR_DISPLAY = new BooleanSetting("clear_display", FALSE);
|
public static final BooleanSetting CLEAR_DISPLAY = new BooleanSetting("clear_display", FALSE);
|
||||||
public static final FloatSetting REMEMBERED_SPEED = new FloatSetting("REMEMBERED_SPEED", 1.0f);
|
public static final FloatSetting REMEMBERED_SPEED = new FloatSetting("REMEMBERED_SPEED", 1.0f);
|
||||||
public static final BooleanSetting SIM_SPOOF = new BooleanSetting("simspoof", TRUE, true);
|
public static final BooleanSetting SIM_SPOOF = new BooleanSetting("simspoof", FALSE, true);
|
||||||
public static final StringSetting SIM_SPOOF_ISO = new StringSetting("simspoof_iso", "us");
|
public static final StringSetting SIM_SPOOF_ISO = new StringSetting("simspoof_iso", "us");
|
||||||
public static final StringSetting SIMSPOOF_MCCMNC = new StringSetting("simspoof_mccmnc", "310160");
|
public static final StringSetting SIMSPOOF_MCCMNC = new StringSetting("simspoof_mccmnc", "310160");
|
||||||
public static final StringSetting SIMSPOOF_OP_NAME = new StringSetting("simspoof_op_name", "T-Mobile");
|
public static final StringSetting SIMSPOOF_OP_NAME = new StringSetting("simspoof_op_name", "T-Mobile");
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import android.widget.RadioButton;
|
|||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class DownloadPathPreference extends DialogPreference {
|
public class DownloadPathPreference extends DialogPreference {
|
||||||
private final Context context;
|
|
||||||
private final String[] entryValues = {"DCIM", "Movies", "Pictures"};
|
private final String[] entryValues = {"DCIM", "Movies", "Pictures"};
|
||||||
private String mValue;
|
private String mValue;
|
||||||
|
|
||||||
@@ -29,11 +29,10 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
|
|
||||||
public DownloadPathPreference(Context context, String title, StringSetting setting) {
|
public DownloadPathPreference(Context context, String title, StringSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
setTitle(title);
|
||||||
this.setTitle(title);
|
setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + setting.get());
|
||||||
this.setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + setting.get());
|
setKey(setting.key);
|
||||||
this.setKey(setting.key);
|
setValue(setting.get());
|
||||||
this.setValue(setting.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
@@ -59,6 +58,7 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
childDownloadPath = getValue().substring(getValue().indexOf("/") + 1);
|
childDownloadPath = getValue().substring(getValue().indexOf("/") + 1);
|
||||||
mediaPathIndex = findIndexOf(currentMedia);
|
mediaPathIndex = findIndexOf(currentMedia);
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
LinearLayout dialogView = new LinearLayout(context);
|
LinearLayout dialogView = new LinearLayout(context);
|
||||||
RadioGroup mediaPath = new RadioGroup(context);
|
RadioGroup mediaPath = new RadioGroup(context);
|
||||||
mediaPath.setLayoutParams(new RadioGroup.LayoutParams(-1, -2));
|
mediaPath.setLayoutParams(new RadioGroup.LayoutParams(-1, -2));
|
||||||
@@ -79,12 +79,10 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
downloadPath.addTextChangedListener(new TextWatcher() {
|
downloadPath.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,6 +97,13 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
return dialogView;
|
return dialogView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
builder.setTitle("Download Path");
|
builder.setTitle("Download Path");
|
||||||
|
|||||||
@@ -2,16 +2,26 @@ package app.revanced.extension.tiktok.settings.preference;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class InputTextPreference extends EditTextPreference {
|
public class InputTextPreference extends EditTextPreference {
|
||||||
|
|
||||||
public InputTextPreference(Context context, String title, String summary, StringSetting setting) {
|
public InputTextPreference(Context context, String title, String summary, StringSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.setTitle(title);
|
setTitle(title);
|
||||||
this.setSummary(summary);
|
setSummary(summary);
|
||||||
this.setKey(setting.key);
|
setKey(setting.key);
|
||||||
this.setText(setting.get());
|
setText(setting.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.revanced.extension.tiktok.settings.preference;
|
package app.revanced.extension.tiktok.settings.preference;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@@ -14,11 +15,10 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class RangeValuePreference extends DialogPreference {
|
public class RangeValuePreference extends DialogPreference {
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
private String minValue;
|
private String minValue;
|
||||||
|
|
||||||
private String maxValue;
|
private String maxValue;
|
||||||
@@ -29,7 +29,6 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
|
|
||||||
public RangeValuePreference(Context context, String title, String summary, StringSetting setting) {
|
public RangeValuePreference(Context context, String title, String summary, StringSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
|
||||||
setTitle(title);
|
setTitle(title);
|
||||||
setSummary(summary);
|
setSummary(summary);
|
||||||
setKey(setting.key);
|
setKey(setting.key);
|
||||||
@@ -53,41 +52,52 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
protected View onCreateDialogView() {
|
protected View onCreateDialogView() {
|
||||||
minValue = getValue().split("-")[0];
|
minValue = getValue().split("-")[0];
|
||||||
maxValue = getValue().split("-")[1];
|
maxValue = getValue().split("-")[1];
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
|
||||||
LinearLayout dialogView = new LinearLayout(context);
|
LinearLayout dialogView = new LinearLayout(context);
|
||||||
dialogView.setOrientation(LinearLayout.VERTICAL);
|
dialogView.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
// Min view
|
||||||
LinearLayout minView = new LinearLayout(context);
|
LinearLayout minView = new LinearLayout(context);
|
||||||
minView.setOrientation(LinearLayout.HORIZONTAL);
|
minView.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
dialogView.addView(minView);
|
||||||
|
|
||||||
TextView min = new TextView(context);
|
TextView min = new TextView(context);
|
||||||
min.setText("Min: ");
|
min.setText("Min: ");
|
||||||
minView.addView(min);
|
minView.addView(min);
|
||||||
|
|
||||||
EditText minEditText = new EditText(context);
|
EditText minEditText = new EditText(context);
|
||||||
minEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
minEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
minEditText.setText(minValue);
|
minEditText.setText(minValue);
|
||||||
minView.addView(minEditText);
|
minView.addView(minEditText);
|
||||||
dialogView.addView(minView);
|
|
||||||
|
// Max view
|
||||||
LinearLayout maxView = new LinearLayout(context);
|
LinearLayout maxView = new LinearLayout(context);
|
||||||
maxView.setOrientation(LinearLayout.HORIZONTAL);
|
maxView.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
dialogView.addView(maxView);
|
||||||
|
|
||||||
TextView max = new TextView(context);
|
TextView max = new TextView(context);
|
||||||
max.setText("Max: ");
|
max.setText("Max: ");
|
||||||
maxView.addView(max);
|
maxView.addView(max);
|
||||||
|
|
||||||
EditText maxEditText = new EditText(context);
|
EditText maxEditText = new EditText(context);
|
||||||
maxEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
maxEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
maxEditText.setText(maxValue);
|
maxEditText.setText(maxValue);
|
||||||
maxView.addView(maxEditText);
|
maxView.addView(maxEditText);
|
||||||
dialogView.addView(maxView);
|
|
||||||
minEditText.addTextChangedListener(new TextWatcher() {
|
minEditText.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -98,12 +108,10 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
maxEditText.addTextChangedListener(new TextWatcher() {
|
maxEditText.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -111,12 +119,21 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
maxValue = editable.toString();
|
maxValue = editable.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return dialogView;
|
return dialogView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> this.onClick(dialog, DialogInterface.BUTTON_POSITIVE));
|
builder.setPositiveButton(android.R.string.ok, (dialog, which)
|
||||||
|
-> this.onClick(dialog, DialogInterface.BUTTON_POSITIVE));
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
builder.setNegativeButton(android.R.string.cancel, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,12 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void syncSettingWithPreference(@NonNull @NotNull Preference pref,
|
protected void syncSettingWithPreference(@NonNull Preference pref,
|
||||||
@NonNull @NotNull Setting<?> setting,
|
@NonNull Setting<?> setting,
|
||||||
boolean applySettingToPreference) {
|
boolean applySettingToPreference) {
|
||||||
if (pref instanceof RangeValuePreference) {
|
if (pref instanceof RangeValuePreference rangeValuePref) {
|
||||||
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
|
|
||||||
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
|
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
|
||||||
} else if (pref instanceof DownloadPathPreference) {
|
} else if (pref instanceof DownloadPathPreference downloadPathPref) {
|
||||||
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
|
|
||||||
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
|
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
|
||||||
} else {
|
} else {
|
||||||
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
|
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package app.revanced.extension.tiktok.settings.preference;
|
package app.revanced.extension.tiktok.settings.preference;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.view.View;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
|
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,22 +25,11 @@ public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
|||||||
"revanced_settings_about_links_header", "Official links"
|
"revanced_settings_about_links_header", "Official links"
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
|
||||||
//noinspection deprecation
|
|
||||||
setTitle("About");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
public ReVancedTikTokAboutPreference(Context context) {
|
public ReVancedTikTokAboutPreference(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
|
setTitle("About");
|
||||||
|
setSummary("About ReVanced");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -52,4 +43,11 @@ public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
|||||||
|
|
||||||
return String.format(format, args);
|
return String.format(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,26 @@ package app.revanced.extension.tiktok.settings.preference;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.preference.SwitchPreference;
|
import android.preference.SwitchPreference;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class TogglePreference extends SwitchPreference {
|
public class TogglePreference extends SwitchPreference {
|
||||||
|
|
||||||
public TogglePreference(Context context, String title, String summary, BooleanSetting setting) {
|
public TogglePreference(Context context, String title, String summary, BooleanSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.setTitle(title);
|
setTitle(title);
|
||||||
this.setSummary(summary);
|
setSummary(summary);
|
||||||
this.setKey(setting.key);
|
setKey(setting.key);
|
||||||
this.setChecked(setting.get());
|
setChecked(setting.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public final class ChangeStartPagePatch {
|
|||||||
/**
|
/**
|
||||||
* Unmodified type, and same as un-patched.
|
* Unmodified type, and same as un-patched.
|
||||||
*/
|
*/
|
||||||
ORIGINAL("", null),
|
DEFAULT("", null),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browse id.
|
* Browse id.
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class ForceOriginalAudioPatch {
|
||||||
|
|
||||||
|
private static final String DEFAULT_AUDIO_TRACKS_IDENTIFIER = "original";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean isDefaultAudioStream(boolean isDefault, String audioTrackId, String audioTrackDisplayName) {
|
||||||
|
try {
|
||||||
|
if (!Settings.FORCE_ORIGINAL_AUDIO.get()) {
|
||||||
|
return isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioTrackDisplayName.isEmpty()) {
|
||||||
|
// Older app targets can have empty audio tracks and these might be placeholders.
|
||||||
|
// The real audio tracks are called after these.
|
||||||
|
return isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.printDebug(() -> "default: " + String.format("%-5s", isDefault) + " id: "
|
||||||
|
+ String.format("%-8s", audioTrackId) + " name:" + audioTrackDisplayName);
|
||||||
|
|
||||||
|
final boolean isOriginal = audioTrackDisplayName.contains(DEFAULT_AUDIO_TRACKS_IDENTIFIER);
|
||||||
|
if (isOriginal) {
|
||||||
|
Logger.printDebug(() -> "Using audio: " + audioTrackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOriginal;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "isDefaultAudioStream failure", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDefault;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class FullscreenPanelsRemoverPatch {
|
|
||||||
public static int getFullscreenPanelsVisibility() {
|
|
||||||
return Settings.HIDE_FULLSCREEN_PANELS.get() ? View.GONE : View.VISIBLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,7 +30,7 @@ public final class MiniplayerPatch {
|
|||||||
*/
|
*/
|
||||||
DISABLED(false, null),
|
DISABLED(false, null),
|
||||||
/** Unmodified type, and same as un-patched. */
|
/** Unmodified type, and same as un-patched. */
|
||||||
ORIGINAL(null, null),
|
DEFAULT(null, null),
|
||||||
/**
|
/**
|
||||||
* Exactly the same as MINIMAL and only here for migration of user settings.
|
* Exactly the same as MINIMAL and only here for migration of user settings.
|
||||||
* Eventually this should be deleted.
|
* Eventually this should be deleted.
|
||||||
@@ -82,7 +82,13 @@ public final class MiniplayerPatch {
|
|||||||
final int WIDTH_DIP_MIN = 170; // Seems to be the smallest that works.
|
final int WIDTH_DIP_MIN = 170; // Seems to be the smallest that works.
|
||||||
final int HORIZONTAL_PADDING_DIP = 15; // Estimated padding.
|
final int HORIZONTAL_PADDING_DIP = 15; // Estimated padding.
|
||||||
// Round down to the nearest 5 pixels, to keep any error toasts easier to read.
|
// Round down to the nearest 5 pixels, to keep any error toasts easier to read.
|
||||||
final int WIDTH_DIP_MAX = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
|
final int estimatedWidthDipMax = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
|
||||||
|
// On some ultra low end devices the pixel width and density are the same number,
|
||||||
|
// which causes the estimate to always give a value of 1.
|
||||||
|
// Fix this by using a fixed size of double the min width.
|
||||||
|
final int WIDTH_DIP_MAX = estimatedWidthDipMax <= WIDTH_DIP_MIN
|
||||||
|
? 2 * WIDTH_DIP_MIN
|
||||||
|
: estimatedWidthDipMax;
|
||||||
Logger.printDebug(() -> "Screen dip width: " + deviceDipWidth + " maxWidth: " + WIDTH_DIP_MAX);
|
Logger.printDebug(() -> "Screen dip width: " + deviceDipWidth + " maxWidth: " + WIDTH_DIP_MAX);
|
||||||
|
|
||||||
int dipWidth = Settings.MINIPLAYER_WIDTH_DIP.get();
|
int dipWidth = Settings.MINIPLAYER_WIDTH_DIP.get();
|
||||||
@@ -127,8 +133,10 @@ public final class MiniplayerPatch {
|
|||||||
private static final boolean HIDE_SUBTEXT_ENABLED =
|
private static final boolean HIDE_SUBTEXT_ENABLED =
|
||||||
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
|
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
|
||||||
|
|
||||||
private static final boolean HIDE_REWIND_FORWARD_ENABLED =
|
// 19.25 is last version that has forward/back buttons for phones,
|
||||||
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get();
|
// but buttons still show for tablets/foldable devices and they don't work well so always hide.
|
||||||
|
private static final boolean HIDE_REWIND_FORWARD_ENABLED = CURRENT_TYPE == MODERN_1
|
||||||
|
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
|
||||||
|
|
||||||
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
|
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
|
||||||
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
|
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
|
||||||
@@ -145,6 +153,18 @@ public final class MiniplayerPatch {
|
|||||||
|
|
||||||
private static final int OPACITY_LEVEL;
|
private static final int OPACITY_LEVEL;
|
||||||
|
|
||||||
|
static {
|
||||||
|
int opacity = Settings.MINIPLAYER_OPACITY.get();
|
||||||
|
|
||||||
|
if (opacity < 0 || opacity > 100) {
|
||||||
|
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
||||||
|
Settings.MINIPLAYER_OPACITY.resetToDefault();
|
||||||
|
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OPACITY_LEVEL = (opacity * 255) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
|
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
@@ -163,18 +183,6 @@ public final class MiniplayerPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
|
||||||
int opacity = Settings.MINIPLAYER_OPACITY.get();
|
|
||||||
|
|
||||||
if (opacity < 0 || opacity > 100) {
|
|
||||||
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
|
||||||
Settings.MINIPLAYER_OPACITY.resetToDefault();
|
|
||||||
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
OPACITY_LEVEL = (opacity * 255) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*
|
*
|
||||||
@@ -182,7 +190,7 @@ public final class MiniplayerPatch {
|
|||||||
* effectively disabling the miniplayer.
|
* effectively disabling the miniplayer.
|
||||||
*/
|
*/
|
||||||
public static boolean getMiniplayerOnCloseHandler(boolean original) {
|
public static boolean getMiniplayerOnCloseHandler(boolean original) {
|
||||||
return CURRENT_TYPE == ORIGINAL
|
return CURRENT_TYPE == DEFAULT
|
||||||
? original
|
? original
|
||||||
: CURRENT_TYPE == DISABLED;
|
: CURRENT_TYPE == DISABLED;
|
||||||
}
|
}
|
||||||
@@ -201,7 +209,7 @@ public final class MiniplayerPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean getModernMiniplayerOverride(boolean original) {
|
public static boolean getModernMiniplayerOverride(boolean original) {
|
||||||
return CURRENT_TYPE == ORIGINAL
|
return CURRENT_TYPE == DEFAULT
|
||||||
? original
|
? original
|
||||||
: CURRENT_TYPE.isModern();
|
: CURRENT_TYPE.isModern();
|
||||||
}
|
}
|
||||||
@@ -229,7 +237,7 @@ public final class MiniplayerPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean getModernFeatureFlagsActiveOverride(boolean original) {
|
public static boolean getModernFeatureFlagsActiveOverride(boolean original) {
|
||||||
if (CURRENT_TYPE == ORIGINAL) {
|
if (CURRENT_TYPE == DEFAULT) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +248,7 @@ public final class MiniplayerPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean enableMiniplayerDoubleTapAction(boolean original) {
|
public static boolean enableMiniplayerDoubleTapAction(boolean original) {
|
||||||
if (CURRENT_TYPE == ORIGINAL) {
|
if (CURRENT_TYPE == DEFAULT) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +259,7 @@ public final class MiniplayerPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean enableMiniplayerDragAndDrop(boolean original) {
|
public static boolean enableMiniplayerDragAndDrop(boolean original) {
|
||||||
if (CURRENT_TYPE == ORIGINAL) {
|
if (CURRENT_TYPE == DEFAULT) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ package app.revanced.extension.youtube.patches;
|
|||||||
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
|
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
|
||||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -26,6 +29,15 @@ public final class NavigationButtonsPatch {
|
|||||||
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
|
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
|
||||||
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
|
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
|
||||||
|
|
||||||
|
private static final Boolean DISABLE_TRANSLUCENT_STATUS_BAR
|
||||||
|
= Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get();
|
||||||
|
|
||||||
|
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT
|
||||||
|
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT.get();
|
||||||
|
|
||||||
|
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
|
||||||
|
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
@@ -48,4 +60,42 @@ public final class NavigationButtonsPatch {
|
|||||||
public static void hideNavigationButtonLabels(TextView navigationLabelsView) {
|
public static void hideNavigationButtonLabels(TextView navigationLabelsView) {
|
||||||
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
|
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean useTranslucentNavigationStatusBar(boolean original) {
|
||||||
|
// Must check Android version, as forcing this on Android 11 or lower causes app hang and crash.
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DISABLE_TRANSLUCENT_STATUS_BAR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean useTranslucentNavigationButtons(boolean original) {
|
||||||
|
// Feature requires Android 13+
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils.isDarkModeEnabled(Utils.getContext())
|
||||||
|
? !DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
|
||||||
|
: !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class OpenShortsInRegularPlayerPatch {
|
||||||
|
|
||||||
|
public enum ShortsPlayerType {
|
||||||
|
SHORTS_PLAYER,
|
||||||
|
REGULAR_PLAYER,
|
||||||
|
REGULAR_PLAYER_FULLSCREEN
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (!VersionCheckPatch.IS_19_46_OR_GREATER
|
||||||
|
&& Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) {
|
||||||
|
// User imported newer settings to an older app target.
|
||||||
|
Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE);
|
||||||
|
Settings.SHORTS_PLAYER_TYPE.resetToDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void setMainActivity(Activity activity) {
|
||||||
|
mainActivityRef = new WeakReference<>(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean openShort(String videoID) {
|
||||||
|
try {
|
||||||
|
ShortsPlayerType type = Settings.SHORTS_PLAYER_TYPE.get();
|
||||||
|
if (type == ShortsPlayerType.SHORTS_PLAYER) {
|
||||||
|
return false; // Default unpatched behavior.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoID.isEmpty()) {
|
||||||
|
// Shorts was opened using launcher app shortcut.
|
||||||
|
//
|
||||||
|
// This check will not detect if the Shorts app shortcut is used
|
||||||
|
// while the app is running in the background (instead the regular player is opened).
|
||||||
|
// To detect that the hooked method map parameter can be checked
|
||||||
|
// if integer key 'com.google.android.apps.youtube.app.endpoint.flags'
|
||||||
|
// has bitmask 16 set.
|
||||||
|
//
|
||||||
|
// This use case seems unlikely if the user has the Shorts
|
||||||
|
// set to open in the regular player, so it's ignored as
|
||||||
|
// checking the map makes the patch more complicated.
|
||||||
|
Logger.printDebug(() -> "Ignoring Short with no videoId");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.SHORTS) {
|
||||||
|
return false; // Always use Shorts player for the Shorts nav button.
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean forceFullScreen = (type == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN);
|
||||||
|
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(forceFullScreen);
|
||||||
|
|
||||||
|
// Can use the application context and add intent flags of
|
||||||
|
// FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
// But the activity context seems to fix random app crashes
|
||||||
|
// if Shorts urls are opened outside the app.
|
||||||
|
var context = mainActivityRef.get();
|
||||||
|
|
||||||
|
Intent videoPlayerIntent = new Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse("https://youtube.com/watch?v=" + videoID)
|
||||||
|
);
|
||||||
|
videoPlayerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
videoPlayerIntent.setPackage(context.getPackageName());
|
||||||
|
|
||||||
|
context.startActivity(videoPlayerIntent);
|
||||||
|
return true;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(null);
|
||||||
|
Logger.printException(() -> "openShort failure", ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class OpenVideosFullscreenHookPatch {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static volatile Boolean openNextVideoFullscreen;
|
||||||
|
|
||||||
|
public static void setOpenNextVideoFullscreen(@Nullable Boolean forceFullScreen) {
|
||||||
|
openNextVideoFullscreen = forceFullScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changed during patching since this class is also
|
||||||
|
* used by {@link OpenVideosFullscreenHookPatch}.
|
||||||
|
*/
|
||||||
|
private static boolean isFullScreenPatchIncluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean openVideoFullscreenPortrait(boolean original) {
|
||||||
|
Boolean openFullscreen = openNextVideoFullscreen;
|
||||||
|
if (openFullscreen != null) {
|
||||||
|
openNextVideoFullscreen = null;
|
||||||
|
return openFullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFullScreenPatchIncluded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,4 +9,5 @@ public class VersionCheckPatch {
|
|||||||
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
|
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
|
||||||
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
|
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
|
||||||
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
|
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
|
||||||
|
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ public final class AdsFilter extends Filter {
|
|||||||
"composite_concurrent_carousel_layout",
|
"composite_concurrent_carousel_layout",
|
||||||
"carousel_headered_layout",
|
"carousel_headered_layout",
|
||||||
"full_width_portrait_image_layout",
|
"full_width_portrait_image_layout",
|
||||||
"brand_video_shelf"
|
"brand_video_shelf",
|
||||||
|
"brand_video_singleton"
|
||||||
);
|
);
|
||||||
|
|
||||||
final var movieAds = new StringFilterGroup(
|
final var movieAds = new StringFilterGroup(
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ final class CommentsFilter extends Filter {
|
|||||||
private final ByteArrayFilterGroup emojiPickerBufferGroup;
|
private final ByteArrayFilterGroup emojiPickerBufferGroup;
|
||||||
|
|
||||||
public CommentsFilter() {
|
public CommentsFilter() {
|
||||||
|
var chatSummary = new StringFilterGroup(
|
||||||
|
Settings.HIDE_COMMENTS_CHAT_SUMMARY,
|
||||||
|
"live_chat_summary_banner.eml"
|
||||||
|
);
|
||||||
|
|
||||||
var commentsByMembers = new StringFilterGroup(
|
var commentsByMembers = new StringFilterGroup(
|
||||||
Settings.HIDE_COMMENTS_BY_MEMBERS_HEADER,
|
Settings.HIDE_COMMENTS_BY_MEMBERS_HEADER,
|
||||||
"sponsorships_comments_header.eml",
|
"sponsorships_comments_header.eml",
|
||||||
@@ -54,6 +59,7 @@ final class CommentsFilter extends Filter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
addPathCallbacks(
|
addPathCallbacks(
|
||||||
|
chatSummary,
|
||||||
commentsByMembers,
|
commentsByMembers,
|
||||||
comments,
|
comments,
|
||||||
createAShort,
|
createAShort,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||||
private final StringFilterGroup searchResultShelfHeader;
|
|
||||||
private final StringFilterGroup inFeedSurvey;
|
private final StringFilterGroup inFeedSurvey;
|
||||||
private final StringFilterGroup notifyMe;
|
private final StringFilterGroup notifyMe;
|
||||||
private final StringFilterGroup expandableMetadata;
|
private final StringFilterGroup expandableMetadata;
|
||||||
@@ -74,13 +73,14 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
|
|
||||||
final var communityPosts = new StringFilterGroup(
|
final var communityPosts = new StringFilterGroup(
|
||||||
Settings.HIDE_COMMUNITY_POSTS,
|
Settings.HIDE_COMMUNITY_POSTS,
|
||||||
"post_base_wrapper",
|
"post_base_wrapper", // may be obsolete and no longer needed.
|
||||||
"text_post_root.eml",
|
"text_post_root.eml",
|
||||||
"images_post_root.eml",
|
"images_post_root.eml",
|
||||||
"images_post_slim.eml",
|
"images_post_slim.eml", // may be obsolete and no longer needed.
|
||||||
"images_post_root_slim.eml",
|
"images_post_root_slim.eml",
|
||||||
"text_post_root_slim.eml",
|
"text_post_root_slim.eml",
|
||||||
"post_base_wrapper_slim.eml"
|
"post_base_wrapper_slim.eml",
|
||||||
|
"poll_post_root.eml"
|
||||||
);
|
);
|
||||||
|
|
||||||
final var communityGuidelines = new StringFilterGroup(
|
final var communityGuidelines = new StringFilterGroup(
|
||||||
@@ -194,11 +194,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
"timed_reaction"
|
"timed_reaction"
|
||||||
);
|
);
|
||||||
|
|
||||||
searchResultShelfHeader = new StringFilterGroup(
|
|
||||||
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
|
|
||||||
"shelf_header.eml"
|
|
||||||
);
|
|
||||||
|
|
||||||
notifyMe = new StringFilterGroup(
|
notifyMe = new StringFilterGroup(
|
||||||
Settings.HIDE_NOTIFY_ME_BUTTON,
|
Settings.HIDE_NOTIFY_ME_BUTTON,
|
||||||
"set_reminder_button"
|
"set_reminder_button"
|
||||||
@@ -324,9 +319,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This also hides the feed Shorts shelf header
|
|
||||||
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
|
|
||||||
|
|
||||||
if (matchedGroup == horizontalShelves) {
|
if (matchedGroup == horizontalShelves) {
|
||||||
if (contentIndex == 0 && hideShelves()) {
|
if (contentIndex == 0 && hideShelves()) {
|
||||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof;
|
|
||||||
|
|
||||||
import android.media.MediaCodecInfo;
|
|
||||||
import android.media.MediaCodecList;
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
public class DeviceHardwareSupport {
|
|
||||||
public static final boolean DEVICE_HAS_HARDWARE_DECODING_VP9;
|
|
||||||
public static final boolean DEVICE_HAS_HARDWARE_DECODING_AV1;
|
|
||||||
|
|
||||||
static {
|
|
||||||
boolean vp9found = false;
|
|
||||||
boolean av1found = false;
|
|
||||||
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
|
|
||||||
final boolean deviceIsAndroidTenOrLater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
|
|
||||||
|
|
||||||
for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
|
|
||||||
final boolean isHardwareAccelerated = deviceIsAndroidTenOrLater
|
|
||||||
? codecInfo.isHardwareAccelerated()
|
|
||||||
: !codecInfo.getName().startsWith("OMX.google"); // Software decoder.
|
|
||||||
if (isHardwareAccelerated && !codecInfo.isEncoder()) {
|
|
||||||
for (String type : codecInfo.getSupportedTypes()) {
|
|
||||||
if (type.equalsIgnoreCase("video/x-vnd.on2.vp9")) {
|
|
||||||
vp9found = true;
|
|
||||||
} else if (type.equalsIgnoreCase("video/av01")) {
|
|
||||||
av1found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEVICE_HAS_HARDWARE_DECODING_VP9 = vp9found;
|
|
||||||
DEVICE_HAS_HARDWARE_DECODING_AV1 = av1found;
|
|
||||||
|
|
||||||
Logger.printDebug(() -> DEVICE_HAS_HARDWARE_DECODING_AV1
|
|
||||||
? "Device supports AV1 hardware decoding\n"
|
|
||||||
: "Device does not support AV1 hardware decoding\n"
|
|
||||||
+ (DEVICE_HAS_HARDWARE_DECODING_VP9
|
|
||||||
? "Device supports VP9 hardware decoding"
|
|
||||||
: "Device does not support VP9 hardware decoding"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean allowVP9() {
|
|
||||||
return DEVICE_HAS_HARDWARE_DECODING_VP9 && !Settings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean allowAV1() {
|
|
||||||
return allowVP9() && DEVICE_HAS_HARDWARE_DECODING_AV1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -55,7 +55,7 @@ public class ThemePatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean gradientLoadingScreenEnabled() {
|
public static boolean gradientLoadingScreenEnabled(boolean original) {
|
||||||
return GRADIENT_LOADING_SCREEN_ENABLED;
|
return GRADIENT_LOADING_SCREEN_ENABLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,42 @@ package app.revanced.extension.youtube.settings;
|
|||||||
|
|
||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static app.revanced.extension.shared.settings.Setting.*;
|
import static app.revanced.extension.shared.settings.Setting.Availability;
|
||||||
|
import static app.revanced.extension.shared.settings.Setting.migrateFromOldPreferences;
|
||||||
|
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew;
|
||||||
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
|
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
||||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
|
||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
|
||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.*;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MINIMAL;
|
||||||
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_1;
|
||||||
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_2;
|
||||||
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
|
||||||
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
|
||||||
|
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
||||||
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
||||||
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
|
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.*;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
|
||||||
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
||||||
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
||||||
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.settings.*;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
|
import app.revanced.extension.shared.settings.EnumSetting;
|
||||||
|
import app.revanced.extension.shared.settings.FloatSetting;
|
||||||
|
import app.revanced.extension.shared.settings.IntegerSetting;
|
||||||
|
import app.revanced.extension.shared.settings.LongSetting;
|
||||||
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
|
||||||
import app.revanced.extension.youtube.patches.spoof.ClientType;
|
|
||||||
import app.revanced.extension.youtube.patches.spoof.SpoofVideoStreamsPatch;
|
|
||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||||
|
|
||||||
public class Settings extends BaseSettings {
|
public class Settings extends BaseSettings {
|
||||||
@@ -35,19 +52,21 @@ public class Settings extends BaseSettings {
|
|||||||
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", -2.0f);
|
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", -2.0f);
|
||||||
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
|
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
|
||||||
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
|
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
|
||||||
|
// Audio
|
||||||
|
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE);
|
||||||
|
|
||||||
// Ads
|
// Ads
|
||||||
public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("revanced_hide_fullscreen_ads", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
|
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("revanced_hide_fullscreen_ads", TRUE);
|
||||||
public static final BooleanSetting HIDE_GENERAL_ADS = new BooleanSetting("revanced_hide_general_ads", TRUE);
|
public static final BooleanSetting HIDE_GENERAL_ADS = new BooleanSetting("revanced_hide_general_ads", TRUE);
|
||||||
public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE);
|
public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE);
|
||||||
public static final BooleanSetting HIDE_HIDE_LATEST_POSTS = new BooleanSetting("revanced_hide_latest_posts_ads", TRUE);
|
public static final BooleanSetting HIDE_HIDE_LATEST_POSTS = new BooleanSetting("revanced_hide_latest_posts_ads", TRUE);
|
||||||
public static final BooleanSetting HIDE_MERCHANDISE_BANNERS = new BooleanSetting("revanced_hide_merchandise_banners", TRUE);
|
public static final BooleanSetting HIDE_MERCHANDISE_BANNERS = new BooleanSetting("revanced_hide_merchandise_banners", TRUE);
|
||||||
public static final BooleanSetting HIDE_PAID_PROMOTION_LABEL = new BooleanSetting("revanced_hide_paid_promotion_label", TRUE);
|
public static final BooleanSetting HIDE_PAID_PROMOTION_LABEL = new BooleanSetting("revanced_hide_paid_promotion_label", TRUE);
|
||||||
public static final BooleanSetting HIDE_PRODUCTS_BANNER = new BooleanSetting("revanced_hide_products_banner", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_STORE_SHELF = new BooleanSetting("revanced_hide_player_store_shelf", TRUE);
|
public static final BooleanSetting HIDE_PLAYER_STORE_SHELF = new BooleanSetting("revanced_hide_player_store_shelf", TRUE);
|
||||||
public static final BooleanSetting HIDE_SHOPPING_LINKS = new BooleanSetting("revanced_hide_shopping_links", TRUE);
|
public static final BooleanSetting HIDE_PRODUCTS_BANNER = new BooleanSetting("revanced_hide_products_banner", TRUE);
|
||||||
public static final BooleanSetting HIDE_SELF_SPONSOR = new BooleanSetting("revanced_hide_self_sponsor_ads", TRUE);
|
public static final BooleanSetting HIDE_SELF_SPONSOR = new BooleanSetting("revanced_hide_self_sponsor_ads", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHOPPING_LINKS = new BooleanSetting("revanced_hide_shopping_links", TRUE);
|
||||||
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_hide_video_ads", TRUE, true);
|
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_hide_video_ads", TRUE, true);
|
||||||
public static final BooleanSetting HIDE_VISIT_STORE_BUTTON = new BooleanSetting("revanced_hide_visit_store_button", TRUE);
|
public static final BooleanSetting HIDE_VISIT_STORE_BUTTON = new BooleanSetting("revanced_hide_visit_store_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_WEB_SEARCH_RESULTS = new BooleanSetting("revanced_hide_web_search_results", TRUE);
|
public static final BooleanSetting HIDE_WEB_SEARCH_RESULTS = new BooleanSetting("revanced_hide_web_search_results", TRUE);
|
||||||
@@ -55,9 +74,26 @@ public class Settings extends BaseSettings {
|
|||||||
// Feed
|
// Feed
|
||||||
public static final BooleanSetting HIDE_ALBUM_CARDS = new BooleanSetting("revanced_hide_album_cards", FALSE, true);
|
public static final BooleanSetting HIDE_ALBUM_CARDS = new BooleanSetting("revanced_hide_album_cards", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_ARTIST_CARDS = new BooleanSetting("revanced_hide_artist_cards", FALSE);
|
public static final BooleanSetting HIDE_ARTIST_CARDS = new BooleanSetting("revanced_hide_artist_cards", FALSE);
|
||||||
public static final BooleanSetting HIDE_EXPANDABLE_CHIP = new BooleanSetting("revanced_hide_expandable_chip", TRUE);
|
public static final BooleanSetting HIDE_CHIPS_SHELF = new BooleanSetting("revanced_hide_chips_shelf", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_COMMUNITY_POSTS = new BooleanSetting("revanced_hide_community_posts", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_COMPACT_BANNER = new BooleanSetting("revanced_hide_compact_banner", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_DOODLES = new BooleanSetting("revanced_hide_doodles", FALSE, true, "revanced_hide_doodles_user_dialog_message");
|
public static final BooleanSetting HIDE_DOODLES = new BooleanSetting("revanced_hide_doodles", FALSE, true, "revanced_hide_doodles_user_dialog_message");
|
||||||
|
public static final BooleanSetting HIDE_EXPANDABLE_CHIP = new BooleanSetting("revanced_hide_expandable_chip", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_FEED_SURVEY = new BooleanSetting("revanced_hide_feed_survey", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_FEED = new BooleanSetting("revanced_hide_filter_bar_feed_in_feed", FALSE, true);
|
||||||
|
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS = new BooleanSetting("revanced_hide_filter_bar_feed_in_related_videos", FALSE, true);
|
||||||
|
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_SEARCH = new BooleanSetting("revanced_hide_filter_bar_feed_in_search", FALSE, true);
|
||||||
|
public static final BooleanSetting HIDE_FLOATING_MICROPHONE_BUTTON = new BooleanSetting("revanced_hide_floating_microphone_button", TRUE, true);
|
||||||
|
public static final BooleanSetting HIDE_FOR_YOU_SHELF = new BooleanSetting("revanced_hide_for_you_shelf", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_HORIZONTAL_SHELVES = new BooleanSetting("revanced_hide_horizontal_shelves", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_IMAGE_SHELF = new BooleanSetting("revanced_hide_image_shelf", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_MIX_PLAYLISTS = new BooleanSetting("revanced_hide_mix_playlists", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_MOVIES_SECTION = new BooleanSetting("revanced_hide_movies_section", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
|
||||||
// Alternative thumbnails
|
// Alternative thumbnails
|
||||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
||||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_SUBSCRIPTIONS = new EnumSetting<>("revanced_alt_thumbnail_subscription", ThumbnailOption.ORIGINAL);
|
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_SUBSCRIPTIONS = new EnumSetting<>("revanced_alt_thumbnail_subscription", ThumbnailOption.ORIGINAL);
|
||||||
@@ -69,7 +105,6 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST = new BooleanSetting("revanced_alt_thumbnail_dearrow_connection_toast", TRUE, new DeArrowAvailability());
|
public static final BooleanSetting ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST = new BooleanSetting("revanced_alt_thumbnail_dearrow_connection_toast", TRUE, new DeArrowAvailability());
|
||||||
public static final EnumSetting<ThumbnailStillTime> ALT_THUMBNAIL_STILLS_TIME = new EnumSetting<>("revanced_alt_thumbnail_stills_time", ThumbnailStillTime.MIDDLE, new StillImagesAvailability());
|
public static final EnumSetting<ThumbnailStillTime> ALT_THUMBNAIL_STILLS_TIME = new EnumSetting<>("revanced_alt_thumbnail_stills_time", ThumbnailStillTime.MIDDLE, new StillImagesAvailability());
|
||||||
public static final BooleanSetting ALT_THUMBNAIL_STILLS_FAST = new BooleanSetting("revanced_alt_thumbnail_stills_fast", FALSE, new StillImagesAvailability());
|
public static final BooleanSetting ALT_THUMBNAIL_STILLS_FAST = new BooleanSetting("revanced_alt_thumbnail_stills_fast", FALSE, new StillImagesAvailability());
|
||||||
|
|
||||||
// Hide keyword content
|
// Hide keyword content
|
||||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_HOME = new BooleanSetting("revanced_hide_keyword_content_home", FALSE);
|
public static final BooleanSetting HIDE_KEYWORD_CONTENT_HOME = new BooleanSetting("revanced_hide_keyword_content_home", FALSE);
|
||||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_keyword_content_subscriptions", FALSE);
|
public static final BooleanSetting HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_keyword_content_subscriptions", FALSE);
|
||||||
@@ -77,88 +112,61 @@ public class Settings extends BaseSettings {
|
|||||||
public static final StringSetting HIDE_KEYWORD_CONTENT_PHRASES = new StringSetting("revanced_hide_keyword_content_phrases", "",
|
public static final StringSetting HIDE_KEYWORD_CONTENT_PHRASES = new StringSetting("revanced_hide_keyword_content_phrases", "",
|
||||||
parentsAny(HIDE_KEYWORD_CONTENT_HOME, HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS, HIDE_KEYWORD_CONTENT_SEARCH));
|
parentsAny(HIDE_KEYWORD_CONTENT_HOME, HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS, HIDE_KEYWORD_CONTENT_SEARCH));
|
||||||
|
|
||||||
// Uncategorized layout related settings. Do not add to this section, and instead move these out and categorize them.
|
|
||||||
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
|
|
||||||
public static final BooleanSetting GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_gradient_loading_screen", FALSE, true);
|
|
||||||
public static final BooleanSetting HIDE_HORIZONTAL_SHELVES = new BooleanSetting("revanced_hide_horizontal_shelves", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_CHANNEL_BAR = new BooleanSetting("revanced_hide_channel_bar", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_CHANNEL_MEMBER_SHELF = new BooleanSetting("revanced_hide_channel_member_shelf", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_CHIPS_SHELF = new BooleanSetting("revanced_hide_chips_shelf", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_COMMUNITY_POSTS = new BooleanSetting("revanced_hide_community_posts", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_COMPACT_BANNER = new BooleanSetting("revanced_hide_compact_banner", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, true);
|
|
||||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_FEED_SURVEY = new BooleanSetting("revanced_hide_feed_survey", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_FEED = new BooleanSetting("revanced_hide_filter_bar_feed_in_feed", FALSE, true);
|
|
||||||
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS = new BooleanSetting("revanced_hide_filter_bar_feed_in_related_videos", FALSE, true);
|
|
||||||
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_SEARCH = new BooleanSetting("revanced_hide_filter_bar_feed_in_search", FALSE, true);
|
|
||||||
public static final BooleanSetting HIDE_FLOATING_MICROPHONE_BUTTON = new BooleanSetting("revanced_hide_floating_microphone_button", TRUE, true);
|
|
||||||
public static final BooleanSetting HIDE_FULLSCREEN_PANELS = new BooleanSetting("revanced_hide_fullscreen_panels", TRUE, true);
|
|
||||||
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_IMAGE_SHELF = new BooleanSetting("revanced_hide_image_shelf", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
|
|
||||||
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_MIX_PLAYLISTS = new BooleanSetting("revanced_hide_mix_playlists", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_MOVIES_SECTION = new BooleanSetting("revanced_hide_movies_section", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_QUICK_ACTIONS = new BooleanSetting("revanced_hide_quick_actions", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_RELATED_VIDEOS = new BooleanSetting("revanced_hide_related_videos", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SEARCH_RESULT_SHELF_HEADER = new BooleanSetting("revanced_hide_search_result_shelf_header", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_FOR_YOU_SHELF = new BooleanSetting("revanced_hide_for_you_shelf", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
|
|
||||||
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity",100, true);
|
|
||||||
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
|
|
||||||
|
|
||||||
// Player
|
// Player
|
||||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
|
||||||
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
|
||||||
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
|
||||||
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS = new BooleanSetting("revanced_hide_player_previous_next_buttons", FALSE, true);
|
|
||||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
|
|
||||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||||
|
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
||||||
|
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
|
||||||
|
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||||
|
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
|
||||||
|
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
||||||
|
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
|
||||||
|
public static final BooleanSetting HIDE_CHANNEL_BAR = new BooleanSetting("revanced_hide_channel_bar", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_CHANNEL_MEMBER_SHELF = new BooleanSetting("revanced_hide_channel_member_shelf", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS = new BooleanSetting("revanced_hide_player_previous_next_buttons", FALSE, true);
|
||||||
|
public static final BooleanSetting HIDE_QUICK_ACTIONS = new BooleanSetting("revanced_hide_quick_actions", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_RELATED_VIDEOS = new BooleanSetting("revanced_hide_related_videos", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
|
||||||
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
|
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
|
||||||
|
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
|
||||||
|
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
|
||||||
|
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
|
||||||
// Miniplayer
|
// Miniplayer
|
||||||
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true);
|
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true);
|
||||||
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
|
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
|
||||||
public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, MINIPLAYER_ANY_MODERN);
|
public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||||
public static final BooleanSetting MINIPLAYER_DRAG_AND_DROP = new BooleanSetting("revanced_miniplayer_drag_and_drop", TRUE, true, MINIPLAYER_ANY_MODERN);
|
public static final BooleanSetting MINIPLAYER_DRAG_AND_DROP = new BooleanSetting("revanced_miniplayer_drag_and_drop", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||||
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerHorizontalDragAvailability());
|
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerHorizontalDragAvailability());
|
||||||
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerHideExpandCloseAvailability());
|
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerHideExpandCloseAvailability());
|
||||||
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
|
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
|
||||||
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
||||||
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
|
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||||
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, MINIPLAYER_ANY_MODERN);
|
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, MINIPLAYER_ANY_MODERN);
|
||||||
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
||||||
|
|
||||||
// External downloader
|
// External downloader
|
||||||
public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE);
|
public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE);
|
||||||
public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE);
|
public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE);
|
||||||
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
|
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
|
||||||
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
|
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
|
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
|
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
|
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
|
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
|
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
|
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
|
||||||
|
|
||||||
// Description
|
// Description
|
||||||
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
|
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
|
||||||
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
|
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
|
||||||
@@ -166,47 +174,43 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_KEY_CONCEPTS_SECTION = new BooleanSetting("revanced_hide_key_concepts_section", FALSE);
|
public static final BooleanSetting HIDE_KEY_CONCEPTS_SECTION = new BooleanSetting("revanced_hide_key_concepts_section", FALSE);
|
||||||
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
|
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
|
||||||
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons
|
||||||
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYLIST_BUTTON = new BooleanSetting("revanced_hide_playlist_button", FALSE);
|
public static final BooleanSetting HIDE_PLAYLIST_BUTTON = new BooleanSetting("revanced_hide_playlist_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
||||||
// Player flyout menu items
|
// Player flyout menu items
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOOP_VIDEO = new BooleanSetting("revanced_hide_player_flyout_loop_video", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_SPEED = new BooleanSetting("revanced_hide_player_flyout_speed", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_MORE_INFO = new BooleanSetting("revanced_hide_player_flyout_more_info", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOOP_VIDEO = new BooleanSetting("revanced_hide_player_flyout_loop_video", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_MORE_INFO = new BooleanSetting("revanced_hide_player_flyout_more_info", TRUE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_SLEEP_TIMER = new BooleanSetting("revanced_hide_player_flyout_sleep_timer", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_SLEEP_TIMER = new BooleanSetting("revanced_hide_player_flyout_sleep_timer", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_SPEED = new BooleanSetting("revanced_hide_player_flyout_speed", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_STABLE_VOLUME = new BooleanSetting("revanced_hide_player_flyout_stable_volume", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_STABLE_VOLUME = new BooleanSetting("revanced_hide_player_flyout_stable_volume", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_WATCH_IN_VR = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE);
|
|
||||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_player_flyout_video_quality_footer", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_player_flyout_video_quality_footer", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_WATCH_IN_VR = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE);
|
||||||
|
|
||||||
// General layout
|
// General layout
|
||||||
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.ORIGINAL, true);
|
|
||||||
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
|
||||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.35.36" : "17.33.42", true, parent(SPOOF_APP_VERSION));
|
|
||||||
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
|
|
||||||
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
|
||||||
public static final BooleanSetting BYPASS_IMAGE_REGION_RESTRICTIONS = new BooleanSetting("revanced_bypass_image_region_restrictions", FALSE, true);
|
public static final BooleanSetting BYPASS_IMAGE_REGION_RESTRICTIONS = new BooleanSetting("revanced_bypass_image_region_restrictions", FALSE, true);
|
||||||
|
public static final BooleanSetting GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_gradient_loading_screen", FALSE, true);
|
||||||
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
|
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
|
||||||
"revanced_remove_viewer_discretion_dialog_user_dialog_message");
|
"revanced_remove_viewer_discretion_dialog_user_dialog_message");
|
||||||
|
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
||||||
|
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
|
||||||
|
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
||||||
|
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
||||||
|
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.35.36" : "17.33.42", true, parent(SPOOF_APP_VERSION));
|
||||||
// Custom filter
|
// Custom filter
|
||||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||||
|
|
||||||
// Navigation buttons
|
// Navigation buttons
|
||||||
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
||||||
@@ -214,73 +218,73 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
|
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
|
||||||
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
|
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
|
||||||
|
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
|
||||||
|
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
|
||||||
|
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
|
||||||
|
|
||||||
// Shorts
|
// Shorts
|
||||||
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
|
|
||||||
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
|
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_HOME = new BooleanSetting("revanced_hide_shorts_home", FALSE);
|
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_shorts_subscriptions", FALSE);
|
public static final EnumSetting<ShortsPlayerType> SHORTS_PLAYER_TYPE = new EnumSetting<>("revanced_shorts_player_type", ShortsPlayerType.SHORTS_PLAYER);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SEARCH = new BooleanSetting("revanced_hide_shorts_search", FALSE);
|
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_JOIN_BUTTON = new BooleanSetting("revanced_hide_shorts_join_button", TRUE);
|
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SUBSCRIBE_BUTTON = new BooleanSetting("revanced_hide_shorts_subscribe_button", TRUE);
|
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_PAUSED_OVERLAY_BUTTONS = new BooleanSetting("revanced_hide_shorts_paused_overlay_buttons", FALSE);
|
public static final BooleanSetting HIDE_SHORTS_FULL_VIDEO_LINK_LABEL = new BooleanSetting("revanced_hide_shorts_full_video_link_label", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SHOP_BUTTON = new BooleanSetting("revanced_hide_shorts_shop_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_TAGGED_PRODUCTS = new BooleanSetting("revanced_hide_shorts_tagged_products", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_LOCATION_LABEL = new BooleanSetting("revanced_hide_shorts_location_label", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_SAVE_SOUND_BUTTON = new BooleanSetting("revanced_hide_shorts_save_sound_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_USE_TEMPLATE_BUTTON = new BooleanSetting("revanced_hide_shorts_use_template_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_UPCOMING_BUTTON = new BooleanSetting("revanced_hide_shorts_upcoming_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_GREEN_SCREEN_BUTTON = new BooleanSetting("revanced_hide_shorts_green_screen_button", TRUE);
|
public static final BooleanSetting HIDE_SHORTS_GREEN_SCREEN_BUTTON = new BooleanSetting("revanced_hide_shorts_green_screen_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_HASHTAG_BUTTON = new BooleanSetting("revanced_hide_shorts_hashtag_button", TRUE);
|
public static final BooleanSetting HIDE_SHORTS_HASHTAG_BUTTON = new BooleanSetting("revanced_hide_shorts_hashtag_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SEARCH_SUGGESTIONS = new BooleanSetting("revanced_hide_shorts_search_suggestions", TRUE);
|
public static final BooleanSetting HIDE_SHORTS_HOME = new BooleanSetting("revanced_hide_shorts_home", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_STICKERS = new BooleanSetting("revanced_hide_shorts_stickers", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_SUPER_THANKS_BUTTON = new BooleanSetting("revanced_hide_shorts_super_thanks_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_LIKE_FOUNTAIN = new BooleanSetting("revanced_hide_shorts_like_fountain", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_LIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_like_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_REMIX_BUTTON = new BooleanSetting("revanced_hide_shorts_remix_button", TRUE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_SHARE_BUTTON = new BooleanSetting("revanced_hide_shorts_share_button", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_INFO_PANEL = new BooleanSetting("revanced_hide_shorts_info_panel", TRUE);
|
public static final BooleanSetting HIDE_SHORTS_INFO_PANEL = new BooleanSetting("revanced_hide_shorts_info_panel", TRUE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SOUND_BUTTON = new BooleanSetting("revanced_hide_shorts_sound_button", FALSE);
|
public static final BooleanSetting HIDE_SHORTS_JOIN_BUTTON = new BooleanSetting("revanced_hide_shorts_join_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
|
public static final BooleanSetting HIDE_SHORTS_LIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_like_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_VIDEO_TITLE = new BooleanSetting("revanced_hide_shorts_video_title", FALSE);
|
public static final BooleanSetting HIDE_SHORTS_LIKE_FOUNTAIN = new BooleanSetting("revanced_hide_shorts_like_fountain", TRUE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_SOUND_METADATA_LABEL = new BooleanSetting("revanced_hide_shorts_sound_metadata_label", FALSE);
|
public static final BooleanSetting HIDE_SHORTS_LOCATION_LABEL = new BooleanSetting("revanced_hide_shorts_location_label", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHORTS_FULL_VIDEO_LINK_LABEL = new BooleanSetting("revanced_hide_shorts_full_video_link_label", FALSE);
|
|
||||||
public static final BooleanSetting HIDE_SHORTS_NAVIGATION_BAR = new BooleanSetting("revanced_hide_shorts_navigation_bar", FALSE, true);
|
public static final BooleanSetting HIDE_SHORTS_NAVIGATION_BAR = new BooleanSetting("revanced_hide_shorts_navigation_bar", FALSE, true);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_PAUSED_OVERLAY_BUTTONS = new BooleanSetting("revanced_hide_shorts_paused_overlay_buttons", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_REMIX_BUTTON = new BooleanSetting("revanced_hide_shorts_remix_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SAVE_SOUND_BUTTON = new BooleanSetting("revanced_hide_shorts_save_sound_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SEARCH = new BooleanSetting("revanced_hide_shorts_search", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SEARCH_SUGGESTIONS = new BooleanSetting("revanced_hide_shorts_search_suggestions", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SHARE_BUTTON = new BooleanSetting("revanced_hide_shorts_share_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SHOP_BUTTON = new BooleanSetting("revanced_hide_shorts_shop_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SOUND_BUTTON = new BooleanSetting("revanced_hide_shorts_sound_button", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SOUND_METADATA_LABEL = new BooleanSetting("revanced_hide_shorts_sound_metadata_label", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_STICKERS = new BooleanSetting("revanced_hide_shorts_stickers", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SUBSCRIBE_BUTTON = new BooleanSetting("revanced_hide_shorts_subscribe_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_shorts_subscriptions", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_SUPER_THANKS_BUTTON = new BooleanSetting("revanced_hide_shorts_super_thanks_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_TAGGED_PRODUCTS = new BooleanSetting("revanced_hide_shorts_tagged_products", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_UPCOMING_BUTTON = new BooleanSetting("revanced_hide_shorts_upcoming_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_USE_TEMPLATE_BUTTON = new BooleanSetting("revanced_hide_shorts_use_template_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_SHORTS_VIDEO_TITLE = new BooleanSetting("revanced_hide_shorts_video_title", FALSE);
|
||||||
public static final BooleanSetting SHORTS_AUTOPLAY = new BooleanSetting("revanced_shorts_autoplay", FALSE);
|
public static final BooleanSetting SHORTS_AUTOPLAY = new BooleanSetting("revanced_shorts_autoplay", FALSE);
|
||||||
public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE);
|
public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE);
|
||||||
|
|
||||||
// Seekbar
|
// Seekbar
|
||||||
|
|
||||||
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
|
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
|
||||||
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
|
|
||||||
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
|
|
||||||
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
|
|
||||||
public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true,
|
|
||||||
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
|
|
||||||
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
|
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
|
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
|
||||||
|
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
|
||||||
|
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
|
||||||
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
|
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
|
||||||
|
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
|
||||||
|
public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true,
|
||||||
|
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
|
||||||
|
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
|
||||||
public static final StringSetting SEEKBAR_CUSTOM_COLOR_VALUE = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
|
public static final StringSetting SEEKBAR_CUSTOM_COLOR_VALUE = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
|
||||||
|
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
||||||
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
||||||
|
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
||||||
|
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
||||||
|
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
|
||||||
public static final BooleanSetting DISABLE_ZOOM_HAPTICS = new BooleanSetting("revanced_disable_zoom_haptics", TRUE);
|
public static final BooleanSetting DISABLE_ZOOM_HAPTICS = new BooleanSetting("revanced_disable_zoom_haptics", TRUE);
|
||||||
public static final BooleanSetting EXTERNAL_BROWSER = new BooleanSetting("revanced_external_browser", TRUE, true);
|
public static final BooleanSetting EXTERNAL_BROWSER = new BooleanSetting("revanced_external_browser", TRUE, true);
|
||||||
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
public static final BooleanSetting REMOVE_TRACKING_QUERY_PARAMETER = new BooleanSetting("revanced_remove_tracking_query_parameter", TRUE);
|
||||||
public static final BooleanSetting SPOOF_DEVICE_DIMENSIONS = new BooleanSetting("revanced_spoof_device_dimensions", FALSE, true,
|
public static final BooleanSetting SPOOF_DEVICE_DIMENSIONS = new BooleanSetting("revanced_spoof_device_dimensions", FALSE, true,
|
||||||
"revanced_spoof_device_dimensions_user_dialog_message");
|
"revanced_spoof_device_dimensions_user_dialog_message");
|
||||||
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
|
||||||
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
|
|
||||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true,"revanced_spoof_video_streams_user_dialog_message");
|
|
||||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
|
|
||||||
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofVideoStreamsPatch.ForceiOSAVCAvailability());
|
|
||||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
|
|
||||||
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
|
||||||
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
|
|
||||||
public static final BooleanSetting REMOVE_TRACKING_QUERY_PARAMETER = new BooleanSetting("revanced_remove_tracking_query_parameter", TRUE);
|
|
||||||
|
|
||||||
// Debugging
|
|
||||||
/**
|
/**
|
||||||
* When enabled, share the debug logs with care.
|
* When enabled, share the debug logs with care.
|
||||||
* The buffer contains select user data, including the client ip address and information that could identify the end user.
|
* The buffer contains select user data, including the client ip address and information that could identify the end user.
|
||||||
@@ -298,6 +302,8 @@ public class Settings extends BaseSettings {
|
|||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127, true,
|
public static final IntegerSetting SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
|
|
||||||
|
// Debugging
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
|
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||||
@@ -320,7 +326,6 @@ public class Settings extends BaseSettings {
|
|||||||
* Do not use directly, instead use {@link SponsorBlockSettings}
|
* Do not use directly, instead use {@link SponsorBlockSettings}
|
||||||
*/
|
*/
|
||||||
public static final StringSetting SB_PRIVATE_USER_ID = new StringSetting("sb_private_user_id_Do_Not_Share", "");
|
public static final StringSetting SB_PRIVATE_USER_ID = new StringSetting("sb_private_user_id_Do_Not_Share", "");
|
||||||
public static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
|
|
||||||
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
||||||
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
||||||
public static final BooleanSetting SB_CREATE_NEW_SEGMENT = new BooleanSetting("sb_create_new_segment", FALSE, parent(SB_ENABLED));
|
public static final BooleanSetting SB_CREATE_NEW_SEGMENT = new BooleanSetting("sb_create_new_segment", FALSE, parent(SB_ENABLED));
|
||||||
@@ -331,34 +336,38 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting SB_TRACK_SKIP_COUNT = new BooleanSetting("sb_track_skip_count", TRUE, parent(SB_ENABLED));
|
public static final BooleanSetting SB_TRACK_SKIP_COUNT = new BooleanSetting("sb_track_skip_count", TRUE, parent(SB_ENABLED));
|
||||||
public static final FloatSetting SB_SEGMENT_MIN_DURATION = new FloatSetting("sb_min_segment_duration", 0F, parent(SB_ENABLED));
|
public static final FloatSetting SB_SEGMENT_MIN_DURATION = new FloatSetting("sb_min_segment_duration", 0F, parent(SB_ENABLED));
|
||||||
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", TRUE, parent(SB_ENABLED));
|
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", TRUE, parent(SB_ENABLED));
|
||||||
public static final StringSetting SB_API_URL = new StringSetting("sb_api_url","https://sponsor.ajay.app");
|
public static final StringSetting SB_API_URL = new StringSetting("sb_api_url", "https://sponsor.ajay.app");
|
||||||
public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE);
|
public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE);
|
||||||
public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
|
public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
|
||||||
public static final LongSetting SB_LOCAL_TIME_SAVED_MILLISECONDS = new LongSetting("sb_local_time_saved_milliseconds", 0L);
|
public static final LongSetting SB_LOCAL_TIME_SAVED_MILLISECONDS = new LongSetting("sb_local_time_saved_milliseconds", 0L);
|
||||||
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
|
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
|
||||||
public static final BooleanSetting SB_HIDE_EXPORT_WARNING = new BooleanSetting("sb_hide_export_warning", FALSE, false, false);
|
public static final BooleanSetting SB_HIDE_EXPORT_WARNING = new BooleanSetting("sb_hide_export_warning", FALSE, false, false);
|
||||||
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
|
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
|
||||||
|
|
||||||
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color","#00D400");
|
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color", "#00D400");
|
||||||
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", MANUAL_SKIP.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color","#FFFF00");
|
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00");
|
||||||
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color","#CC00FF");
|
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
|
||||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color","#FF1684");
|
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
|
||||||
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color","#00FFFF");
|
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
|
||||||
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color","#0202ED");
|
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
|
||||||
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", IGNORE.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", IGNORE.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color","#008FD6");
|
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
|
||||||
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", IGNORE.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", IGNORE.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color","#7300FF");
|
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
|
||||||
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color","#FF9900");
|
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900");
|
||||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color","#FFFFFF");
|
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
|
||||||
|
|
||||||
|
// Deprecated migrations
|
||||||
|
public static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
|
||||||
|
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
|
||||||
|
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// region Migration
|
// region Migration
|
||||||
@@ -384,7 +393,8 @@ public class Settings extends BaseSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Migrate renamed enum.
|
// Migrate renamed enum.
|
||||||
if (MINIPLAYER_TYPE.get() == PHONE) {
|
//noinspection deprecation
|
||||||
|
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
|
||||||
MINIPLAYER_TYPE.save(MINIMAL);
|
MINIPLAYER_TYPE.save(MINIMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.settings.preference;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.DeviceHardwareSupport.DEVICE_HAS_HARDWARE_DECODING_VP9;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.preference.SwitchPreference;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "deprecation"})
|
|
||||||
public class ForceAVCSpoofingPreference extends SwitchPreference {
|
|
||||||
{
|
|
||||||
if (!DEVICE_HAS_HARDWARE_DECODING_VP9) {
|
|
||||||
setSummaryOn(str("revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForceAVCSpoofingPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
public ForceAVCSpoofingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
public ForceAVCSpoofingPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
public ForceAVCSpoofingPreference(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUI() {
|
|
||||||
if (DEVICE_HAS_HARDWARE_DECODING_VP9) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily remove the preference key to allow changing this preference without
|
|
||||||
// causing the settings UI listeners from showing reboot dialogs by the changes made here.
|
|
||||||
String key = getKey();
|
|
||||||
setKey(null);
|
|
||||||
|
|
||||||
// This setting cannot be changed by the user.
|
|
||||||
super.setEnabled(false);
|
|
||||||
super.setChecked(true);
|
|
||||||
|
|
||||||
setKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
super.setChecked(checked);
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ import android.os.Build;
|
|||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -18,6 +19,10 @@ import android.widget.Toolbar;
|
|||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
|
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
|
||||||
@@ -41,6 +46,55 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
return Utils.getContext().getResources().getDrawable(backButtonResource);
|
return Utils.getContext().getResources().getDrawable(backButtonResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts a preference list by menu entries, but preserves the first value as the first entry.
|
||||||
|
*
|
||||||
|
* @noinspection SameParameterValue
|
||||||
|
*/
|
||||||
|
private static void sortListPreferenceByValues(ListPreference listPreference, int firstEntriesToPreserve) {
|
||||||
|
CharSequence[] entries = listPreference.getEntries();
|
||||||
|
CharSequence[] entryValues = listPreference.getEntryValues();
|
||||||
|
final int entrySize = entries.length;
|
||||||
|
|
||||||
|
if (entrySize != entryValues.length) {
|
||||||
|
// Xml array declaration has a missing/extra entry.
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Pair<String, String>> firstPairs = new ArrayList<>(firstEntriesToPreserve);
|
||||||
|
List<Pair<String, String>> pairsToSort = new ArrayList<>(entrySize);
|
||||||
|
|
||||||
|
for (int i = 0; i < entrySize; i++) {
|
||||||
|
Pair<String, String> pair = new Pair<>(entries[i].toString(), entryValues[i].toString());
|
||||||
|
if (i < firstEntriesToPreserve) {
|
||||||
|
firstPairs.add(pair);
|
||||||
|
} else {
|
||||||
|
pairsToSort.add(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
|
||||||
|
|
||||||
|
CharSequence[] sortedEntries = new CharSequence[entrySize];
|
||||||
|
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (Pair<String, String> pair : firstPairs) {
|
||||||
|
sortedEntries[i] = pair.first;
|
||||||
|
sortedEntryValues[i] = pair.second;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Pair<String, String> pair : pairsToSort) {
|
||||||
|
sortedEntries[i] = pair.first;
|
||||||
|
sortedEntryValues[i] = pair.second;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
listPreference.setEntries(sortedEntries);
|
||||||
|
listPreference.setEntryValues(sortedEntryValues);
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
@Override
|
@Override
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
@@ -50,9 +104,14 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
setPreferenceScreenToolbar(getPreferenceScreen());
|
setPreferenceScreenToolbar(getPreferenceScreen());
|
||||||
|
|
||||||
// If the preference was included, then initialize it based on the available playback speed.
|
// If the preference was included, then initialize it based on the available playback speed.
|
||||||
Preference defaultSpeedPreference = findPreference(Settings.PLAYBACK_SPEED_DEFAULT.key);
|
Preference preference = findPreference(Settings.PLAYBACK_SPEED_DEFAULT.key);
|
||||||
if (defaultSpeedPreference instanceof ListPreference) {
|
if (preference instanceof ListPreference playbackPreference) {
|
||||||
CustomPlaybackSpeedPatch.initializeListPreference((ListPreference) defaultSpeedPreference);
|
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
|
||||||
|
if (preference instanceof ListPreference languagePreference) {
|
||||||
|
sortListPreferenceByValues(languagePreference, 1);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "initialize failure", ex);
|
Logger.printException(() -> "initialize failure", ex);
|
||||||
|
|||||||
@@ -507,7 +507,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
|
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
|
||||||
} else {
|
} else {
|
||||||
preference.setText(userName); // revert to previous
|
preference.setText(userName); // revert to previous
|
||||||
Utils.showToastLong(errorMessage);
|
SponsorBlockUtils.showErrorDialog(errorMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package app.revanced.extension.youtube.settings.preference;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.Utils;
|
||||||
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
|
@SuppressWarnings({"deprecation", "unused"})
|
||||||
|
public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ClientType currentClientType;
|
||||||
|
|
||||||
|
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||||
|
// Because this listener may run before the ReVanced settings fragment updates Settings,
|
||||||
|
// this could show the prior config and not the current.
|
||||||
|
//
|
||||||
|
// Push this call to the end of the main run queue,
|
||||||
|
// so all other listeners are done and Settings is up to date.
|
||||||
|
Utils.runOnMainThread(this::updateUI);
|
||||||
|
};
|
||||||
|
|
||||||
|
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoofStreamingDataSideEffectsPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addChangeListener() {
|
||||||
|
Setting.preferences.preferences.registerOnSharedPreferenceChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeChangeListener() {
|
||||||
|
Setting.preferences.preferences.unregisterOnSharedPreferenceChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
|
||||||
|
super.onAttachedToHierarchy(preferenceManager);
|
||||||
|
updateUI();
|
||||||
|
addChangeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareForRemoval() {
|
||||||
|
super.onPrepareForRemoval();
|
||||||
|
removeChangeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUI() {
|
||||||
|
ClientType clientType = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
||||||
|
if (currentClientType == clientType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.printDebug(() -> "Updating spoof stream side effects preference");
|
||||||
|
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
|
||||||
|
|
||||||
|
String key = "revanced_spoof_video_streams_about_"
|
||||||
|
+ clientType.name().toLowerCase();
|
||||||
|
setTitle(str(key + "_title"));
|
||||||
|
setSummary(str(key + "_summary"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -363,6 +363,16 @@ public class SponsorBlockUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showErrorDialog(String dialogMessage) {
|
||||||
|
Utils.runOnMainThreadNowOrLater(() ->
|
||||||
|
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
|
||||||
|
.setMessage(dialogMessage)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static void onEditByHandClicked() {
|
public static void onEditByHandClicked() {
|
||||||
try {
|
try {
|
||||||
Utils.verifyOnMainThread();
|
Utils.verifyOnMainThread();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||||
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
||||||
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
|
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
|
||||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
||||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
|
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
|
||||||
@@ -142,6 +143,7 @@ public class SBRequester {
|
|||||||
public static void submitSegments(@NonNull String videoId, @NonNull String category,
|
public static void submitSegments(@NonNull String videoId, @NonNull String category,
|
||||||
long startTime, long endTime, long videoLength) {
|
long startTime, long endTime, long videoLength) {
|
||||||
Utils.verifyOffMainThread();
|
Utils.verifyOffMainThread();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String privateUserId = SponsorBlockSettings.getSBPrivateUserID();
|
String privateUserId = SponsorBlockSettings.getSBPrivateUserID();
|
||||||
String start = String.format(Locale.US, TIME_TEMPLATE, startTime / 1000f);
|
String start = String.format(Locale.US, TIME_TEMPLATE, startTime / 1000f);
|
||||||
@@ -151,35 +153,29 @@ public class SBRequester {
|
|||||||
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
|
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
|
||||||
final int responseCode = connection.getResponseCode();
|
final int responseCode = connection.getResponseCode();
|
||||||
|
|
||||||
final String messageToToast;
|
String userMessage = switch (responseCode) {
|
||||||
switch (responseCode) {
|
case HTTP_STATUS_CODE_SUCCESS -> str("revanced_sb_submit_succeeded");
|
||||||
case HTTP_STATUS_CODE_SUCCESS:
|
case 409 -> str("revanced_sb_submit_failed_duplicate");
|
||||||
messageToToast = str("revanced_sb_submit_succeeded");
|
case 403 -> str("revanced_sb_submit_failed_forbidden",
|
||||||
break;
|
Requester.parseErrorStringAndDisconnect(connection));
|
||||||
case 409:
|
case 429 -> str("revanced_sb_submit_failed_rate_limit");
|
||||||
messageToToast = str("revanced_sb_submit_failed_duplicate");
|
case 400 -> str("revanced_sb_submit_failed_invalid",
|
||||||
break;
|
Requester.parseErrorStringAndDisconnect(connection));
|
||||||
case 403:
|
default -> str("revanced_sb_submit_failed_unknown_error",
|
||||||
messageToToast = str("revanced_sb_submit_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection));
|
responseCode, connection.getResponseMessage());
|
||||||
break;
|
};
|
||||||
case 429:
|
|
||||||
messageToToast = str("revanced_sb_submit_failed_rate_limit");
|
// Message might be about the users account or an error too large to show in a toast.
|
||||||
break;
|
// Use a dialog instead.
|
||||||
case 400:
|
SponsorBlockUtils.showErrorDialog(userMessage);
|
||||||
messageToToast = str("revanced_sb_submit_failed_invalid", Requester.parseErrorStringAndDisconnect(connection));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
messageToToast = str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Utils.showToastLong(messageToToast);
|
|
||||||
} catch (SocketTimeoutException ex) {
|
} catch (SocketTimeoutException ex) {
|
||||||
// Always show, even if show connection toasts is turned off
|
Logger.printDebug(() -> "Timeout", ex);
|
||||||
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));
|
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
Logger.printDebug(() -> "IOException", ex);
|
||||||
Utils.showToastLong(str("revanced_sb_submit_failed_unknown_error", 0, ex.getMessage()));
|
Utils.showToastLong(str("revanced_sb_submit_failed_unknown_error", 0, ex.getMessage()));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "failed to submit segments", ex);
|
Logger.printException(() -> "failed to submit segments", ex); // Should never happen.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,19 +214,22 @@ public class SBRequester {
|
|||||||
: getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, uuid, segmentUuid, String.valueOf(voteOption.apiVoteType));
|
: getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, uuid, segmentUuid, String.valueOf(voteOption.apiVoteType));
|
||||||
final int responseCode = connection.getResponseCode();
|
final int responseCode = connection.getResponseCode();
|
||||||
|
|
||||||
|
String userMessage;
|
||||||
switch (responseCode) {
|
switch (responseCode) {
|
||||||
case HTTP_STATUS_CODE_SUCCESS:
|
case HTTP_STATUS_CODE_SUCCESS:
|
||||||
Logger.printDebug(() -> "Vote success for segment: " + segment);
|
Logger.printDebug(() -> "Vote success for segment: " + segment);
|
||||||
break;
|
return;
|
||||||
case 403:
|
case 403:
|
||||||
Utils.showToastLong(
|
userMessage = str("revanced_sb_vote_failed_forbidden",
|
||||||
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection)));
|
Requester.parseErrorStringAndDisconnect(connection));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Utils.showToastLong(
|
userMessage = str("revanced_sb_vote_failed_unknown_error",
|
||||||
str("revanced_sb_vote_failed_unknown_error", responseCode, connection.getResponseMessage()));
|
responseCode, connection.getResponseMessage());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SponsorBlockUtils.showErrorDialog(userMessage);
|
||||||
} catch (SocketTimeoutException ex) {
|
} catch (SocketTimeoutException ex) {
|
||||||
Utils.showToastShort(str("revanced_sb_vote_failed_timeout"));
|
Utils.showToastShort(str("revanced_sb_vote_failed_timeout"));
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.2.3-dev.1
|
version = 5.6.0-dev.3
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ public final class app/revanced/patches/all/misc/directory/ChangeDataDirectoryLo
|
|||||||
public static final fun getChangeDataDirectoryLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getChangeDataDirectoryLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatchKt {
|
||||||
|
public static final fun getExportInternalDataDocumentsProviderPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/hex/HexPatchKt {
|
public final class app/revanced/patches/all/misc/hex/HexPatchKt {
|
||||||
public static final fun getHexPatch ()Lapp/revanced/patcher/patch/RawResourcePatch;
|
public static final fun getHexPatch ()Lapp/revanced/patcher/patch/RawResourcePatch;
|
||||||
}
|
}
|
||||||
@@ -320,6 +324,14 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
|
|||||||
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt {
|
||||||
|
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||||
|
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
|
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
|
||||||
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -753,6 +765,15 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref
|
|||||||
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
|
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
|
||||||
|
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||||
|
public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
|
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
|
||||||
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1155,6 +1176,14 @@ public final class app/revanced/patches/youtube/layout/player/background/PlayerC
|
|||||||
public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt {
|
||||||
|
public static final fun getOpenVideosFullscreen ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt {
|
||||||
|
public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt {
|
public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt {
|
||||||
public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1185,6 +1214,10 @@ public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAuto
|
|||||||
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatchKt {
|
||||||
|
public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
|
public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
|
||||||
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1359,6 +1392,14 @@ public final class app/revanced/patches/youtube/misc/settings/SettingsPatchKt {
|
|||||||
public static final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
|
public static final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatchKt {
|
||||||
|
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||||
|
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatchKt {
|
public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatchKt {
|
||||||
public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1367,6 +1408,10 @@ public final class app/revanced/patches/youtube/shared/FingerprintsKt {
|
|||||||
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
|
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
|
||||||
|
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
|
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
|
||||||
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V
|
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
|||||||
@@ -1,58 +1,19 @@
|
|||||||
package app.revanced.patches.all.misc.directory
|
package app.revanced.patches.all.misc.directory
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
import app.revanced.patches.all.misc.directory.documentsprovider.exportInternalDataDocumentsProviderPatch
|
||||||
import app.revanced.util.getReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@Deprecated(
|
||||||
|
"Superseded by internalDataDocumentsProviderPatch",
|
||||||
|
ReplaceWith("internalDataDocumentsProviderPatch"),
|
||||||
|
)
|
||||||
val changeDataDirectoryLocationPatch = bytecodePatch(
|
val changeDataDirectoryLocationPatch = bytecodePatch(
|
||||||
name = "Change data directory location",
|
// name = "Change data directory location",
|
||||||
description = "Changes the data directory in the application from " +
|
description = "Changes the data directory in the application from " +
|
||||||
"the app internal storage directory to /sdcard/android/data accessible by root-less devices." +
|
"the app internal storage directory to /sdcard/android/data accessible by root-less devices." +
|
||||||
"Using this patch can cause unexpected issues with some apps.",
|
"Using this patch can cause unexpected issues with some apps.",
|
||||||
use = false,
|
use = false,
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(exportInternalDataDocumentsProviderPatch)
|
||||||
transformInstructionsPatch(
|
|
||||||
filterMap = filter@{ _, _, instruction, instructionIndex ->
|
|
||||||
val reference = instruction.getReference<MethodReference>() ?: return@filter null
|
|
||||||
|
|
||||||
if (!MethodUtil.methodSignaturesMatch(reference, MethodCall.GetDir.reference)) {
|
|
||||||
return@filter null
|
|
||||||
}
|
|
||||||
|
|
||||||
return@filter instructionIndex
|
|
||||||
},
|
|
||||||
transform = { method, index ->
|
|
||||||
val getDirInstruction = method.getInstruction<Instruction35c>(index)
|
|
||||||
val contextRegister = getDirInstruction.registerC
|
|
||||||
val dataRegister = getDirInstruction.registerD
|
|
||||||
|
|
||||||
method.replaceInstruction(
|
|
||||||
index,
|
|
||||||
"invoke-virtual { v$contextRegister, v$dataRegister }, " +
|
|
||||||
"Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum class MethodCall(
|
|
||||||
val reference: MethodReference,
|
|
||||||
) {
|
|
||||||
GetDir(
|
|
||||||
ImmutableMethodReference(
|
|
||||||
"Landroid/content/Context;",
|
|
||||||
"getDir",
|
|
||||||
listOf("Ljava/lang/String;", "I"),
|
|
||||||
"Ljava/io/File;",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package app.revanced.patches.all.misc.directory.documentsprovider
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
|
import app.revanced.util.asSequence
|
||||||
|
import app.revanced.util.getNode
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val exportInternalDataDocumentsProviderPatch = resourcePatch(
|
||||||
|
name = "Export internal data documents provider",
|
||||||
|
description = "Exports a documents provider that grants access to the internal data directory of this app " +
|
||||||
|
"to file managers and other apps that support the Storage Access Framework.",
|
||||||
|
use = false,
|
||||||
|
) {
|
||||||
|
dependsOn(
|
||||||
|
bytecodePatch {
|
||||||
|
extendWith("extensions/all/misc/directory/export-internal-data-documents-provider.rve")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
val documentsProviderClass =
|
||||||
|
"app.revanced.extension.all.misc.directory.documentsprovider.InternalDataDocumentsProvider"
|
||||||
|
|
||||||
|
document("AndroidManifest.xml").use { document ->
|
||||||
|
// Check if the provider is already declared
|
||||||
|
if (document.getElementsByTagName("provider")
|
||||||
|
.asSequence()
|
||||||
|
.any { it.attributes.getNamedItem("android:name")?.nodeValue == documentsProviderClass }
|
||||||
|
) {
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
|
val authority =
|
||||||
|
document.getNode("manifest").attributes.getNamedItem("package").let {
|
||||||
|
// Select a URI authority name that is unique to the current app
|
||||||
|
"${it.nodeValue}.$documentsProviderClass"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the documents provider
|
||||||
|
with(document.getNode("application")) {
|
||||||
|
document.createElement("provider").apply {
|
||||||
|
setAttribute("android:name", documentsProviderClass)
|
||||||
|
setAttribute("android:authorities", authority)
|
||||||
|
setAttribute("android:exported", "true")
|
||||||
|
setAttribute("android:grantUriPermissions", "true")
|
||||||
|
setAttribute("android:permission", "android.permission.MANAGE_DOCUMENTS")
|
||||||
|
|
||||||
|
document.createElement("intent-filter").apply {
|
||||||
|
document.createElement("action").apply {
|
||||||
|
setAttribute("android:name", "android.content.action.DOCUMENTS_PROVIDER")
|
||||||
|
}.let(this::appendChild)
|
||||||
|
}.let(this::appendChild)
|
||||||
|
}.let(this::appendChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
package app.revanced.patches.all.misc.packagename
|
package app.revanced.patches.all.misc.packagename
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Option
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.util.asSequence
|
||||||
import app.revanced.patcher.patch.stringOption
|
import app.revanced.util.getNode
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
lateinit var packageNameOption: Option<String>
|
lateinit var packageNameOption: Option<String>
|
||||||
|
|
||||||
@@ -27,7 +28,8 @@ fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
|
|||||||
|
|
||||||
val changePackageNamePatch = resourcePatch(
|
val changePackageNamePatch = resourcePatch(
|
||||||
name = "Change package name",
|
name = "Change package name",
|
||||||
description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.",
|
description = "Appends \".revanced\" to the package name by default. " +
|
||||||
|
"Changing the package name of the app can lead to unexpected issues.",
|
||||||
use = false,
|
use = false,
|
||||||
) {
|
) {
|
||||||
packageNameOption = stringOption(
|
packageNameOption = stringOption(
|
||||||
@@ -41,20 +43,81 @@ val changePackageNamePatch = resourcePatch(
|
|||||||
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
|
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val updatePermissions by booleanOption(
|
||||||
|
key = "updatePermissions",
|
||||||
|
default = false,
|
||||||
|
title = "Update permissions",
|
||||||
|
description = "Update compatibility receiver permissions. " +
|
||||||
|
"Enabling this can fix installation errors, but this can also break features in certain apps.",
|
||||||
|
)
|
||||||
|
|
||||||
|
val updateProviders by booleanOption(
|
||||||
|
key = "updateProviders",
|
||||||
|
default = false,
|
||||||
|
title = "Update providers",
|
||||||
|
description = "Update provider names declared by the app. " +
|
||||||
|
"Enabling this can fix installation errors, but this can also break features in certain apps.",
|
||||||
|
)
|
||||||
|
|
||||||
finalize {
|
finalize {
|
||||||
|
/**
|
||||||
|
* Apps that are confirmed to not work correctly with this patch.
|
||||||
|
* This is not an exhaustive list, and is only the apps with
|
||||||
|
* ReVanced specific patches and are confirmed incompatible with this patch.
|
||||||
|
*/
|
||||||
|
val incompatibleAppPackages = setOf(
|
||||||
|
// Cannot log in, settings menu is broken.
|
||||||
|
"com.reddit.frontpage",
|
||||||
|
|
||||||
|
// Patches and installs but crashes on launch.
|
||||||
|
"com.duolingo",
|
||||||
|
"com.twitter.android",
|
||||||
|
"tv.twitch.android.app",
|
||||||
|
)
|
||||||
|
|
||||||
document("AndroidManifest.xml").use { document ->
|
document("AndroidManifest.xml").use { document ->
|
||||||
|
val manifest = document.getNode("manifest") as Element
|
||||||
|
val packageName = manifest.getAttribute("package")
|
||||||
|
|
||||||
|
if (incompatibleAppPackages.contains(packageName)) {
|
||||||
|
return@finalize Logger.getLogger(this::class.java.name).severe(
|
||||||
|
"'$packageName' does not work correctly with \"Change package name\"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val replacementPackageName = packageNameOption.value
|
val replacementPackageName = packageNameOption.value
|
||||||
|
val newPackageName = if (replacementPackageName != packageNameOption.default) {
|
||||||
|
replacementPackageName!!
|
||||||
|
} else {
|
||||||
|
"$packageName.revanced"
|
||||||
|
}
|
||||||
|
|
||||||
val manifest = document.getElementsByTagName("manifest").item(0) as Element
|
manifest.setAttribute("package", newPackageName)
|
||||||
manifest.setAttribute(
|
|
||||||
"package",
|
if (updatePermissions == true) {
|
||||||
if (replacementPackageName != packageNameOption.default) {
|
val permissions = manifest.getElementsByTagName("permission").asSequence()
|
||||||
replacementPackageName
|
val usesPermissions = manifest.getElementsByTagName("uses-permission").asSequence()
|
||||||
} else {
|
|
||||||
"${manifest.getAttribute("package")}.revanced"
|
val receiverNotExported = "DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||||
},
|
|
||||||
)
|
(permissions + usesPermissions)
|
||||||
|
.map { it as Element }
|
||||||
|
.filter { it.getAttribute("android:name") == "$packageName.$receiverNotExported" }
|
||||||
|
.forEach { it.setAttribute("android:name", "$newPackageName.$receiverNotExported") }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateProviders == true) {
|
||||||
|
val providers = manifest.getElementsByTagName("provider").asSequence()
|
||||||
|
|
||||||
|
for (node in providers) {
|
||||||
|
val provider = node as Element
|
||||||
|
|
||||||
|
val authorities = provider.getAttribute("android:authorities")
|
||||||
|
if (!authorities.startsWith("$packageName.")) continue
|
||||||
|
|
||||||
|
provider.setAttribute("android:authorities", authorities.replace(packageName, newPackageName))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package app.revanced.patches.music.interaction.permanentshuffle
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
|
||||||
|
@Deprecated("This patch no longer works and will be removed in the future.")
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val permanentShufflePatch = bytecodePatch(
|
val permanentShufflePatch = bytecodePatch(
|
||||||
name = "Permanent shuffle",
|
|
||||||
description = "Permanently remember your shuffle preference " +
|
description = "Permanently remember your shuffle preference " +
|
||||||
"even if the playlist ends or another track is played.",
|
"even if the playlist ends or another track is played.",
|
||||||
use = false,
|
use = false,
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
package app.revanced.patches.music.misc.androidauto
|
package app.revanced.patches.music.misc.androidauto
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val bypassCertificateChecksPatch = bytecodePatch(
|
val bypassCertificateChecksPatch = bytecodePatch(
|
||||||
name = "Bypass certificate checks",
|
name = "Bypass certificate checks",
|
||||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.google.android.apps.youtube.music")
|
compatibleWith("com.google.android.apps.youtube.music"("7.29.52"))
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
checkCertificateFingerprint.method.addInstructions(
|
checkCertificateFingerprint.method.returnEarly(true)
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const/4 v0, 0x1
|
|
||||||
return v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
|
|||||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
|
||||||
val sharedExtensionPatch = sharedExtensionPatch(
|
val sharedExtensionPatch = sharedExtensionPatch(
|
||||||
|
"music",
|
||||||
applicationInitHook,
|
applicationInitHook,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import app.revanced.patcher.patch.Option
|
|||||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
||||||
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.music.misc.spoof.spoofClientPatch
|
||||||
import app.revanced.patches.shared.castContextFetchFingerprint
|
import app.revanced.patches.shared.castContextFetchFingerprint
|
||||||
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
||||||
import app.revanced.patches.shared.primeMethodFingerprint
|
import app.revanced.patches.shared.primeMethodFingerprint
|
||||||
@@ -20,6 +21,8 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
|||||||
extensionPatch = sharedExtensionPatch,
|
extensionPatch = sharedExtensionPatch,
|
||||||
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
|
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
|
||||||
) {
|
) {
|
||||||
|
dependsOn(spoofClientPatch)
|
||||||
|
|
||||||
compatibleWith(MUSIC_PACKAGE_NAME)
|
compatibleWith(MUSIC_PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package app.revanced.patches.music.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal val playerRequestConstructorFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||||
|
strings("player")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches using the class found in [playerRequestConstructorFingerprint].
|
||||||
|
*/
|
||||||
|
internal val createPlayerRequestBodyFingerprint = fingerprint {
|
||||||
|
parameters("L")
|
||||||
|
returns("V")
|
||||||
|
opcodes(
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.AND_INT_LIT16,
|
||||||
|
)
|
||||||
|
strings("ms")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to get a reference to other clientInfo fields.
|
||||||
|
*/
|
||||||
|
internal val setClientInfoFieldsFingerprint = fingerprint {
|
||||||
|
returns("L")
|
||||||
|
strings("Google Inc.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to get a reference to the clientInfo and clientInfo.clientVersion field.
|
||||||
|
*/
|
||||||
|
internal val setClientInfoClientVersionFingerprint = fingerprint {
|
||||||
|
strings("10.29")
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package app.revanced.patches.music.misc.spoof
|
||||||
|
|
||||||
|
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.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
|
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.TypeReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
|
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
|
||||||
|
|
||||||
|
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
|
||||||
|
val spoofClientPatch = bytecodePatch(
|
||||||
|
name = "Spoof client",
|
||||||
|
description = "Spoofs the client to fix playback.",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.google.android.apps.youtube.music")
|
||||||
|
|
||||||
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
// TODO: Add settingsPatch
|
||||||
|
userAgentClientSpoofPatch,
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
val playerRequestClass = playerRequestConstructorFingerprint.classDef
|
||||||
|
|
||||||
|
val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass)
|
||||||
|
|
||||||
|
val clientInfoContainerClass = createPlayerRequestBodyMatch.method
|
||||||
|
.getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex)
|
||||||
|
.getReference<TypeReference>()!!.type
|
||||||
|
|
||||||
|
val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first {
|
||||||
|
it.opcode == Opcode.IPUT_OBJECT && it.getReference<FieldReference>()!!.definingClass == clientInfoContainerClass
|
||||||
|
}.getReference<FieldReference>()!!
|
||||||
|
|
||||||
|
val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter {
|
||||||
|
(it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) &&
|
||||||
|
it.getReference<FieldReference>()!!.definingClass == clientInfoField.type
|
||||||
|
}.map { it.getReference<FieldReference>()!! }
|
||||||
|
|
||||||
|
// Offsets are known for the fields in the clientInfo object.
|
||||||
|
val clientIdField = setClientInfoFieldInstructions[0]
|
||||||
|
val clientModelField = setClientInfoFieldInstructions[5]
|
||||||
|
val osVersionField = setClientInfoFieldInstructions[7]
|
||||||
|
val clientVersionField = setClientInfoClientVersionFingerprint.method
|
||||||
|
.getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1)
|
||||||
|
.getReference<FieldReference>()
|
||||||
|
|
||||||
|
// Helper method to spoof the client info.
|
||||||
|
val spoofClientInfoMethod = ImmutableMethod(
|
||||||
|
playerRequestClass.type,
|
||||||
|
"spoofClientInfo",
|
||||||
|
listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)),
|
||||||
|
"V",
|
||||||
|
AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(3),
|
||||||
|
).toMutable().also(playerRequestClass.methods::add).apply {
|
||||||
|
addInstructions(
|
||||||
|
"""
|
||||||
|
iget-object v0, p0, $clientInfoField
|
||||||
|
|
||||||
|
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I
|
||||||
|
move-result v1
|
||||||
|
iput v1, v0, $clientIdField
|
||||||
|
|
||||||
|
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String;
|
||||||
|
move-result-object v1
|
||||||
|
iput-object v1, v0, $clientModelField
|
||||||
|
|
||||||
|
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String;
|
||||||
|
move-result-object v1
|
||||||
|
iput-object v1, v0, $clientVersionField
|
||||||
|
|
||||||
|
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String;
|
||||||
|
move-result-object v1
|
||||||
|
iput-object v1, v0, $osVersionField
|
||||||
|
|
||||||
|
return-void
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
createPlayerRequestBodyMatch.method.apply {
|
||||||
|
val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex
|
||||||
|
val clientInfoContainerRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
|
||||||
|
|
||||||
|
addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package app.revanced.patches.music.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
|
||||||
|
|
||||||
|
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.apps.youtube.music")
|
||||||
@@ -3,10 +3,9 @@ package app.revanced.patches.nyx.misc.pro
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
|
||||||
|
@Deprecated("This patch will be removed in the future.")
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val unlockProPatch = bytecodePatch(
|
val unlockProPatch = bytecodePatch {
|
||||||
name = "Unlock pro",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.awedea.nyx")
|
compatibleWith("com.awedea.nyx")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.extension
|
|||||||
import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks.initHook
|
import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks.initHook
|
||||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
|
||||||
val sharedExtensionPatch = sharedExtensionPatch("sync", initHook)
|
val sharedExtensionPatch = sharedExtensionPatch("syncforreddit", initHook)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.checks
|
|||||||
|
|
||||||
import android.os.Build.*
|
import android.os.Build.*
|
||||||
import app.revanced.patcher.Fingerprint
|
import app.revanced.patcher.Fingerprint
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||||
@@ -82,7 +82,7 @@ fun checkEnvironmentPatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstructions(
|
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
|
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.misc.fix.playback
|
package app.revanced.patches.shared.misc.spoof
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.literal
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
@@ -27,7 +28,6 @@ internal val buildPlayerRequestURIFingerprint = fingerprint {
|
|||||||
Opcode.RETURN_OBJECT,
|
Opcode.RETURN_OBJECT,
|
||||||
)
|
)
|
||||||
strings(
|
strings(
|
||||||
"youtubei/v1",
|
|
||||||
"key",
|
"key",
|
||||||
"asig",
|
"asig",
|
||||||
)
|
)
|
||||||
@@ -111,3 +111,13 @@ internal val buildMediaDataSourceFingerprint = fingerprint {
|
|||||||
"Ljava/lang/Object;",
|
"Ljava/lang/Object;",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L
|
||||||
|
|
||||||
|
internal val hlsCurrentTimeFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
parameters("Z", "L")
|
||||||
|
literal {
|
||||||
|
HLS_CURRENT_TIME_FEATURE_FLAG
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package app.revanced.patches.shared.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
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.instructions
|
||||||
|
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||||
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
|
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/shared/spoof/SpoofVideoStreamsPatch;"
|
||||||
|
|
||||||
|
fun spoofVideoStreamsPatch(
|
||||||
|
block: BytecodePatchBuilder.() -> Unit = {},
|
||||||
|
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||||
|
) = bytecodePatch(
|
||||||
|
name = "Spoof video streams",
|
||||||
|
description = "Spoofs the client video streams to fix playback.",
|
||||||
|
) {
|
||||||
|
block()
|
||||||
|
|
||||||
|
dependsOn(addResourcesPatch)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
// region Block /initplayback requests to fall back to /get_watch requests.
|
||||||
|
|
||||||
|
val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex
|
||||||
|
|
||||||
|
buildInitPlaybackRequestFingerprint.method.apply {
|
||||||
|
val targetRegister = getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
moveUriStringIndex + 1,
|
||||||
|
"""
|
||||||
|
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
move-result-object v$targetRegister
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Block /get_watch requests to fall back to /player requests.
|
||||||
|
|
||||||
|
val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex
|
||||||
|
|
||||||
|
buildPlayerRequestURIFingerprint.method.apply {
|
||||||
|
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
invokeToStringIndex,
|
||||||
|
"""
|
||||||
|
invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
||||||
|
move-result-object v$uriRegister
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Get replacement streams at player requests.
|
||||||
|
|
||||||
|
buildRequestFingerprint.method.apply {
|
||||||
|
val newRequestBuilderIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||||
|
getReference<MethodReference>()?.name == "newUrlRequestBuilder"
|
||||||
|
}
|
||||||
|
val urlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
|
||||||
|
val freeRegister = getInstruction<OneRegisterInstruction>(newRequestBuilderIndex + 1).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
newRequestBuilderIndex,
|
||||||
|
"""
|
||||||
|
move-object v$freeRegister, p1
|
||||||
|
invoke-static { v$urlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Replace the streaming data with the replacement streams.
|
||||||
|
|
||||||
|
createStreamingDataFingerprint.method.apply {
|
||||||
|
val setStreamDataMethodName = "patch_setStreamingData"
|
||||||
|
val resultMethodType = createStreamingDataFingerprint.classDef.type
|
||||||
|
val videoDetailsIndex = createStreamingDataFingerprint.patternMatch!!.endIndex
|
||||||
|
val videoDetailsRegister = getInstruction<TwoRegisterInstruction>(videoDetailsIndex).registerA
|
||||||
|
val videoDetailsClass = getInstruction(videoDetailsIndex).getReference<FieldReference>()!!.type
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
videoDetailsIndex + 1,
|
||||||
|
"invoke-direct { p0, v$videoDetailsRegister }, " +
|
||||||
|
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
||||||
|
)
|
||||||
|
|
||||||
|
val protobufClass = protobufClassParseByteBufferFingerprint.method.definingClass
|
||||||
|
val setStreamingDataIndex = createStreamingDataFingerprint.patternMatch!!.startIndex
|
||||||
|
|
||||||
|
val playerProtoClass = getInstruction(setStreamingDataIndex + 1)
|
||||||
|
.getReference<FieldReference>()!!.definingClass
|
||||||
|
|
||||||
|
val setStreamingDataField = getInstruction(setStreamingDataIndex).getReference<FieldReference>()
|
||||||
|
|
||||||
|
val getStreamingDataField = getInstruction(
|
||||||
|
indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.definingClass == playerProtoClass
|
||||||
|
},
|
||||||
|
).getReference<FieldReference>()
|
||||||
|
|
||||||
|
// Use a helper method to avoid the need of picking out multiple free registers from the hooked code.
|
||||||
|
createStreamingDataFingerprint.classDef.methods.add(
|
||||||
|
ImmutableMethod(
|
||||||
|
resultMethodType,
|
||||||
|
setStreamDataMethodName,
|
||||||
|
listOf(ImmutableMethodParameter(videoDetailsClass, null, "videoDetails")),
|
||||||
|
"V",
|
||||||
|
AccessFlags.PRIVATE.value or AccessFlags.FINAL.value,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(9),
|
||||||
|
).toMutable().apply {
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled()Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :disabled
|
||||||
|
|
||||||
|
# Get video id.
|
||||||
|
iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String;
|
||||||
|
if-eqz v2, :disabled
|
||||||
|
|
||||||
|
# Get streaming data.
|
||||||
|
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
|
||||||
|
move-result-object v3
|
||||||
|
if-eqz v3, :disabled
|
||||||
|
|
||||||
|
# Parse streaming data.
|
||||||
|
sget-object v4, $playerProtoClass->a:$playerProtoClass
|
||||||
|
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
|
||||||
|
move-result-object v5
|
||||||
|
check-cast v5, $playerProtoClass
|
||||||
|
|
||||||
|
# Set streaming data.
|
||||||
|
iget-object v6, v5, $getStreamingDataField
|
||||||
|
if-eqz v6, :disabled
|
||||||
|
iput-object v6, p0, $setStreamingDataField
|
||||||
|
|
||||||
|
:disabled
|
||||||
|
return-void
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Remove /videoplayback request body to fix playback.
|
||||||
|
// It is assumed, YouTube makes a request with a body tuned for Android.
|
||||||
|
// Requesting streams intended for other platforms with a body tuned for Android could be the cause of 400 errors.
|
||||||
|
// A proper fix may include modifying the request body to match the platforms expected body.
|
||||||
|
|
||||||
|
buildMediaDataSourceFingerprint.method.apply {
|
||||||
|
val targetIndex = instructions.lastIndex
|
||||||
|
|
||||||
|
// Instructions are added just before the method returns,
|
||||||
|
// so there's no concern of clobbering in-use registers.
|
||||||
|
addInstructions(
|
||||||
|
targetIndex,
|
||||||
|
"""
|
||||||
|
# Field a: Stream uri.
|
||||||
|
# Field c: Http method.
|
||||||
|
# Field d: Post data.
|
||||||
|
move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register.
|
||||||
|
iget-object v1, v0, $definingClass->a:Landroid/net/Uri;
|
||||||
|
iget v2, v0, $definingClass->c:I
|
||||||
|
iget-object v3, v0, $definingClass->d:[B
|
||||||
|
invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B
|
||||||
|
move-result-object v1
|
||||||
|
iput-object v1, v0, $definingClass->d:[B
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Fix iOS livestream current time.
|
||||||
|
|
||||||
|
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
HLS_CURRENT_TIME_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
|
||||||
|
)
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
executeBlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package app.revanced.patches.shared.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patches.all.misc.transformation.IMethodCall
|
||||||
|
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
||||||
|
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
|
|
||||||
|
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
|
||||||
|
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
|
||||||
|
|
||||||
|
fun userAgentClientSpoofPatch(originalPackageName: String) = transformInstructionsPatch(
|
||||||
|
filterMap = { classDef, _, instruction, instructionIndex ->
|
||||||
|
filterMapInstruction35c<MethodCall>(
|
||||||
|
"Lapp/revanced/extension",
|
||||||
|
classDef,
|
||||||
|
instruction,
|
||||||
|
instructionIndex,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
transform = transform@{ mutableMethod, entry ->
|
||||||
|
val (_, _, instructionIndex) = entry
|
||||||
|
|
||||||
|
// Replace the result of context.getPackageName(), if it is used in a user agent string.
|
||||||
|
mutableMethod.apply {
|
||||||
|
// After context.getPackageName() the result is moved to a register.
|
||||||
|
val targetRegister = (
|
||||||
|
getInstruction(instructionIndex + 1)
|
||||||
|
as? OneRegisterInstruction ?: return@transform
|
||||||
|
).registerA
|
||||||
|
|
||||||
|
// IndexOutOfBoundsException is technically possible here,
|
||||||
|
// but no such occurrences are present in the app.
|
||||||
|
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
|
||||||
|
|
||||||
|
// Only replace string builder usage.
|
||||||
|
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
|
||||||
|
return@transform
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
|
||||||
|
// Changing these package names will result in playback limitations,
|
||||||
|
// particularly Android VR background audio only playback.
|
||||||
|
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
|
||||||
|
val reference = getReference<StringReference>()
|
||||||
|
opcode == Opcode.CONST_STRING &&
|
||||||
|
(reference?.string == "android.resource://" || reference?.string == "gcore_")
|
||||||
|
}
|
||||||
|
if (resourceOrGmsStringInstructionIndex >= 0) {
|
||||||
|
return@transform
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite the result of context.getPackageName() with the original package name.
|
||||||
|
replaceInstruction(
|
||||||
|
instructionIndex + 1,
|
||||||
|
"const-string v$targetRegister, \"$originalPackageName\"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
private enum class MethodCall(
|
||||||
|
override val definedClassName: String,
|
||||||
|
override val methodName: String,
|
||||||
|
override val methodParams: Array<String>,
|
||||||
|
override val returnType: String,
|
||||||
|
) : IMethodCall {
|
||||||
|
GetPackageName(
|
||||||
|
"Landroid/content/Context;",
|
||||||
|
"getPackageName",
|
||||||
|
emptyArray(),
|
||||||
|
"Ljava/lang/String;",
|
||||||
|
),
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ val audioAdsPatch = bytecodePatch(
|
|||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
|
compatibleWith("tv.twitch.android.app")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("twitch", "ad.audio.audioAdsPatch")
|
addResources("twitch", "ad.audio.audioAdsPatch")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ val embeddedAdsPatch = bytecodePatch(
|
|||||||
settingsPatch,
|
settingsPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
|
compatibleWith("tv.twitch.android.app")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("twitch", "ad.embedded.embeddedAdsPatch")
|
addResources("twitch", "ad.embedded.embeddedAdsPatch")
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ import app.revanced.patcher.fingerprint
|
|||||||
|
|
||||||
internal val createsUsherClientFingerprint = fingerprint {
|
internal val createsUsherClientFingerprint = fingerprint {
|
||||||
custom { method, _ ->
|
custom { method, _ ->
|
||||||
method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient"
|
method.name == "buildOkHttpClient" && method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,24 +141,21 @@ val videoAdsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Spoof showAds JSON field.
|
// Spoof showAds JSON field.
|
||||||
contentConfigShowAdsFingerprint.method.addInstructions(
|
// Late versions of the app don't have the method anymore.
|
||||||
|
contentConfigShowAdsFingerprint.methodOrNull?.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
${createConditionInstructions("v0")}
|
${createConditionInstructions("v0")}
|
||||||
const/4 v0, 0
|
const/4 v0, 0
|
||||||
:$skipLabelName
|
:$skipLabelName
|
||||||
return v0
|
return v0
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
"tv.twitch.android.app"(
|
"tv.twitch.android.app",
|
||||||
"15.4.1",
|
|
||||||
"16.1.0",
|
|
||||||
"16.9.1",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ val showDeletedMessagesPatch = bytecodePatch(
|
|||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
|
compatibleWith("tv.twitch.android.app")
|
||||||
|
|
||||||
fun createSpoilerConditionInstructions(register: String = "v0") = """
|
fun createSpoilerConditionInstructions(register: String = "v0") = """
|
||||||
invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z
|
invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
|
|||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
|
compatibleWith("tv.twitch.android.app")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")
|
addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")
|
||||||
|
|||||||
@@ -48,13 +48,7 @@ val settingsPatch = bytecodePatch(
|
|||||||
settingsPatch(preferences = preferences),
|
settingsPatch(preferences = preferences),
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith(
|
compatibleWith("tv.twitch.android.app")
|
||||||
"tv.twitch.android.app"(
|
|
||||||
"15.4.1",
|
|
||||||
"16.1.0",
|
|
||||||
"16.9.1",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("twitch", "misc.settings.settingsPatch")
|
addResources("twitch", "misc.settings.settingsPatch")
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
|||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.reddit.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch
|
||||||
import java.io.InvalidClassException
|
import java.io.InvalidClassException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
|
|
||||||
internal var tweetShareLinkTemplateId = -1L
|
internal var tweetShareLinkTemplateId = -1L
|
||||||
private set
|
private set
|
||||||
@@ -25,15 +25,7 @@ internal val changeLinkSharingDomainResourcePatch = resourcePatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is used to build the link that is shared when the "Share via..." button is pressed.
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/links/ChangeLinkSharingDomainPatch;"
|
||||||
private const val FORMAT_METHOD_RESOURCE_REFERENCE =
|
|
||||||
"Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
|
|
||||||
"formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;"
|
|
||||||
|
|
||||||
// This method is used to build the link that is shared when the "Copy link" button is pressed.
|
|
||||||
private const val FORMAT_METHOD_REFERENCE =
|
|
||||||
"Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
|
|
||||||
"formatLink(JLjava/lang/String;)Ljava/lang/String;"
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val changeLinkSharingDomainPatch = bytecodePatch(
|
val changeLinkSharingDomainPatch = bytecodePatch(
|
||||||
@@ -71,7 +63,7 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
|||||||
addInstructions(
|
addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
invoke-static { p0, p1, p2 }, $FORMAT_METHOD_REFERENCE
|
invoke-static { p0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->formatLink(JLjava/lang/String;)Ljava/lang/String;
|
||||||
move-result-object p0
|
move-result-object p0
|
||||||
return-object p0
|
return-object p0
|
||||||
""",
|
""",
|
||||||
@@ -84,12 +76,12 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
|||||||
|
|
||||||
// Format the link with the new domain name register (1 instruction below the const).
|
// Format the link with the new domain name register (1 instruction below the const).
|
||||||
val formatLinkCallIndex = templateIdConstIndex + 1
|
val formatLinkCallIndex = templateIdConstIndex + 1
|
||||||
val formatLinkCall = getInstruction<Instruction35c>(formatLinkCallIndex)
|
val register = getInstruction<FiveRegisterInstruction>(formatLinkCallIndex).registerE
|
||||||
|
|
||||||
// Replace the original method call with the new method call.
|
// Replace the original method call with the new method call.
|
||||||
replaceInstruction(
|
replaceInstruction(
|
||||||
formatLinkCallIndex,
|
formatLinkCallIndex,
|
||||||
"invoke-static { v${formatLinkCall.registerE} }, $FORMAT_METHOD_RESOURCE_REFERENCE",
|
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.buttons.navigation
|
|||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.literal
|
||||||
|
|
||||||
internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive"
|
internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive"
|
||||||
|
|
||||||
@@ -22,4 +23,31 @@ internal val createPivotBarFingerprint = fingerprint {
|
|||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.RETURN_VOID,
|
Opcode.RETURN_VOID,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const val TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG = 45400535L
|
||||||
|
|
||||||
|
internal val translucentNavigationStatusBarFeatureFlagFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("Z")
|
||||||
|
literal { TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const val TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG = 45630927L
|
||||||
|
|
||||||
|
internal val translucentNavigationButtonsFeatureFlagFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
literal { TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device on screen back/home/recent buttons.
|
||||||
|
*/
|
||||||
|
internal const val TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG = 45632194L
|
||||||
|
|
||||||
|
internal val translucentNavigationButtonsSystemFeatureFlagFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("Z")
|
||||||
|
literal { TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG }
|
||||||
}
|
}
|
||||||
@@ -12,10 +12,13 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated
|
import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated
|
||||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
@@ -32,6 +35,7 @@ val navigationButtonsPatch = bytecodePatch(
|
|||||||
settingsPatch,
|
settingsPatch,
|
||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
navigationBarHookPatch,
|
navigationBarHookPatch,
|
||||||
|
versionCheckPatch
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
@@ -50,19 +54,27 @@ val navigationButtonsPatch = bytecodePatch(
|
|||||||
execute {
|
execute {
|
||||||
addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch")
|
addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch")
|
||||||
|
|
||||||
|
val preferences = mutableSetOf(
|
||||||
|
SwitchPreference("revanced_hide_home_button"),
|
||||||
|
SwitchPreference("revanced_hide_shorts_button"),
|
||||||
|
SwitchPreference("revanced_hide_create_button"),
|
||||||
|
SwitchPreference("revanced_hide_subscriptions_button"),
|
||||||
|
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
||||||
|
SwitchPreference("revanced_hide_navigation_button_labels"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (is_19_25_or_greater) {
|
||||||
|
preferences += SwitchPreference("revanced_disable_translucent_status_bar")
|
||||||
|
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
|
||||||
|
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
|
||||||
|
}
|
||||||
|
|
||||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||||
PreferenceScreenPreference(
|
PreferenceScreenPreference(
|
||||||
key = "revanced_navigation_buttons_screen",
|
key = "revanced_navigation_buttons_screen",
|
||||||
sorting = Sorting.UNSORTED,
|
sorting = Sorting.UNSORTED,
|
||||||
preferences = setOf(
|
preferences = preferences
|
||||||
SwitchPreference("revanced_hide_home_button"),
|
)
|
||||||
SwitchPreference("revanced_hide_shorts_button"),
|
|
||||||
SwitchPreference("revanced_hide_create_button"),
|
|
||||||
SwitchPreference("revanced_hide_subscriptions_button"),
|
|
||||||
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
|
||||||
SwitchPreference("revanced_hide_navigation_button_labels"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Switch create with notifications button.
|
// Switch create with notifications button.
|
||||||
@@ -101,5 +113,24 @@ val navigationButtonsPatch = bytecodePatch(
|
|||||||
|
|
||||||
// Hook navigation button created, in order to hide them.
|
// Hook navigation button created, in order to hide them.
|
||||||
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
|
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
|
||||||
|
|
||||||
|
|
||||||
|
// Force on/off translucent effect on status bar and navigation buttons.
|
||||||
|
if (is_19_25_or_greater) {
|
||||||
|
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
||||||
|
)
|
||||||
|
|
||||||
|
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||||
|
)
|
||||||
|
|
||||||
|
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
PreferenceScreenPreference(
|
PreferenceScreenPreference(
|
||||||
"revanced_comments_screen",
|
"revanced_comments_screen",
|
||||||
preferences = setOf(
|
preferences = setOf(
|
||||||
|
SwitchPreference("revanced_hide_comments_chat_summary"),
|
||||||
SwitchPreference("revanced_hide_comments_by_members_header"),
|
SwitchPreference("revanced_hide_comments_by_members_header"),
|
||||||
SwitchPreference("revanced_hide_comments_section"),
|
SwitchPreference("revanced_hide_comments_section"),
|
||||||
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
||||||
@@ -221,7 +222,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_hide_notify_me_button"),
|
SwitchPreference("revanced_hide_notify_me_button"),
|
||||||
SwitchPreference("revanced_hide_playables"),
|
SwitchPreference("revanced_hide_playables"),
|
||||||
SwitchPreference("revanced_hide_search_result_recommendations"),
|
SwitchPreference("revanced_hide_search_result_recommendations"),
|
||||||
SwitchPreference("revanced_hide_search_result_shelf_header"),
|
|
||||||
SwitchPreference("revanced_hide_show_more_button"),
|
SwitchPreference("revanced_hide_show_more_button"),
|
||||||
SwitchPreference("revanced_hide_doodles"),
|
SwitchPreference("revanced_hide_doodles"),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -240,14 +240,14 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) {
|
fun MutableMethod.insertMiniplayerBooleanOverride(index: Int, methodName: String) {
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
addInstructions(
|
addInstructions(
|
||||||
index,
|
index,
|
||||||
"""
|
"""
|
||||||
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
|
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
|
||||||
move-result v$register
|
move-result v$register
|
||||||
""",
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,29 +257,25 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
* Adds an override to force legacy tablet miniplayer to be used or not used.
|
* Adds an override to force legacy tablet miniplayer to be used or not used.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) {
|
fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) {
|
||||||
insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride")
|
insertMiniplayerBooleanOverride(index, "getLegacyTabletMiniplayerOverride")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an override to force modern miniplayer to be used or not used.
|
* Adds an override to force modern miniplayer to be used or not used.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.insertModernMiniplayerOverride(index: Int) {
|
fun MutableMethod.insertModernMiniplayerOverride(index: Int) {
|
||||||
insertBooleanOverride(index, "getModernMiniplayerOverride")
|
insertMiniplayerBooleanOverride(index, "getModernMiniplayerOverride")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Fingerprint.insertLiteralValueBooleanOverride(
|
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
literal: Long,
|
literal: Long,
|
||||||
extensionMethod: String,
|
extensionMethod: String,
|
||||||
) {
|
) = method.insertFeatureFlagBooleanOverride(
|
||||||
method.apply {
|
literal,
|
||||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
|
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z"
|
||||||
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
)
|
||||||
|
|
||||||
insertBooleanOverride(targetIndex + 1, extensionMethod)
|
fun Fingerprint.insertMiniplayerFeatureFlagFloatOverride(
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Fingerprint.insertLiteralValueFloatOverride(
|
|
||||||
literal: Long,
|
literal: Long,
|
||||||
extensionMethod: String,
|
extensionMethod: String,
|
||||||
) {
|
) {
|
||||||
@@ -370,24 +366,24 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_19_23_or_greater) {
|
if (is_19_23_or_greater) {
|
||||||
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
||||||
"enableMiniplayerDragAndDrop",
|
"enableMiniplayerDragAndDrop",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_19_25_or_greater) {
|
if (is_19_25_or_greater) {
|
||||||
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
|
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
|
||||||
"getModernMiniplayerOverride",
|
"getModernMiniplayerOverride",
|
||||||
)
|
)
|
||||||
|
|
||||||
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_MODERN_FEATURE_KEY,
|
MINIPLAYER_MODERN_FEATURE_KEY,
|
||||||
"getModernFeatureFlagsActiveOverride",
|
"getModernFeatureFlagsActiveOverride",
|
||||||
)
|
)
|
||||||
|
|
||||||
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
|
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
|
||||||
"enableMiniplayerDoubleTapAction",
|
"enableMiniplayerDoubleTapAction",
|
||||||
)
|
)
|
||||||
@@ -426,19 +422,19 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_19_36_or_greater) {
|
if (is_19_36_or_greater) {
|
||||||
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
|
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
|
||||||
"setRoundedCorners",
|
"setRoundedCorners",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_19_43_or_greater) {
|
if (is_19_43_or_greater) {
|
||||||
miniplayerOnCloseHandlerFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerOnCloseHandlerFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_DISABLED_FEATURE_KEY,
|
MINIPLAYER_DISABLED_FEATURE_KEY,
|
||||||
"getMiniplayerOnCloseHandler"
|
"getMiniplayerOnCloseHandler"
|
||||||
)
|
)
|
||||||
|
|
||||||
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
|
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
|
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
|
||||||
"setHorizontalDrag",
|
"setHorizontalDrag",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.literal
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal const val OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG = 45666112L
|
||||||
|
|
||||||
|
internal val openVideosFullscreenPortraitFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters("L", "Lj\$/util/Optional;")
|
||||||
|
literal {
|
||||||
|
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to enable opening regular videos fullscreen.
|
||||||
|
*/
|
||||||
|
internal val openVideosFullscreenHookPatchExtensionFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||||
|
returns("Z")
|
||||||
|
parameters()
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
methodDef.name == "isFullScreenPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@Deprecated("Renamed to openVideosFullscreenPatch", ReplaceWith("openVideosFullscreenPatch"))
|
||||||
|
val openVideosFullscreen = bytecodePatch{
|
||||||
|
dependsOn(openVideosFullscreenPatch)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlayerPatch
|
||||||
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
|
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||||
|
|
||||||
|
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by both [openVideosFullscreenPatch] and [openShortsInRegularPlayerPatch].
|
||||||
|
*/
|
||||||
|
internal val openVideosFullscreenHookPatch = bytecodePatch {
|
||||||
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
versionCheckPatch
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
if (!is_19_46_or_greater) {
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
|
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val openVideosFullscreenPatch = bytecodePatch(
|
||||||
|
name = "Open videos fullscreen",
|
||||||
|
description = "Adds an option to open videos in full screen portrait mode.",
|
||||||
|
) {
|
||||||
|
dependsOn(
|
||||||
|
openVideosFullscreenHookPatch,
|
||||||
|
settingsPatch,
|
||||||
|
addResourcesPatch,
|
||||||
|
versionCheckPatch
|
||||||
|
)
|
||||||
|
|
||||||
|
compatibleWith(
|
||||||
|
"com.google.android.youtube"(
|
||||||
|
"19.46.42",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
if (!is_19_46_or_greater) {
|
||||||
|
throw PatchException("'Open videos fullscreen' requires 19.46.42 or greater")
|
||||||
|
}
|
||||||
|
|
||||||
|
addResources("youtube", "layout.player.fullscreen.openVideosFullscreen")
|
||||||
|
|
||||||
|
PreferenceScreen.PLAYER.addPreferences(
|
||||||
|
SwitchPreference("revanced_open_videos_fullscreen_portrait")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable the logic for the user Setting to open regular videos fullscreen.
|
||||||
|
openVideosFullscreenHookPatchExtensionFingerprint.method.returnEarly(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import app.revanced.util.getReference
|
|||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||||
import app.revanced.util.inputStreamFromBundledResource
|
import app.revanced.util.inputStreamFromBundledResource
|
||||||
|
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
@@ -228,19 +229,10 @@ val seekbarColorPatch = bytecodePatch(
|
|||||||
|
|
||||||
// 19.25+ changes
|
// 19.25+ changes
|
||||||
|
|
||||||
playerSeekbarGradientConfigFingerprint.method.apply {
|
playerSeekbarGradientConfigFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG)
|
PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG,
|
||||||
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
"$EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z"
|
||||||
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
|
)
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
resultIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z
|
|
||||||
move-result v$register
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
lithoLinearGradientFingerprint.method.addInstruction(
|
lithoLinearGradientFingerprint.method.addInstruction(
|
||||||
0,
|
0,
|
||||||
@@ -255,19 +247,10 @@ val seekbarColorPatch = bytecodePatch(
|
|||||||
launchScreenLayoutTypeFingerprint,
|
launchScreenLayoutTypeFingerprint,
|
||||||
mainActivityOnCreateFingerprint
|
mainActivityOnCreateFingerprint
|
||||||
).forEach { fingerprint ->
|
).forEach { fingerprint ->
|
||||||
fingerprint.method.apply {
|
fingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(launchScreenLayoutTypeLotteFeatureFlag)
|
launchScreenLayoutTypeLotteFeatureFlag,
|
||||||
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
"$EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z"
|
||||||
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
|
)
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
resultIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z
|
|
||||||
move-result v$register
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hook the splash animation drawable to set the a seekbar color theme.
|
// Hook the splash animation drawable to set the a seekbar color theme.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.revanced.patches.youtube.layout.shortsautoplay
|
package app.revanced.patches.youtube.layout.shortsautoplay
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
@@ -56,7 +57,7 @@ val shortsAutoplayPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main activity is used to check if app is in pip mode.
|
// Main activity is used to check if app is in pip mode.
|
||||||
mainActivityOnCreateFingerprint.method.addInstructions(
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
1,
|
1,
|
||||||
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||||
"setMainActivity(Landroid/app/Activity;)V",
|
"setMainActivity(Landroid/app/Activity;)V",
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.shortsplayer
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.literal
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose of this method is not clear, and it's only used to identify
|
||||||
|
* the obfuscated name of the videoId() method in PlaybackStartDescriptor.
|
||||||
|
*/
|
||||||
|
internal val playbackStartFeatureFlagFingerprint = fingerprint {
|
||||||
|
returns("Z")
|
||||||
|
parameters(
|
||||||
|
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
|
||||||
|
)
|
||||||
|
literal {
|
||||||
|
45380134L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre 19.25
|
||||||
|
internal val shortsPlaybackIntentLegacyFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters(
|
||||||
|
"L",
|
||||||
|
"Ljava/util/Map;",
|
||||||
|
"J",
|
||||||
|
"Ljava/lang/String;",
|
||||||
|
"Z",
|
||||||
|
"Ljava/util/Map;"
|
||||||
|
)
|
||||||
|
strings(
|
||||||
|
// None of these strings are unique.
|
||||||
|
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||||
|
"ReelWatchFragmentArgs",
|
||||||
|
"reels_fragment_descriptor"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val shortsPlaybackIntentFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters(
|
||||||
|
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
|
||||||
|
"Ljava/util/Map;",
|
||||||
|
"J",
|
||||||
|
"Ljava/lang/String;"
|
||||||
|
)
|
||||||
|
strings(
|
||||||
|
// None of these strings are unique.
|
||||||
|
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||||
|
"ReelWatchFragmentArgs",
|
||||||
|
"reels_fragment_descriptor"
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.shortsplayer
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
|
import app.revanced.patches.youtube.layout.player.fullscreen.openVideosFullscreenHookPatch
|
||||||
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch;"
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val openShortsInRegularPlayerPatch = bytecodePatch(
|
||||||
|
name = "Open Shorts in regular player",
|
||||||
|
description = "Adds options to open Shorts in the regular video player.",
|
||||||
|
) {
|
||||||
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
settingsPatch,
|
||||||
|
addResourcesPatch,
|
||||||
|
openVideosFullscreenHookPatch,
|
||||||
|
navigationBarHookPatch,
|
||||||
|
versionCheckPatch
|
||||||
|
)
|
||||||
|
|
||||||
|
compatibleWith(
|
||||||
|
"com.google.android.youtube"(
|
||||||
|
"18.38.44",
|
||||||
|
"18.49.37",
|
||||||
|
"19.16.39",
|
||||||
|
"19.25.37",
|
||||||
|
"19.34.42",
|
||||||
|
"19.43.41",
|
||||||
|
"19.45.38",
|
||||||
|
"19.46.42",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
addResources("youtube", "layout.shortsplayer.shortsPlayerTypePatch")
|
||||||
|
|
||||||
|
PreferenceScreen.SHORTS.addPreferences(
|
||||||
|
if (is_19_46_or_greater) {
|
||||||
|
ListPreference(
|
||||||
|
key = "revanced_shorts_player_type",
|
||||||
|
summaryKey = null,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ListPreference(
|
||||||
|
key = "revanced_shorts_player_type",
|
||||||
|
summaryKey = null,
|
||||||
|
entriesKey = "revanced_shorts_player_type_legacy_entries",
|
||||||
|
entryValuesKey = "revanced_shorts_player_type_legacy_entry_values"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Activity is used as the context to launch an Intent.
|
||||||
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
|
1,
|
||||||
|
"invoke-static/range { p0 .. p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
|
||||||
|
"setMainActivity(Landroid/app/Activity;)V",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find the obfuscated method name for PlaybackStartDescriptor.videoId()
|
||||||
|
val playbackStartVideoIdMethodName = playbackStartFeatureFlagFingerprint.method.let {
|
||||||
|
val stringMethodIndex = it.indexOfFirstInstructionOrThrow {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
|
||||||
|
&& reference.returnType == "Ljava/lang/String;"
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate(it).to(stringMethodIndex).stop().name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extensionInstructions(playbackStartRegister: Int, freeRegister: Int) =
|
||||||
|
"""
|
||||||
|
invoke-virtual { v$playbackStartRegister }, Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;->$playbackStartVideoIdMethodName()Ljava/lang/String;
|
||||||
|
move-result-object v$freeRegister
|
||||||
|
invoke-static { v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->openShort(Ljava/lang/String;)Z
|
||||||
|
move-result v$freeRegister
|
||||||
|
if-eqz v$freeRegister, :disabled
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:disabled
|
||||||
|
nop
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (!is_19_25_or_greater) {
|
||||||
|
shortsPlaybackIntentLegacyFingerprint.method.apply {
|
||||||
|
val index = indexOfFirstInstructionOrThrow {
|
||||||
|
getReference<MethodReference>()?.returnType ==
|
||||||
|
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
|
||||||
|
}
|
||||||
|
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
|
||||||
|
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||||
|
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
index + 2,
|
||||||
|
extensionInstructions(playbackStartRegister, freeRegister)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
|
shortsPlaybackIntentFingerprint.method.addInstructionsWithLabels(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
move-object/from16 v0, p1
|
||||||
|
${extensionInstructions(0, 1)}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package app.revanced.patches.youtube.layout.theme
|
package app.revanced.patches.youtube.layout.theme
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
@@ -17,10 +16,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.util.forEachChildElement
|
import app.revanced.util.forEachChildElement
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
@@ -212,19 +208,10 @@ val themePatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_gradient_loading_screen"),
|
SwitchPreference("revanced_gradient_loading_screen"),
|
||||||
)
|
)
|
||||||
|
|
||||||
useGradientLoadingScreenFingerprint.method.apply {
|
useGradientLoadingScreenFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT)
|
GRADIENT_LOADING_SCREEN_AB_CONSTANT,
|
||||||
val isEnabledIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
"$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z"
|
||||||
val isEnabledRegister = getInstruction<OneRegisterInstruction>(isEnabledIndex).registerA
|
)
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
isEnabledIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled()Z
|
|
||||||
move-result v$isEnabledRegister
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
mapOf(
|
mapOf(
|
||||||
themeHelperLightColorFingerprint to lightThemeBackgroundColor,
|
themeHelperLightColorFingerprint to lightThemeBackgroundColor,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package app.revanced.patches.youtube.misc.announcements
|
package app.revanced.patches.youtube.misc.announcements
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
@@ -41,7 +41,7 @@ val announcementsPatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_announcements"),
|
SwitchPreference("revanced_announcements"),
|
||||||
)
|
)
|
||||||
|
|
||||||
mainActivityOnCreateFingerprint.method.addInstructions(
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
// Insert index must be greater than the insert index used by GmsCoreSupport,
|
// Insert index must be greater than the insert index used by GmsCoreSupport,
|
||||||
// as both patch the same method and GmsCore check should be first.
|
// as both patch the same method and GmsCore check should be first.
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -110,22 +110,22 @@ val enableDebuggingPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
experimentalStringFeatureFlagFingerprint.match(
|
experimentalStringFeatureFlagFingerprint.match(
|
||||||
experimentalFeatureFlagParentFingerprint.originalClassDef
|
experimentalFeatureFlagParentFingerprint.originalClassDef
|
||||||
).method.apply {
|
).method.apply {
|
||||||
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
|
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex,
|
insertIndex,
|
||||||
"""
|
"""
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
|
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
return-object v0
|
return-object v0
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// There exists other experimental accessor methods for byte[]
|
// There exists other experimental accessor methods for byte[]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package app.revanced.patches.youtube.misc.dns
|
package app.revanced.patches.youtube.misc.dns
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
@@ -31,7 +31,7 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
|
|||||||
execute {
|
execute {
|
||||||
addResources("youtube", "misc.dns.checkWatchHistoryDomainNameResolutionPatch")
|
addResources("youtube", "misc.dns.checkWatchHistoryDomainNameResolutionPatch")
|
||||||
|
|
||||||
mainActivityOnCreateFingerprint.method.addInstructions(
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
// FIXME: Insert index must be greater than the insert index used by GmsCoreSupport,
|
// FIXME: Insert index must be greater than the insert index used by GmsCoreSupport,
|
||||||
// as both patch the same method and GmsCoreSupport check should be first,
|
// as both patch the same method and GmsCoreSupport check should be first,
|
||||||
// but the patch does not depend on GmsCoreSupport, so it should not be possible to enforce this
|
// but the patch does not depend on GmsCoreSupport, so it should not be possible to enforce this
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ internal val disableCairoSettingsPatch = bytecodePatch(
|
|||||||
* <a href="https://github.com/qnblackcat/uYouPlus/issues/1468">uYouPlus#1468</a>.
|
* <a href="https://github.com/qnblackcat/uYouPlus/issues/1468">uYouPlus#1468</a>.
|
||||||
*/
|
*/
|
||||||
cairoFragmentConfigFingerprint.method.apply {
|
cairoFragmentConfigFingerprint.method.apply {
|
||||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
|
val literalIndex = indexOfFirstLiteralInstructionOrThrow(CAIRO_CONFIG_LITERAL_VALUE)
|
||||||
CAIRO_CONFIG_LITERAL_VALUE,
|
|
||||||
)
|
|
||||||
|
|
||||||
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
||||||
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
|
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
|
||||||
|
|
||||||
|
|||||||
@@ -1,243 +1,9 @@
|
|||||||
package app.revanced.patches.youtube.misc.fix.playback
|
package app.revanced.patches.youtube.misc.fix.playback
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
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.instructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
@Deprecated("Use app.revanced.patches.youtube.misc.spoof.spoofVideoStreamsPatch instead.")
|
||||||
"Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;"
|
@Suppress("unused")
|
||||||
|
val spoofVideoStreamsPatch = bytecodePatch {
|
||||||
val spoofVideoStreamsPatch = bytecodePatch(
|
dependsOn(app.revanced.patches.youtube.misc.spoof.spoofVideoStreamsPatch)
|
||||||
name = "Spoof video streams",
|
|
||||||
description = "Spoofs the client video streams to allow video playback.",
|
|
||||||
) {
|
|
||||||
compatibleWith(
|
|
||||||
"com.google.android.youtube"(
|
|
||||||
"18.38.44",
|
|
||||||
"18.49.37",
|
|
||||||
"19.16.39",
|
|
||||||
"19.25.37",
|
|
||||||
"19.34.42",
|
|
||||||
"19.43.41",
|
|
||||||
"19.45.38",
|
|
||||||
"19.46.42",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
dependsOn(
|
|
||||||
settingsPatch,
|
|
||||||
addResourcesPatch,
|
|
||||||
userAgentClientSpoofPatch,
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")
|
|
||||||
|
|
||||||
PreferenceScreen.MISC.addPreferences(
|
|
||||||
PreferenceScreenPreference(
|
|
||||||
key = "revanced_spoof_video_streams_screen",
|
|
||||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
|
||||||
preferences = setOf(
|
|
||||||
SwitchPreference("revanced_spoof_video_streams"),
|
|
||||||
ListPreference(
|
|
||||||
"revanced_spoof_video_streams_client",
|
|
||||||
summaryKey = null,
|
|
||||||
),
|
|
||||||
SwitchPreference(
|
|
||||||
"revanced_spoof_video_streams_ios_force_avc",
|
|
||||||
tag = "app.revanced.extension.youtube.settings.preference.ForceAVCSpoofingPreference",
|
|
||||||
),
|
|
||||||
NonInteractivePreference("revanced_spoof_video_streams_about_android_vr"),
|
|
||||||
NonInteractivePreference("revanced_spoof_video_streams_about_ios"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
// region Block /initplayback requests to fall back to /get_watch requests.
|
|
||||||
|
|
||||||
val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex
|
|
||||||
|
|
||||||
buildInitPlaybackRequestFingerprint.method.apply {
|
|
||||||
val targetRegister = getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
moveUriStringIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
|
|
||||||
move-result-object v$targetRegister
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Block /get_watch requests to fall back to /player requests.
|
|
||||||
|
|
||||||
val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex
|
|
||||||
|
|
||||||
buildPlayerRequestURIFingerprint.method.apply {
|
|
||||||
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
invokeToStringIndex,
|
|
||||||
"""
|
|
||||||
invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
|
||||||
move-result-object v$uriRegister
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Get replacement streams at player requests.
|
|
||||||
|
|
||||||
buildRequestFingerprint.method.apply {
|
|
||||||
val newRequestBuilderIndex = indexOfFirstInstructionOrThrow {
|
|
||||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
|
||||||
getReference<MethodReference>()?.name == "newUrlRequestBuilder"
|
|
||||||
}
|
|
||||||
val urlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
|
|
||||||
val freeRegister = getInstruction<OneRegisterInstruction>(newRequestBuilderIndex + 1).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
newRequestBuilderIndex,
|
|
||||||
"""
|
|
||||||
move-object v$freeRegister, p1
|
|
||||||
invoke-static { v$urlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Replace the streaming data with the replacement streams.
|
|
||||||
|
|
||||||
createStreamingDataFingerprint.method.apply {
|
|
||||||
val setStreamDataMethodName = "patch_setStreamingData"
|
|
||||||
val resultMethodType = createStreamingDataFingerprint.classDef.type
|
|
||||||
val videoDetailsIndex = createStreamingDataFingerprint.patternMatch!!.endIndex
|
|
||||||
val videoDetailsRegister = getInstruction<TwoRegisterInstruction>(videoDetailsIndex).registerA
|
|
||||||
val videoDetailsClass = getInstruction(videoDetailsIndex).getReference<FieldReference>()!!.type
|
|
||||||
|
|
||||||
addInstruction(
|
|
||||||
videoDetailsIndex + 1,
|
|
||||||
"invoke-direct { p0, v$videoDetailsRegister }, " +
|
|
||||||
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
|
||||||
)
|
|
||||||
|
|
||||||
val protobufClass = protobufClassParseByteBufferFingerprint.method.definingClass
|
|
||||||
val setStreamingDataIndex = createStreamingDataFingerprint.patternMatch!!.startIndex
|
|
||||||
|
|
||||||
val playerProtoClass = getInstruction(setStreamingDataIndex + 1)
|
|
||||||
.getReference<FieldReference>()!!.definingClass
|
|
||||||
|
|
||||||
val setStreamingDataField = getInstruction(setStreamingDataIndex).getReference<FieldReference>()
|
|
||||||
|
|
||||||
val getStreamingDataField = getInstruction(
|
|
||||||
indexOfFirstInstructionOrThrow {
|
|
||||||
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.definingClass == playerProtoClass
|
|
||||||
},
|
|
||||||
).getReference<FieldReference>()
|
|
||||||
|
|
||||||
// Use a helper method to avoid the need of picking out multiple free registers from the hooked code.
|
|
||||||
createStreamingDataFingerprint.classDef.methods.add(
|
|
||||||
ImmutableMethod(
|
|
||||||
resultMethodType,
|
|
||||||
setStreamDataMethodName,
|
|
||||||
listOf(ImmutableMethodParameter(videoDetailsClass, null, "videoDetails")),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PRIVATE.value or AccessFlags.FINAL.value,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
MutableMethodImplementation(9),
|
|
||||||
).toMutable().apply {
|
|
||||||
addInstructionsWithLabels(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled()Z
|
|
||||||
move-result v0
|
|
||||||
if-eqz v0, :disabled
|
|
||||||
|
|
||||||
# Get video id.
|
|
||||||
iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String;
|
|
||||||
if-eqz v2, :disabled
|
|
||||||
|
|
||||||
# Get streaming data.
|
|
||||||
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
|
|
||||||
move-result-object v3
|
|
||||||
if-eqz v3, :disabled
|
|
||||||
|
|
||||||
# Parse streaming data.
|
|
||||||
sget-object v4, $playerProtoClass->a:$playerProtoClass
|
|
||||||
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
|
|
||||||
move-result-object v5
|
|
||||||
check-cast v5, $playerProtoClass
|
|
||||||
|
|
||||||
# Set streaming data.
|
|
||||||
iget-object v6, v5, $getStreamingDataField
|
|
||||||
if-eqz v6, :disabled
|
|
||||||
iput-object v6, p0, $setStreamingDataField
|
|
||||||
|
|
||||||
:disabled
|
|
||||||
return-void
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Remove /videoplayback request body to fix playback.
|
|
||||||
// It is assumed, YouTube makes a request with a body tuned for Android.
|
|
||||||
// Requesting streams intended for other platforms with a body tuned for Android could be the cause of 400 errors.
|
|
||||||
// A proper fix may include modifying the request body to match the platforms expected body.
|
|
||||||
|
|
||||||
buildMediaDataSourceFingerprint.method.apply {
|
|
||||||
val targetIndex = instructions.lastIndex
|
|
||||||
|
|
||||||
// Instructions are added just before the method returns,
|
|
||||||
// so there's no concern of clobbering in-use registers.
|
|
||||||
addInstructions(
|
|
||||||
targetIndex,
|
|
||||||
"""
|
|
||||||
# Field a: Stream uri.
|
|
||||||
# Field c: Http method.
|
|
||||||
# Field d: Post data.
|
|
||||||
move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register.
|
|
||||||
iget-object v1, v0, $definingClass->a:Landroid/net/Uri;
|
|
||||||
iget v2, v0, $definingClass->c:I
|
|
||||||
iget-object v3, v0, $definingClass->d:[B
|
|
||||||
invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B
|
|
||||||
move-result-object v1
|
|
||||||
iput-object v1, v0, $definingClass->d:[B
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +1,9 @@
|
|||||||
package app.revanced.patches.youtube.misc.fix.playback
|
package app.revanced.patches.youtube.misc.fix.playback
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patches.all.misc.transformation.IMethodCall
|
|
||||||
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
|
||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.indexOfFirstInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
|
||||||
|
|
||||||
private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube"
|
|
||||||
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
|
|
||||||
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
|
|
||||||
|
|
||||||
val userAgentClientSpoofPatch = transformInstructionsPatch(
|
|
||||||
filterMap = { classDef, _, instruction, instructionIndex ->
|
|
||||||
filterMapInstruction35c<MethodCall>(
|
|
||||||
"Lapp/revanced/extension",
|
|
||||||
classDef,
|
|
||||||
instruction,
|
|
||||||
instructionIndex,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
transform = transform@{ mutableMethod, entry ->
|
|
||||||
val (_, _, instructionIndex) = entry
|
|
||||||
|
|
||||||
// Replace the result of context.getPackageName(), if it is used in a user agent string.
|
|
||||||
mutableMethod.apply {
|
|
||||||
// After context.getPackageName() the result is moved to a register.
|
|
||||||
val targetRegister = (
|
|
||||||
getInstruction(instructionIndex + 1)
|
|
||||||
as? OneRegisterInstruction ?: return@transform
|
|
||||||
).registerA
|
|
||||||
|
|
||||||
// IndexOutOfBoundsException is technically possible here,
|
|
||||||
// but no such occurrences are present in the app.
|
|
||||||
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
|
|
||||||
|
|
||||||
// Only replace string builder usage.
|
|
||||||
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
|
|
||||||
return@transform
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
|
|
||||||
// Changing these package names will result in playback limitations,
|
|
||||||
// particularly Android VR background audio only playback.
|
|
||||||
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
|
|
||||||
val reference = getReference<StringReference>()
|
|
||||||
opcode == Opcode.CONST_STRING &&
|
|
||||||
(reference?.string == "android.resource://" || reference?.string == "gcore_")
|
|
||||||
}
|
|
||||||
if (resourceOrGmsStringInstructionIndex >= 0) {
|
|
||||||
return@transform
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite the result of context.getPackageName() with the original package name.
|
|
||||||
replaceInstruction(
|
|
||||||
instructionIndex + 1,
|
|
||||||
"const-string v$targetRegister, \"$ORIGINAL_PACKAGE_NAME\"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
@Deprecated("Use app.revanced.patches.youtube.misc.spoof.userAgentClientSpoofPatch instead.")
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
private enum class MethodCall(
|
val userAgentClientSpoofPatch = bytecodePatch {
|
||||||
override val definedClassName: String,
|
dependsOn(app.revanced.patches.youtube.misc.spoof.userAgentClientSpoofPatch)
|
||||||
override val methodName: String,
|
|
||||||
override val methodParams: Array<String>,
|
|
||||||
override val returnType: String,
|
|
||||||
) : IMethodCall {
|
|
||||||
GetPackageName(
|
|
||||||
"Landroid/content/Context;",
|
|
||||||
"getPackageName",
|
|
||||||
emptyArray(),
|
|
||||||
"Ljava/lang/String;",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
|||||||
import app.revanced.patches.shared.primeMethodFingerprint
|
import app.revanced.patches.shared.primeMethodFingerprint
|
||||||
import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch
|
import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.spoofVideoStreamsPatch
|
|
||||||
import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME
|
import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME
|
||||||
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
|
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.spoof.spoofVideoStreamsPatch
|
||||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
|
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
|
||||||
|
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
||||||
|
compatibleWith(
|
||||||
|
"com.google.android.youtube"(
|
||||||
|
"18.38.44",
|
||||||
|
"18.49.37",
|
||||||
|
"19.16.39",
|
||||||
|
"19.25.37",
|
||||||
|
"19.34.42",
|
||||||
|
"19.43.41",
|
||||||
|
"19.45.38",
|
||||||
|
"19.46.42",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
dependsOn(
|
||||||
|
userAgentClientSpoofPatch,
|
||||||
|
settingsPatch,
|
||||||
|
)
|
||||||
|
}, {
|
||||||
|
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")
|
||||||
|
|
||||||
|
PreferenceScreen.MISC.addPreferences(
|
||||||
|
PreferenceScreenPreference(
|
||||||
|
key = "revanced_spoof_video_streams_screen",
|
||||||
|
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||||
|
preferences = setOf(
|
||||||
|
SwitchPreference("revanced_spoof_video_streams"),
|
||||||
|
ListPreference(
|
||||||
|
"revanced_spoof_video_streams_client",
|
||||||
|
summaryKey = null,
|
||||||
|
),
|
||||||
|
ListPreference(
|
||||||
|
"revanced_spoof_video_streams_language",
|
||||||
|
summaryKey = null
|
||||||
|
),
|
||||||
|
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
|
||||||
|
// Preference requires a title but the actual text is chosen at runtime.
|
||||||
|
NonInteractivePreference(
|
||||||
|
key = "revanced_spoof_video_streams_about_android_vr",
|
||||||
|
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
|
||||||
|
|
||||||
|
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.youtube")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user