Compare commits

...

73 Commits

Author SHA1 Message Date
semantic-release-bot
217d3c69ba chore(release): 4.7.0-dev.13 [skip ci]
# [4.7.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.12...v4.7.0-dev.13) (2024-04-18)

### Bug Fixes

* **YouTube Music - Remove upgrade button:** Fix compatibility with latest versions ([#3045](https://github.com/ReVanced/revanced-patches/issues/3045)) ([e329e1d](e329e1dd1a))
2024-04-18 23:18:22 +00:00
Alberto Ponces
e329e1dd1a fix(YouTube Music - Remove upgrade button): Fix compatibility with latest versions (#3045) 2024-04-19 01:16:25 +02:00
semantic-release-bot
6a8b669ba2 chore(release): 4.7.0-dev.12 [skip ci]
# [4.7.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.11...v4.7.0-dev.12) (2024-04-18)

### Features

* Add `Hex` patch ([#3034](https://github.com/ReVanced/revanced-patches/issues/3034)) ([6fd46ad](6fd46ad9b6))
2024-04-18 22:50:02 +00:00
oSumAtrIX
6fd46ad9b6 feat: Add Hex patch (#3034) 2024-04-18 21:37:46 +02:00
semantic-release-bot
50ddb680ed chore(release): 4.7.0-dev.11 [skip ci]
# [4.7.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.10...v4.7.0-dev.11) (2024-04-18)

### Bug Fixes

* **YouTube - Spoof device dimensions:** Warn about potential performance issues ([#3039](https://github.com/ReVanced/revanced-patches/issues/3039)) ([cc7f79d](cc7f79d903))
2024-04-18 13:21:16 +00:00
LisoUseInAIKyrios
cc7f79d903 fix(YouTube - Spoof device dimensions): Warn about potential performance issues (#3039) 2024-04-18 17:19:04 +04:00
LisoUseInAIKyrios
57274b5435 chore: Fix typo 2024-04-18 12:41:48 +04:00
semantic-release-bot
8295b7a22e chore(release): 4.7.0-dev.10 [skip ci]
# [4.7.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.9...v4.7.0-dev.10) (2024-04-17)

### Features

* **YouTube:** Add 'About' preference to settings menu ([#2981](https://github.com/ReVanced/revanced-patches/issues/2981)) ([c272333](c272333adb))
* **YouTube:** Match overlay icons style to YouTube ([#3023](https://github.com/ReVanced/revanced-patches/issues/3023)) ([366f754](366f754a6a))
2024-04-17 16:04:01 +00:00
MarcaD
366f754a6a feat(YouTube): Match overlay icons style to YouTube (#3023) 2024-04-17 18:01:47 +02:00
LisoUseInAIKyrios
c272333adb feat(YouTube): Add 'About' preference to settings menu (#2981)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-04-17 20:01:17 +04:00
semantic-release-bot
ef6c00a520 chore(release): 4.7.0-dev.9 [skip ci]
# [4.7.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.8...v4.7.0-dev.9) (2024-04-15)

### Bug Fixes

* **YouTube - Hide ads:** rename `Hide paid content` to `Hide paid promotion label` ([#3026](https://github.com/ReVanced/revanced-patches/issues/3026)) ([58ce37b](58ce37b0d3))
2024-04-15 15:21:08 +00:00
LisoUseInAIKyrios
58ce37b0d3 fix(YouTube - Hide ads): rename Hide paid content to Hide paid promotion label (#3026) 2024-04-15 19:18:57 +04:00
semantic-release-bot
4b7e12f995 chore(release): 4.7.0-dev.8 [skip ci]
# [4.7.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.7...v4.7.0-dev.8) (2024-04-14)

### Features

* **YouTube - Hide Shorts components:** Hide tagged products, hide search suggestions ([#3019](https://github.com/ReVanced/revanced-patches/issues/3019)) ([2e59965](2e59965bb5))
2024-04-14 14:56:15 +00:00
LisoUseInAIKyrios
2e59965bb5 feat(YouTube - Hide Shorts components): Hide tagged products, hide search suggestions (#3019) 2024-04-14 18:54:00 +04:00
semantic-release-bot
426f405407 chore(release): 4.7.0-dev.7 [skip ci]
# [4.7.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.6...v4.7.0-dev.7) (2024-04-14)

### Features

* **YouTube - Hide layout components:** Hide playables ([f85f518](f85f518fc9))
2024-04-14 00:09:07 +00:00
oSumAtrIX
f85f518fc9 feat(YouTube - Hide layout components): Hide playables 2024-04-14 02:06:41 +02:00
LisoUseInAIKyrios
f3972d313f chore: fix typo 2024-04-12 23:52:21 +04:00
semantic-release-bot
04a72d52de chore(release): 4.7.0-dev.6 [skip ci]
# [4.7.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.5...v4.7.0-dev.6) (2024-04-12)

### Features

* **YouTube - Hide Shorts components:** Hide `Shop`, `Location` and `Save sound to playlist` buttons ([#3018](https://github.com/ReVanced/revanced-patches/issues/3018)) ([be76e49](be76e49a8b))
2024-04-12 19:46:09 +00:00
LisoUseInAIKyrios
be76e49a8b feat(YouTube - Hide Shorts components): Hide Shop, Location and Save sound to playlist buttons (#3018) 2024-04-12 23:44:00 +04:00
semantic-release-bot
fa1c616014 chore(release): 4.7.0-dev.5 [skip ci]
# [4.7.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.4...v4.7.0-dev.5) (2024-04-10)

### Bug Fixes

* **YouTube - Hide Shorts components:** Correctly hide Shorts if navigation tab is changed using device back button ([#3007](https://github.com/ReVanced/revanced-patches/issues/3007)) ([e97aaf4](e97aaf4aae))
2024-04-10 08:31:43 +00:00
LisoUseInAIKyrios
e97aaf4aae fix(YouTube - Hide Shorts components): Correctly hide Shorts if navigation tab is changed using device back button (#3007) 2024-04-10 12:29:39 +04:00
semantic-release-bot
92c90ec208 chore(release): 4.7.0-dev.4 [skip ci]
# [4.7.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.3...v4.7.0-dev.4) (2024-04-09)

### Features

* **Amazon:** Add `Always allow deep-linking` patch ([#3000](https://github.com/ReVanced/revanced-patches/issues/3000)) ([f2fe0d5](f2fe0d5c6f))
* **Twitter:** Add `Sanitize sharing links` patch ([#3003](https://github.com/ReVanced/revanced-patches/issues/3003)) ([b4e8beb](b4e8beb8ec))
2024-04-09 17:45:11 +00:00
Twin
b4e8beb8ec feat(Twitter): Add Sanitize sharing links patch (#3003) 2024-04-09 19:42:51 +02:00
1fexd
f2fe0d5c6f feat(Amazon): Add Always allow deep-linking patch (#3000) 2024-04-09 19:41:45 +02:00
semantic-release-bot
3d3197701c chore(release): 4.7.0-dev.3 [skip ci]
# [4.7.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.2...v4.7.0-dev.3) (2024-04-09)

### Bug Fixes

* **Tumblr - Fix old versions:** Improve reliability by removing remnances of Tumblr Live  ([#2988](https://github.com/ReVanced/revanced-patches/issues/2988)) ([1ac6127](1ac612798b))
2024-04-09 17:40:30 +00:00
Temm
1ac612798b fix(Tumblr - Fix old versions): Improve reliability by removing remnances of Tumblr Live (#2988)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-04-09 19:38:11 +02:00
LisoUseInAIKyrios
e1ed1aee69 chore(YouTube): Fix typos, simplify some strings for translating 2024-04-07 23:56:09 +04:00
oSumAtrIX
795ccac990 chore: Fix spelling mistake 2024-04-07 17:36:01 +02:00
semantic-release-bot
da55410a2b chore(release): 4.7.0-dev.2 [skip ci]
# [4.7.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.1...v4.7.0-dev.2) (2024-04-06)

### Features

* **Strava - Unlock subscription:** Remove compatible version constraint ([e798341](e7983411a7))
2024-04-06 18:53:36 +00:00
oSumAtrIX
e7983411a7 feat(Strava - Unlock subscription): Remove compatible version constraint 2024-04-06 20:51:37 +02:00
semantic-release-bot
182748cb16 chore(release): 4.7.0-dev.1 [skip ci]
# [4.7.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.6.1-dev.3...v4.7.0-dev.1) (2024-04-06)

### Features

* **YouTube - Hide layout components:** Add option to hide horizontal shelves ([#2951](https://github.com/ReVanced/revanced-patches/issues/2951)) ([122f042](122f04219f))
2024-04-06 18:51:17 +00:00
nullptr
122f04219f feat(YouTube - Hide layout components): Add option to hide horizontal shelves (#2951)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2024-04-06 20:49:03 +02:00
semantic-release-bot
80763bcef6 chore(release): 4.6.1-dev.3 [skip ci]
## [4.6.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.6.1-dev.2...v4.6.1-dev.3) (2024-04-06)

### Bug Fixes

* **YouTube - GmsCore support:** Prompt to disable battery optimizations, if not done already ([#2958](https://github.com/ReVanced/revanced-patches/issues/2958)) ([b80bb89](b80bb8969c))
2024-04-06 18:48:03 +00:00
LisoUseInAIKyrios
b80bb8969c fix(YouTube - GmsCore support): Prompt to disable battery optimizations, if not done already (#2958)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-04-06 20:45:59 +02:00
oSumAtrIX
2a86a670f0 ci: Only push strings once a month 2024-04-05 01:16:20 +02:00
LisoUseInAIKyrios
dba37b834d chore: Add comments for translations 2024-04-05 01:15:06 +04:00
semantic-release-bot
3ad4eb8dbc chore(release): 4.6.1-dev.2 [skip ci]
## [4.6.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.6.1-dev.1...v4.6.1-dev.2) (2024-04-04)

### Bug Fixes

* **YouTube - Hide load more button:** Include patch with `Hide layout components`, and hide button only in search feed  ([#2959](https://github.com/ReVanced/revanced-patches/issues/2959)) ([0e88306](0e88306f3f))
2024-04-04 21:11:39 +00:00
nullptr
0e88306f3f fix(YouTube - Hide load more button): Include patch with Hide layout components, and hide button only in search feed (#2959) 2024-04-05 01:09:28 +04:00
LisoUseInAIKyrios
87cb63f2e7 chore: Fix typo 2024-04-04 14:25:36 +04:00
LisoUseInAIKyrios
0fead38701 chore: Add comments for translations 2024-04-04 14:19:03 +04:00
semantic-release-bot
19a31019c2 chore(release): 4.6.1-dev.1 [skip ci]
## [4.6.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.6.0...v4.6.1-dev.1) (2024-04-04)

### Bug Fixes

* **YouTube - Player flyout menu:** Add hide Lock screen menu ([#2985](https://github.com/ReVanced/revanced-patches/issues/2985)) ([6175841](61758414c2))
2024-04-04 06:49:26 +00:00
LisoUseInAIKyrios
61758414c2 fix(YouTube - Player flyout menu): Add hide Lock screen menu (#2985) 2024-04-04 10:47:09 +04:00
LisoUseInAIKyrios
b4ee8b6da4 chore: Remove unused string 2024-04-02 16:40:19 +04:00
semantic-release-bot
e86e3d88cf chore(release): 4.6.0 [skip ci]
# [4.6.0](https://github.com/ReVanced/revanced-patches/compare/v4.5.0...v4.6.0) (2024-04-02)

### Bug Fixes

* **Mi Fitness - Fix login:** Patch correct register ([#2942](https://github.com/ReVanced/revanced-patches/issues/2942)) ([fef5f35](fef5f35788))
* **Tumblr:** Restore compatibility with latest versions ([#2955](https://github.com/ReVanced/revanced-patches/issues/2955)) ([1a53a93](1a53a93cd2))

### Features

* **Tumblr:** Add `Fix old versions` patch ([#2954](https://github.com/ReVanced/revanced-patches/issues/2954)) ([2f28e95](2f28e95252))
2024-04-02 00:25:07 +00:00
oSumAtrIX
8cf444962c chore: Merge branch dev to main (#2957) 2024-04-02 02:23:06 +02:00
Ushie
48bf632dbe ci: Push strings to Crowdin on string changes (#2979)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-04-02 01:34:22 +02:00
Ushie
03e91d4251 ci: Don't sync Crowdin every hour (#2967) 2024-04-01 17:17:16 +04:00
semantic-release-bot
8caf70719e chore(release): 4.6.0-dev.1 [skip ci]
# [4.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.5.1-dev.2...v4.6.0-dev.1) (2024-03-31)

### Features

* **Tumblr:** Add `Fix old versions` patch ([#2954](https://github.com/ReVanced/revanced-patches/issues/2954)) ([2f28e95](2f28e95252))
2024-03-31 00:49:07 +00:00
Temm
2f28e95252 feat(Tumblr): Add Fix old versions patch (#2954)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-03-31 01:46:50 +01:00
semantic-release-bot
d819e7bbe9 chore(release): 4.5.1-dev.2 [skip ci]
## [4.5.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.5.1-dev.1...v4.5.1-dev.2) (2024-03-30)

### Bug Fixes

* **Mi Fitness - Fix login:** Patch correct register ([#2942](https://github.com/ReVanced/revanced-patches/issues/2942)) ([fef5f35](fef5f35788))
2024-03-30 22:54:45 +00:00
Michał Pisarski
fef5f35788 fix(Mi Fitness - Fix login): Patch correct register (#2942)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-03-30 23:52:45 +01:00
LisoUseInAIKyrios
80034bc70a chore: Add string format indexes, to allow more flexible translations (#2956) 2024-03-31 02:12:33 +04:00
semantic-release-bot
c40911a815 chore(release): 4.5.1-dev.1 [skip ci]
## [4.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.5.0...v4.5.1-dev.1) (2024-03-30)

### Bug Fixes

* **Tumblr:** Restore compatibility with latest versions ([#2955](https://github.com/ReVanced/revanced-patches/issues/2955)) ([1a53a93](1a53a93cd2))
2024-03-30 22:10:49 +00:00
Temm
1a53a93cd2 fix(Tumblr): Restore compatibility with latest versions (#2955) 2024-03-30 23:08:34 +01:00
semantic-release-bot
2e68c65c9f chore(release): 4.5.0 [skip ci]
# [4.5.0](https://github.com/ReVanced/revanced-patches/compare/v4.4.0...v4.5.0) (2024-03-30)

### Features

* **YouTube - Alternative thumbnails:** Selectively enable for home / subscription / search ([#2926](https://github.com/ReVanced/revanced-patches/issues/2926)) ([a272962](a27296223f))
* **YouTube - GmsCore:** Require ignoring battery optimizations ([#2952](https://github.com/ReVanced/revanced-patches/issues/2952)) ([9b3c060](9b3c060acb))
2024-03-30 19:42:19 +00:00
oSumAtrIX
6f8d92827b chore: Merge branch dev to main (#2943) 2024-03-30 20:40:10 +01:00
semantic-release-bot
ab2260acfd chore(release): 4.5.0-dev.2 [skip ci]
# [4.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.5.0-dev.1...v4.5.0-dev.2) (2024-03-30)

### Features

* **YouTube - GmsCore:** Require ignoring battery optimizations ([#2952](https://github.com/ReVanced/revanced-patches/issues/2952)) ([9b3c060](9b3c060acb))
2024-03-30 18:54:26 +00:00
oSumAtrIX
9b3c060acb feat(YouTube - GmsCore): Require ignoring battery optimizations (#2952) 2024-03-30 19:52:22 +01:00
LisoUseInAIKyrios
bbc4a6bfa0 chore: fix typo 2024-03-30 20:33:28 +04:00
Ushie
4df9b9d5b1 ci: Add workflow to sync Crowdin translations (#2953) 2024-03-30 03:15:07 +01:00
semantic-release-bot
eebf72f476 chore(release): 4.5.0-dev.1 [skip ci]
# [4.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.4.0...v4.5.0-dev.1) (2024-03-29)

### Features

* **YouTube - Alternative thumbnails:** Selectively enable for home / subscription / search ([#2926](https://github.com/ReVanced/revanced-patches/issues/2926)) ([a272962](a27296223f))
2024-03-29 09:31:55 +00:00
LisoUseInAIKyrios
a27296223f feat(YouTube - Alternative thumbnails): Selectively enable for home / subscription / search (#2926)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-03-29 13:30:00 +04:00
LisoUseInAIKyrios
77a9a56e2d Merge remote-tracking branch 'upstream/dev' into upstream-dev 2024-03-28 17:15:53 +04:00
LisoUseInAIKyrios
3df3441385 chore: fix typo 2024-03-28 17:15:40 +04:00
semantic-release-bot
86def3efea chore(release): 4.4.0 [skip ci]
# [4.4.0](https://github.com/ReVanced/revanced-patches/compare/v4.3.0...v4.4.0) (2024-03-27)

### Bug Fixes

* **TikTok:** Hook application context earlier to prevent crash ([#2893](https://github.com/ReVanced/revanced-patches/issues/2893)) ([e6a468c](e6a468cf7a))
* **YouTube - Client spoof:** Spoof all user agents ([d18719c](d18719c59c))
* **YouTube - Downloads:** Use new task context ([#2841](https://github.com/ReVanced/revanced-patches/issues/2841)) ([5cca421](5cca42121a))
* **YouTube - Hide ads:** Prevent app crash if hiding fullscreen ads is not possible ([#2910](https://github.com/ReVanced/revanced-patches/issues/2910)) ([2fcd786](2fcd786cc7))
* **YouTube Music:** Fix compatibility with latest versions ([#2924](https://github.com/ReVanced/revanced-patches/issues/2924)) ([7f852e8](7f852e8f32))
* **YouTube:** Fix video playback by switching to ReVanced GmsCore vendor ([#2907](https://github.com/ReVanced/revanced-patches/issues/2907)) ([0bd0c82](0bd0c827a4))
* **YouTube:** Move setting to correct screen ([46ff5b8](46ff5b8b92))

### Features

* **Instagram - Hide timeline ads:** Make compatible with latest versions ([8306f70](8306f70fd1))
* **Mi Fitness:** Add `Force English locale` and `Fix login` patch ([#2734](https://github.com/ReVanced/revanced-patches/issues/2734)) ([2679fad](2679fad25f))
* **Sync for Lemmy:** Add `Disable ads` patch ([#2872](https://github.com/ReVanced/revanced-patches/issues/2872)) ([08c4468](08c4468ff1))
* **YouTube - Downloads:** Use external downloader when selecting 'Download' in home feed flyout menu ([#2881](https://github.com/ReVanced/revanced-patches/issues/2881)) ([bc8bc8c](bc8bc8c798))
* **YouTube - External downloader:** Add ability to use in-app download button ([7f8b7c0](7f8b7c08b2))
* **YouTube - Hide layout components:** Filter home/search results by keywords ([#2853](https://github.com/ReVanced/revanced-patches/issues/2853)) ([cb4b651](cb4b6514db))
* **YouTube - Hide Shorts components:** Hide like and dislike buttons ([069f26d](069f26de20))
* **YouTube - Hide Shorts components:** Hide sound metadata label ([a9f418a](a9f418a19f))
* **YouTube - Hide Shorts components:** Hide title and full video link label ([5b0360c](5b0360c067))
* **YouTube - Hide Shorts components:** Selectively hide Shorts for home / subscription / search ([#2925](https://github.com/ReVanced/revanced-patches/issues/2925)) ([5b7b134](5b7b134d73))
* **YouTube :** Remove `HDR auto brightness` patch ([#2863](https://github.com/ReVanced/revanced-patches/issues/2863)) ([32139f1](32139f151b))
* **YouTube Vanced:** Remove `Hide ads` patch ([bcce7be](bcce7bee9a))
* **YouTube:** Support version `19.05`, `19.06`, `19.07`, `19.08` and `19.09` ([#2862](https://github.com/ReVanced/revanced-patches/issues/2862)) ([122ef68](122ef68a4b))
2024-03-27 18:37:58 +00:00
oSumAtrIX
bc9a612824 chore: Merge branch dev to main (#2804) 2024-03-27 19:35:49 +01:00
semantic-release-bot
f378ddc6d5 chore(release): 4.4.0-dev.15 [skip ci]
# [4.4.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.14...v4.4.0-dev.15) (2024-03-27)

### Bug Fixes

* **YouTube Music:** Fix compatibility with latest versions ([#2924](https://github.com/ReVanced/revanced-patches/issues/2924)) ([7f852e8](7f852e8f32))

### Features

* **YouTube - Hide Shorts components:** Selectively hide Shorts for home / subscription / search ([#2925](https://github.com/ReVanced/revanced-patches/issues/2925)) ([5b7b134](5b7b134d73))
2024-03-27 18:18:21 +00:00
oSumAtrIX
7f852e8f32 fix(YouTube Music): Fix compatibility with latest versions (#2924) 2024-03-27 19:08:18 +01:00
LisoUseInAIKyrios
5b7b134d73 feat(YouTube - Hide Shorts components): Selectively hide Shorts for home / subscription / search (#2925) 2024-03-27 15:02:34 +01:00
oSumAtrIX
df9361ea2b refactor: Move integrations to correct package 2024-03-27 15:02:34 +01:00
LisoUseInAIKyrios
8c226c4bdc chore: fix typo 2024-03-27 13:09:23 +04:00
semantic-release-bot
c87f8a376d chore(release): 4.4.0-dev.14 [skip ci]
# [4.4.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.13...v4.4.0-dev.14) (2024-03-27)

### Features

* **YouTube - Hide layout components:** Filter home/search results by keywords ([#2853](https://github.com/ReVanced/revanced-patches/issues/2853)) ([cb4b651](cb4b6514db))
2024-03-27 07:28:55 +00:00
LisoUseInAIKyrios
cb4b6514db feat(YouTube - Hide layout components): Filter home/search results by keywords (#2853)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-03-27 11:26:40 +04:00
111 changed files with 2411 additions and 1088 deletions

34
.github/workflows/pull_strings.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Pull strings
on:
workflow_dispatch:
schedule:
- cron: 0 0 1 * *
jobs:
pull:
name: Pull strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Pull strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
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:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

27
.github/workflows/push_strings.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Push strings
on:
workflow_dispatch:
push:
paths:
- /src/main/resources/addresources/values/strings.xml
jobs:
push:
name: Push strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
upload_sources: true
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -1,3 +1,222 @@
# [4.7.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.12...v4.7.0-dev.13) (2024-04-18)
### Bug Fixes
* **YouTube Music - Remove upgrade button:** Fix compatibility with latest versions ([#3045](https://github.com/ReVanced/revanced-patches/issues/3045)) ([80de996](https://github.com/ReVanced/revanced-patches/commit/80de99666555694670529bbfe2e0be7a14d66555))
# [4.7.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.11...v4.7.0-dev.12) (2024-04-18)
### Features
* Add `Hex` patch ([#3034](https://github.com/ReVanced/revanced-patches/issues/3034)) ([3c95aac](https://github.com/ReVanced/revanced-patches/commit/3c95aac838693b354d3a7b0e3dc57c6da5adfa9e))
# [4.7.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.10...v4.7.0-dev.11) (2024-04-18)
### Bug Fixes
* **YouTube - Spoof device dimensions:** Warn about potential performance issues ([#3039](https://github.com/ReVanced/revanced-patches/issues/3039)) ([9d6f305](https://github.com/ReVanced/revanced-patches/commit/9d6f305b7c923e62b89581d221fedbe1e3f81835))
# [4.7.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.9...v4.7.0-dev.10) (2024-04-17)
### Features
* **YouTube:** Add 'About' preference to settings menu ([#2981](https://github.com/ReVanced/revanced-patches/issues/2981)) ([5abf894](https://github.com/ReVanced/revanced-patches/commit/5abf89444a3e6a211ec03c242eb9a7847542b08c))
* **YouTube:** Match overlay icons style to YouTube ([#3023](https://github.com/ReVanced/revanced-patches/issues/3023)) ([6849393](https://github.com/ReVanced/revanced-patches/commit/684939314be3d0d43482f229b2adb033e7aa492a))
# [4.7.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.8...v4.7.0-dev.9) (2024-04-15)
### Bug Fixes
* **YouTube - Hide ads:** rename `Hide paid content` to `Hide paid promotion label` ([#3026](https://github.com/ReVanced/revanced-patches/issues/3026)) ([17e4ac9](https://github.com/ReVanced/revanced-patches/commit/17e4ac978a2f109fd62469a3163b636cd63c55ae))
# [4.7.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.7...v4.7.0-dev.8) (2024-04-14)
### Features
* **YouTube - Hide Shorts components:** Hide tagged products, hide search suggestions ([#3019](https://github.com/ReVanced/revanced-patches/issues/3019)) ([e0d2fe5](https://github.com/ReVanced/revanced-patches/commit/e0d2fe5bd2e681b9a5252a8e4ad582cc019b1606))
# [4.7.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.6...v4.7.0-dev.7) (2024-04-14)
### Features
* **YouTube - Hide layout components:** Hide playables ([8423515](https://github.com/ReVanced/revanced-patches/commit/842351548baa33737db09be1cbca9f87c1951341))
# [4.7.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.5...v4.7.0-dev.6) (2024-04-12)
### Features
* **YouTube - Hide Shorts components:** Hide `Shop`, `Location` and `Save sound to playlist` buttons ([#3018](https://github.com/ReVanced/revanced-patches/issues/3018)) ([5210ac4](https://github.com/ReVanced/revanced-patches/commit/5210ac431c191987264865bf8e789ea9f3fdd360))
# [4.7.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.4...v4.7.0-dev.5) (2024-04-10)
### Bug Fixes
* **YouTube - Hide Shorts components:** Correctly hide Shorts if navigation tab is changed using device back button ([#3007](https://github.com/ReVanced/revanced-patches/issues/3007)) ([e5848e9](https://github.com/ReVanced/revanced-patches/commit/e5848e99c4cc838595164ef673a77fe60d28086b))
# [4.7.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.3...v4.7.0-dev.4) (2024-04-09)
### Features
* **Amazon:** Add `Always allow deep-linking` patch ([#3000](https://github.com/ReVanced/revanced-patches/issues/3000)) ([a92b7fb](https://github.com/ReVanced/revanced-patches/commit/a92b7fb43c8b1b45577360cdc6d883fe2815c2f2))
* **Twitter:** Add `Sanitize sharing links` patch ([#3003](https://github.com/ReVanced/revanced-patches/issues/3003)) ([186b887](https://github.com/ReVanced/revanced-patches/commit/186b8874157eef1b882b05d491ba1d4ca2809535))
# [4.7.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.2...v4.7.0-dev.3) (2024-04-09)
### Bug Fixes
* **Tumblr - Fix old versions:** Improve reliability by removing remnances of Tumblr Live ([#2988](https://github.com/ReVanced/revanced-patches/issues/2988)) ([897b4db](https://github.com/ReVanced/revanced-patches/commit/897b4dbce984270ae1fd7de5bd30bd05153e45f2))
# [4.7.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.7.0-dev.1...v4.7.0-dev.2) (2024-04-06)
### Features
* **Strava - Unlock subscription:** Remove compatible version constraint ([80a5599](https://github.com/ReVanced/revanced-patches/commit/80a55991683d7b22626224fa2935a5bf9bfcbfee))
# [4.7.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.6.1-dev.3...v4.7.0-dev.1) (2024-04-06)
### Features
* **YouTube - Hide layout components:** Add option to hide horizontal shelves ([#2951](https://github.com/ReVanced/revanced-patches/issues/2951)) ([9ae0650](https://github.com/ReVanced/revanced-patches/commit/9ae0650c0005d882299996aa442410bab4261395))
## [4.6.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.6.1-dev.2...v4.6.1-dev.3) (2024-04-06)
### Bug Fixes
* **YouTube - GmsCore support:** Prompt to disable battery optimizations, if not done already ([#2958](https://github.com/ReVanced/revanced-patches/issues/2958)) ([82acb84](https://github.com/ReVanced/revanced-patches/commit/82acb84b5f6ff0722a2eb080b53da9dd3622502f))
## [4.6.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.6.1-dev.1...v4.6.1-dev.2) (2024-04-04)
### Bug Fixes
* **YouTube - Hide load more button:** Include patch with `Hide layout components`, and hide button only in search feed ([#2959](https://github.com/ReVanced/revanced-patches/issues/2959)) ([b007e8e](https://github.com/ReVanced/revanced-patches/commit/b007e8e06a3afad79b40bec1c6a14604f059049c))
## [4.6.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.6.0...v4.6.1-dev.1) (2024-04-04)
### Bug Fixes
* **YouTube - Player flyout menu:** Add hide Lock screen menu ([#2985](https://github.com/ReVanced/revanced-patches/issues/2985)) ([308de4a](https://github.com/ReVanced/revanced-patches/commit/308de4a63ca99b8d30d6b3242f98d6f0e2aefb37))
# [4.6.0](https://github.com/ReVanced/revanced-patches/compare/v4.5.0...v4.6.0) (2024-04-02)
### Bug Fixes
* **Mi Fitness - Fix login:** Patch correct register ([#2942](https://github.com/ReVanced/revanced-patches/issues/2942)) ([dc96942](https://github.com/ReVanced/revanced-patches/commit/dc969422b5d50f21e6ea7a64b67dfc650fee6e36))
* **Tumblr:** Restore compatibility with latest versions ([#2955](https://github.com/ReVanced/revanced-patches/issues/2955)) ([2954ba7](https://github.com/ReVanced/revanced-patches/commit/2954ba78d21d77308404961f79234bbec606d42e))
### Features
* **Tumblr:** Add `Fix old versions` patch ([#2954](https://github.com/ReVanced/revanced-patches/issues/2954)) ([2fde60e](https://github.com/ReVanced/revanced-patches/commit/2fde60eceb0a96fa857c32cd55c1fd7fe776a679))
# [4.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.5.1-dev.2...v4.6.0-dev.1) (2024-03-31)
### Features
* **Tumblr:** Add `Fix old versions` patch ([#2954](https://github.com/ReVanced/revanced-patches/issues/2954)) ([2fde60e](https://github.com/ReVanced/revanced-patches/commit/2fde60eceb0a96fa857c32cd55c1fd7fe776a679))
## [4.5.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.5.1-dev.1...v4.5.1-dev.2) (2024-03-30)
### Bug Fixes
* **Mi Fitness - Fix login:** Patch correct register ([#2942](https://github.com/ReVanced/revanced-patches/issues/2942)) ([dc96942](https://github.com/ReVanced/revanced-patches/commit/dc969422b5d50f21e6ea7a64b67dfc650fee6e36))
## [4.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.5.0...v4.5.1-dev.1) (2024-03-30)
### Bug Fixes
* **Tumblr:** Restore compatibility with latest versions ([#2955](https://github.com/ReVanced/revanced-patches/issues/2955)) ([2954ba7](https://github.com/ReVanced/revanced-patches/commit/2954ba78d21d77308404961f79234bbec606d42e))
# [4.5.0](https://github.com/ReVanced/revanced-patches/compare/v4.4.0...v4.5.0) (2024-03-30)
### Features
* **YouTube - Alternative thumbnails:** Selectively enable for home / subscription / search ([#2926](https://github.com/ReVanced/revanced-patches/issues/2926)) ([8549e1b](https://github.com/ReVanced/revanced-patches/commit/8549e1ba58ad1e1608f5e3ceacd31eeb94578949))
* **YouTube - GmsCore:** Require ignoring battery optimizations ([#2952](https://github.com/ReVanced/revanced-patches/issues/2952)) ([c0bef25](https://github.com/ReVanced/revanced-patches/commit/c0bef255909ca884838675ca6f7ac5b0e2e21730))
# [4.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.5.0-dev.1...v4.5.0-dev.2) (2024-03-30)
### Features
* **YouTube - GmsCore:** Require ignoring battery optimizations ([#2952](https://github.com/ReVanced/revanced-patches/issues/2952)) ([c0bef25](https://github.com/ReVanced/revanced-patches/commit/c0bef255909ca884838675ca6f7ac5b0e2e21730))
# [4.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.4.0...v4.5.0-dev.1) (2024-03-29)
### Features
* **YouTube - Alternative thumbnails:** Selectively enable for home / subscription / search ([#2926](https://github.com/ReVanced/revanced-patches/issues/2926)) ([8549e1b](https://github.com/ReVanced/revanced-patches/commit/8549e1ba58ad1e1608f5e3ceacd31eeb94578949))
# [4.4.0](https://github.com/ReVanced/revanced-patches/compare/v4.3.0...v4.4.0) (2024-03-27)
### Bug Fixes
* **TikTok:** Hook application context earlier to prevent crash ([#2893](https://github.com/ReVanced/revanced-patches/issues/2893)) ([395ccda](https://github.com/ReVanced/revanced-patches/commit/395ccda7b9218c522c7ca0f99f75a12036d3e3f3))
* **YouTube - Client spoof:** Spoof all user agents ([44a8a13](https://github.com/ReVanced/revanced-patches/commit/44a8a1399897caaff3ff45db8549ddedb2f01b68))
* **YouTube - Downloads:** Use new task context ([#2841](https://github.com/ReVanced/revanced-patches/issues/2841)) ([6d88cb4](https://github.com/ReVanced/revanced-patches/commit/6d88cb49ec739700866290babcba5fb3032dbced))
* **YouTube - Hide ads:** Prevent app crash if hiding fullscreen ads is not possible ([#2910](https://github.com/ReVanced/revanced-patches/issues/2910)) ([9f50470](https://github.com/ReVanced/revanced-patches/commit/9f50470bf6582fe2f20a903a97bf66c41f296fb2))
* **YouTube Music:** Fix compatibility with latest versions ([#2924](https://github.com/ReVanced/revanced-patches/issues/2924)) ([8378c84](https://github.com/ReVanced/revanced-patches/commit/8378c8481634a63a5568480475757e64b7ec466e))
* **YouTube:** Fix video playback by switching to ReVanced GmsCore vendor ([#2907](https://github.com/ReVanced/revanced-patches/issues/2907)) ([33ea122](https://github.com/ReVanced/revanced-patches/commit/33ea12228c2ae5dcadf3e7c7016d4bf6006d899a))
* **YouTube:** Move setting to correct screen ([a16eda8](https://github.com/ReVanced/revanced-patches/commit/a16eda864515612d3a6b846082844df15eb49f56))
### Features
* **Instagram - Hide timeline ads:** Make compatible with latest versions ([a212f29](https://github.com/ReVanced/revanced-patches/commit/a212f29bd33bb5e10f024e058d26e20ee926190b))
* **Mi Fitness:** Add `Force English locale` and `Fix login` patch ([#2734](https://github.com/ReVanced/revanced-patches/issues/2734)) ([7a25791](https://github.com/ReVanced/revanced-patches/commit/7a25791d53530b1236896b2c3d6275ee7556e8b7))
* **Sync for Lemmy:** Add `Disable ads` patch ([#2872](https://github.com/ReVanced/revanced-patches/issues/2872)) ([0785819](https://github.com/ReVanced/revanced-patches/commit/0785819dd5ad487c778b5baf09004cdab3687184))
* **YouTube - Downloads:** Use external downloader when selecting 'Download' in home feed flyout menu ([#2881](https://github.com/ReVanced/revanced-patches/issues/2881)) ([10afc8c](https://github.com/ReVanced/revanced-patches/commit/10afc8cc71ff29fea4937fb12fd3d1edf9c581f5))
* **YouTube - External downloader:** Add ability to use in-app download button ([d900011](https://github.com/ReVanced/revanced-patches/commit/d9000113a905c14f8409aa75008f1ef6a1aecd0c))
* **YouTube - Hide layout components:** Filter home/search results by keywords ([#2853](https://github.com/ReVanced/revanced-patches/issues/2853)) ([5916204](https://github.com/ReVanced/revanced-patches/commit/59162042b0a68edf7f94a3c21f838dada3c3f9c3))
* **YouTube - Hide Shorts components:** Hide like and dislike buttons ([2df0892](https://github.com/ReVanced/revanced-patches/commit/2df0892682406e67283c4aeaacebf8f222029833))
* **YouTube - Hide Shorts components:** Hide sound metadata label ([ea7d1e0](https://github.com/ReVanced/revanced-patches/commit/ea7d1e0d08cc245117ffe8ad0df3c31c5e87f739))
* **YouTube - Hide Shorts components:** Hide title and full video link label ([e7b64e1](https://github.com/ReVanced/revanced-patches/commit/e7b64e154e7fb8edd0037f5e171f4aa3ed9017f8))
* **YouTube - Hide Shorts components:** Selectively hide Shorts for home / subscription / search ([#2925](https://github.com/ReVanced/revanced-patches/issues/2925)) ([497c067](https://github.com/ReVanced/revanced-patches/commit/497c067e80e560eb125f5fc30eef9763929b4ee4))
* **YouTube :** Remove `HDR auto brightness` patch ([#2863](https://github.com/ReVanced/revanced-patches/issues/2863)) ([b4c7bf4](https://github.com/ReVanced/revanced-patches/commit/b4c7bf48084184e5f031f7f5139a9a29341d8ebf))
* **YouTube Vanced:** Remove `Hide ads` patch ([87887e4](https://github.com/ReVanced/revanced-patches/commit/87887e4163dd9e242209f4d0fefb415f9bc7ca75))
* **YouTube:** Support version `19.05`, `19.06`, `19.07`, `19.08` and `19.09` ([#2862](https://github.com/ReVanced/revanced-patches/issues/2862)) ([f044dde](https://github.com/ReVanced/revanced-patches/commit/f044dde054861ff16846a6be6287f86fa3afb3d8))
# [4.4.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.14...v4.4.0-dev.15) (2024-03-27)
### Bug Fixes
* **YouTube Music:** Fix compatibility with latest versions ([#2924](https://github.com/ReVanced/revanced-patches/issues/2924)) ([8378c84](https://github.com/ReVanced/revanced-patches/commit/8378c8481634a63a5568480475757e64b7ec466e))
### Features
* **YouTube - Hide Shorts components:** Selectively hide Shorts for home / subscription / search ([#2925](https://github.com/ReVanced/revanced-patches/issues/2925)) ([497c067](https://github.com/ReVanced/revanced-patches/commit/497c067e80e560eb125f5fc30eef9763929b4ee4))
# [4.4.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.13...v4.4.0-dev.14) (2024-03-27)
### Features
* **YouTube - Hide layout components:** Filter home/search results by keywords ([#2853](https://github.com/ReVanced/revanced-patches/issues/2853)) ([5916204](https://github.com/ReVanced/revanced-patches/commit/59162042b0a68edf7f94a3c21f838dada3c3f9c3))
# [4.4.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.12...v4.4.0-dev.13) (2024-03-26)

View File

@@ -28,6 +28,10 @@ public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggin
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/all/misc/hex/HexPatch : app/revanced/patches/shared/misc/hex/BaseHexPatch {
public fun <init> ()V
}
public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/all/misc/network/OverrideCertificatePinningPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@@ -152,6 +156,12 @@ public final class app/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryP
public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
}
public final class app/revanced/patches/amazon/deeplinking/DeepLinkingPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/amazon/deeplinking/DeepLinkingPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/backdrops/misc/pro/ProUnlockPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -302,6 +312,12 @@ public final class app/revanced/patches/moneymanager/UnlockProPatch : app/revanc
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/ad/video/HideMusicVideoAds : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideMusicVideoAds;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/ad/video/MusicVideoAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/ad/video/MusicVideoAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -314,6 +330,12 @@ public final class app/revanced/patches/music/audio/codecs/CodecsUnlockPatch : a
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -326,6 +348,12 @@ public final class app/revanced/patches/music/interaction/permanentrepeat/Perman
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -338,6 +366,12 @@ public final class app/revanced/patches/music/layout/compactheader/CompactHeader
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/layout/compactheader/HideCategoryBar : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/layout/compactheader/HideCategoryBar;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -639,6 +673,21 @@ public abstract class app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportRes
protected final fun getGmsCoreVendorGroupId ()Ljava/lang/String;
}
public abstract class app/revanced/patches/shared/misc/hex/BaseHexPatch : app/revanced/patcher/patch/RawResourcePatch {
public fun <init> ()V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement {
public static final field Companion Lapp/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public final fun replacePattern ([B)V
}
public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion {
}
public abstract class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch : app/revanced/patcher/patch/BytecodePatch {
public fun <init> (Ljava/lang/String;Ljava/util/Set;)V
public fun <init> (Ljava/util/Set;)V
@@ -814,6 +863,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
}
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSelectable ()Z
@@ -1032,6 +1083,12 @@ public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlags
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/tumblr/fixes/FixOldVersionsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/tumblr/live/DisableTumblrLivePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/tumblr/live/DisableTumblrLivePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -1166,6 +1223,12 @@ public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChoos
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/vsco/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/vsco/misc/pro/UnlockProPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -1609,6 +1672,13 @@ public final class app/revanced/patches/youtube/misc/minimizedplayback/Minimized
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch;
public final fun addControls (Ljava/lang/String;)V

View File

@@ -15,7 +15,7 @@ repositories {
mavenLocal()
google()
maven {
// A repository must be speficied for some reason. "registry" is a dummy.
// A repository must be specified for some reason. "registry" is a dummy.
url = uri("https://maven.pkg.github.com/revanced/registry")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")

8
crowdin.yml Normal file
View File

@@ -0,0 +1,8 @@
project_id_env: "CROWDIN_PROJECT_ID"
api_token_env: "CROWDIN_PERSONAL_TOKEN"
preserve_hierarchy: false
files:
- source: src/main/resources/addresources/values/strings.xml
translation: src/main/resources/addresources/values-%android_code%/strings.xml
skip_untranslated_strings: true

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 4.4.0-dev.13
version = 4.7.0-dev.13

View File

@@ -0,0 +1,55 @@
package app.revanced.patches.all.misc.hex
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.registerNewPatchOption
import app.revanced.patches.shared.misc.hex.BaseHexPatch
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.patcher.patch.Patch as PatchClass
@Patch(
name = "Hex",
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
use = false,
)
@Suppress("unused")
class HexPatch : BaseHexPatch() {
// TODO: Instead of stringArrayOption, use a custom option type to work around
// https://github.com/ReVanced/revanced-library/issues/48.
// Replace the custom option type with a stringArrayOption once the issue is resolved.
private val replacementsOption by registerNewPatchOption<PatchClass<*>, List<String>>(
key = "replacements",
title = "replacements",
description = """
Hexadecimal patterns to search for and replace with another in a target file.
A pattern is a sequence of case insensitive strings, each representing hexadecimal bytes, separated by spaces.
An example pattern is 'aa 01 02 FF'.
Every pattern must be followed by a pipe ('|'), the replacement pattern,
another pipe ('|'), and the path to the file to make the changes in relative to the APK root.
The replacement pattern must have the same length as the original pattern.
Full example of a valid input:
'aa 01 02 FF|00 00 00 00|path/to/file'
""".trimIndentMultiline(),
required = true,
valueType = "StringArray",
)
override val replacements
get() = replacementsOption!!.map { from ->
val (pattern, replacementPattern, targetFilePath) = try {
from.split("|", limit = 3)
} catch (e: Exception) {
throw PatchException(
"Invalid input: $from.\n" +
"Every pattern must be followed by a pipe ('|'), " +
"the replacement pattern, another pipe ('|'), " +
"and the path to the file to make the changes in relative to the APK root. ",
)
}
Replacement(pattern, replacementPattern, targetFilePath)
}
}

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.amazon.deeplinking
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object DeepLinkingFingerprint : MethodFingerprint(
"Z",
parameters = listOf("L"),
accessFlags = AccessFlags.PRIVATE.value,
strings = listOf("https://www.", "android.intent.action.VIEW")
)

View File

@@ -0,0 +1,28 @@
package app.revanced.patches.amazon.deeplinking
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.exception
@Patch(
name = "Always allow deep-linking",
description = "Open Amazon links, even if the app is not set to handle Amazon links.",
compatiblePackages = [CompatiblePackage("com.amazon.mShop.android.shopping")]
)
@Suppress("unused")
object DeepLinkingPatch : BytecodePatch(
setOf(DeepLinkingFingerprint)
) {
override fun execute(context: BytecodeContext) {
DeepLinkingFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
) ?: throw DeepLinkingFingerprint.exception
}
}

View File

@@ -2,13 +2,11 @@ package app.revanced.patches.mifitness.misc.login
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.mifitness.misc.login.fingerprints.XiaomiAccountManagerConstructorFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Fix login",
@@ -20,16 +18,9 @@ object FixLoginPatch : BytecodePatch(
setOf(XiaomiAccountManagerConstructorFingerprint),
) {
override fun execute(context: BytecodeContext) {
XiaomiAccountManagerConstructorFingerprint.result?.let {
it.mutableMethod.apply {
val isCertifiedIndex = it.scanResult.patternScanResult!!.startIndex
val isCertifiedRegister = getInstruction<OneRegisterInstruction>(isCertifiedIndex).registerA
addInstruction(
isCertifiedIndex,
"const/4 p$isCertifiedRegister, 0x0",
)
}
} ?: throw XiaomiAccountManagerConstructorFingerprint.exception
XiaomiAccountManagerConstructorFingerprint.result?.mutableMethod?.addInstruction(
0,
"const/16 p2, 0x0",
) ?: throw XiaomiAccountManagerConstructorFingerprint.exception
}
}

View File

@@ -3,12 +3,14 @@ package app.revanced.patches.mifitness.misc.login.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object XiaomiAccountManagerConstructorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.CONSTRUCTOR,
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lcom/xiaomi/passport/accountmanager/XiaomiAccountManager;"
},
opcodes = listOf(Opcode.IF_NEZ),
parameters = listOf(
"Landroid/content/Context;",
"Z",
),
)

View File

@@ -0,0 +1,37 @@
package app.revanced.patches.music.ad.video
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsParentFingerprint
import app.revanced.util.exception
@Patch(
name = "Hide music video ads",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
)
@Suppress("unused")
object HideMusicVideoAds : BytecodePatch(
setOf(ShowMusicVideoAdsParentFingerprint),
) {
override fun execute(context: BytecodeContext) {
ShowMusicVideoAdsParentFingerprint.result?.let {
val showMusicVideoAdsMethod = context
.toMethodWalker(it.mutableMethod)
.nextMethod(it.scanResult.patternScanResult!!.startIndex + 1, true).getMethod() as MutableMethod
showMusicVideoAdsMethod.addInstruction(0, "const/4 p1, 0x0")
} ?: throw ShowMusicVideoAdsParentFingerprint.exception
}
}
@Deprecated("This patch class has been renamed to HideMusicVideoAds.")
object MusicVideoAdsPatch : BytecodePatch(
dependencies = setOf(HideMusicVideoAds::class),
) {
override fun execute(context: BytecodeContext) {
}
}

View File

@@ -1,32 +0,0 @@
package app.revanced.patches.music.ad.video
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsConstructorFingerprint
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsFingerprint
@Patch(
name = "Music video ads",
description = "Removes ads in the music player.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
)
@Suppress("unused")
object MusicVideoAdsPatch : BytecodePatch(
setOf(ShowMusicVideoAdsConstructorFingerprint)
) {
override fun execute(context: BytecodeContext) {
ShowMusicVideoAdsFingerprint.resolve(context, ShowMusicVideoAdsConstructorFingerprint.result!!.classDef)
val result = ShowMusicVideoAdsFingerprint.result!!
result.mutableMethod.addInstruction(
result.scanResult.patternScanResult!!.startIndex,
"""
const/4 p1, 0x0
"""
)
}
}

View File

@@ -1,27 +0,0 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
internal object ShowMusicVideoAdsConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN,
Opcode.RETURN_VOID
)
)

View File

@@ -1,14 +0,0 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object ShowMusicVideoAdsFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), listOf(
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID
)
)

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object ShowMusicVideoAdsParentFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
),
strings = listOf("maybeRegenerateCpnAndStatsClient called unexpectedly, but no error."),
)

View File

@@ -10,13 +10,12 @@ import app.revanced.patches.music.audio.codecs.fingerprints.CodecsLockFingerprin
import com.android.tools.smali.dexlib2.Opcode
@Patch(
name = "Codecs unlock",
description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
)
@Suppress("unused")
@Deprecated("This patch is no longer needed as the feature is now enabled by default.")
object CodecsUnlockPatch : BytecodePatch(
setOf(CodecsLockFingerprint, AllCodecsReferenceFingerprint)
setOf(CodecsLockFingerprint, AllCodecsReferenceFingerprint),
) {
override fun execute(context: BytecodeContext) {
val codecsLockResult = CodecsLockFingerprint.result!!
@@ -25,13 +24,13 @@ object CodecsUnlockPatch : BytecodePatch(
val scanResultStartIndex = codecsLockResult.scanResult.patternScanResult!!.startIndex
val instructionIndex = scanResultStartIndex +
if (implementation.instructions[scanResultStartIndex - 1].opcode == Opcode.CHECK_CAST) {
// for 5.16.xx and lower
-3
} else {
// since 5.17.xx
-2
}
if (implementation.instructions[scanResultStartIndex - 1].opcode == Opcode.CHECK_CAST) {
// for 5.16.xx and lower
-3
} else {
// since 5.17.xx
-2
}
val allCodecsResult = AllCodecsReferenceFingerprint.result!!
val allCodecsMethod =
@@ -41,7 +40,7 @@ object CodecsUnlockPatch : BytecodePatch(
implementation.replaceInstruction(
instructionIndex,
"invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;".toInstruction()
"invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;".toInstruction(),
)
}
}

View File

@@ -1,31 +1,36 @@
package app.revanced.patches.music.audio.exclusiveaudio
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.audio.exclusiveaudio.fingerprints.AllowExclusiveAudioPlaybackFingerprint
import app.revanced.util.exception
@Patch(
name = "Exclusive audio playback",
name = "Enable exclusive audio playback",
description = "Enables the option to play audio without video.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
)
@Suppress("unused")
object ExclusiveAudioPatch : BytecodePatch(
setOf(AllowExclusiveAudioPlaybackFingerprint)
object EnableExclusiveAudioPlayback : BytecodePatch(
setOf(AllowExclusiveAudioPlaybackFingerprint),
) {
override fun execute(context: BytecodeContext) {
AllowExclusiveAudioPlaybackFingerprint.result?.mutableMethod?.apply {
addInstructions(
0,
"""
"""
const/4 v0, 0x1
return v0
"""
""",
)
} ?: throw AllowExclusiveAudioPlaybackFingerprint.exception
}
}
}
@Deprecated("This patch class has been renamed to EnableExclusiveAudioPlayback.")
object ExclusiveAudioPatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) = EnableExclusiveAudioPlayback.execute(context)
}

View File

@@ -1,25 +1,32 @@
package app.revanced.patches.music.interaction.permanentshuffle
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.interaction.permanentshuffle.fingerprints.DisableShuffleFingerprint
import app.revanced.util.exception
@Patch(
name = "Permanent shuffle",
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.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
use = false
use = false,
)
@Suppress("unused")
object PermanentShuffleTogglePatch : BytecodePatch(setOf(DisableShuffleFingerprint)) {
object PermanentShufflePatch : BytecodePatch(setOf(DisableShuffleFingerprint)) {
override fun execute(context: BytecodeContext) {
DisableShuffleFingerprint.result?.mutableMethod?.addInstruction(0, "return-void")
?: throw DisableShuffleFingerprint.exception
}
}
@Deprecated("This patch class has been renamed to PermanentShufflePatch.")
object PermanentShuffleTogglePatch : BytecodePatch(
dependencies = setOf(PermanentShufflePatch::class),
) {
override fun execute(context: BytecodeContext) {
}
}

View File

@@ -1,34 +0,0 @@
package app.revanced.patches.music.layout.compactheader
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.layout.compactheader.fingerprints.CompactHeaderConstructorFingerprint
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
@Patch(
name = "Compact header",
description = "Hides the music category bar at the top of the homepage.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
use = false
)
@Suppress("unused")
object CompactHeaderPatch : BytecodePatch(
setOf(CompactHeaderConstructorFingerprint)
) {
override fun execute(context: BytecodeContext) {
val result = CompactHeaderConstructorFingerprint.result!!
val method = result.mutableMethod
val insertIndex = result.scanResult.patternScanResult!!.endIndex
val register = (method.implementation!!.instructions[insertIndex - 1] as BuilderInstruction11x).registerA
method.addInstructions(
insertIndex, """
const/16 v2, 0x8
invoke-virtual {v${register}, v2}, Landroid/view/View;->setVisibility(I)V
"""
)
}
}

View File

@@ -0,0 +1,47 @@
package app.revanced.patches.music.layout.compactheader
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.layout.compactheader.fingerprints.ConstructCategoryBarFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Hide category bar",
description = "Hides the category bar at the top of the homepage.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
use = false,
)
@Suppress("unused")
object HideCategoryBar : BytecodePatch(
setOf(ConstructCategoryBarFingerprint),
) {
override fun execute(context: BytecodeContext) {
ConstructCategoryBarFingerprint.result?.let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
addInstructions(
insertIndex,
"""
const/16 v2, 0x8
invoke-virtual {v$register, v2}, Landroid/view/View;->setVisibility(I)V
""",
)
}
} ?: throw ConstructCategoryBarFingerprint.exception
}
}
@Deprecated("This patch class has been renamed to HideCategoryBar.")
object CompactHeaderPatch : BytecodePatch(
dependencies = setOf(HideCategoryBar::class),
) {
override fun execute(context: BytecodeContext) {
}
}

View File

@@ -5,19 +5,19 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object CompactHeaderConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
internal object ConstructCategoryBarFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf("Landroid/content/Context;", "L", "L", "L"),
listOf(
Opcode.IPUT_OBJECT,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_STATIC,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST
)
Opcode.IPUT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
),
)

View File

@@ -1,26 +1,39 @@
package app.revanced.patches.music.layout.minimizedplayback
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.BackgroundPlaybackDisableFingerprint
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
import app.revanced.util.exception
@Patch(
name = "Minimized playback music",
description = "Enables minimized playback on Kids music.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
name = "Minimized playback",
description = "Unlocks options for picture-in-picture and background playback.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
)
@Suppress("unused")
object MinimizedPlaybackPatch : BytecodePatch(setOf(MinimizedPlaybackManagerFingerprint)) {
override fun execute(context: BytecodeContext) =
MinimizedPlaybackManagerFingerprint.result?.mutableMethod?.addInstruction(
object MinimizedPlaybackPatch : BytecodePatch(
setOf(
KidsMinimizedPlaybackPolicyControllerFingerprint,
BackgroundPlaybackDisableFingerprint,
),
) {
override fun execute(context: BytecodeContext) {
KidsMinimizedPlaybackPolicyControllerFingerprint.result?.mutableMethod?.addInstruction(
0,
"return-void",
) ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception
BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
return-void
"""
) ?: throw MinimizedPlaybackManagerFingerprint.exception
const/4 v0, 0x1
return v0
""",
) ?: throw BackgroundPlaybackDisableFingerprint.exception
}
}

View File

@@ -0,0 +1,23 @@
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object BackgroundPlaybackDisableFingerprint : MethodFingerprint(
"Z",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L"),
listOf(
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET,
Opcode.AND_INT_LIT16,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
),
)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("I", "L", "Z"),
@@ -22,5 +22,5 @@ internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
Opcode.CONST_4,
Opcode.IF_NE,
Opcode.IPUT_BOOLEAN,
)
),
)

View File

@@ -2,41 +2,44 @@ package app.revanced.patches.music.layout.premium
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
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.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumFingerprint
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumParentFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@Patch(
name = "Hide get premium",
description = "Removes all \"Get Premium\" evidences from the avatar menu.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
name = "Hide 'Get Music Premium' label",
description = "Hides the red \"Get Music Premium\" label from the account menu.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
)
@Suppress("unused")
object HideGetPremiumPatch : BytecodePatch(setOf(HideGetPremiumParentFingerprint)) {
object HideGetPremiumPatch : BytecodePatch(
setOf(HideGetPremiumFingerprint),
) {
override fun execute(context: BytecodeContext) {
val parentResult = HideGetPremiumParentFingerprint.result!!
HideGetPremiumFingerprint.resolve(context, parentResult.classDef)
HideGetPremiumFingerprint.result?.let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.endIndex
val startIndex = parentResult.scanResult.patternScanResult!!.startIndex
val setVisibilityInstruction = getInstruction<FiveRegisterInstruction>(insertIndex)
val getPremiumViewRegister = setVisibilityInstruction.registerC
val visibilityRegister = setVisibilityInstruction.registerD
val parentMethod = parentResult.mutableMethod
parentMethod.replaceInstruction(
startIndex,
"""
const/4 v1, 0x0
"""
)
replaceInstruction(
insertIndex,
"const/16 v$visibilityRegister, 0x8",
)
val result = HideGetPremiumFingerprint.result!!
val method = result.mutableMethod
method.addInstruction(
startIndex,
"""
const/16 v0, 0x8
"""
)
addInstruction(
insertIndex + 1,
"invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " +
"Landroid/view/View;->setVisibility(I)V",
)
}
} ?: throw HideGetPremiumFingerprint.exception
}
}

View File

@@ -6,11 +6,13 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object HideGetPremiumFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf(),
listOf(
Opcode.IF_NEZ,
Opcode.CONST_16,
Opcode.GOTO,
Opcode.NOP,
Opcode.INVOKE_VIRTUAL
)
Opcode.INVOKE_VIRTUAL,
),
listOf("FEmusic_history", "FEmusic_offline"),
)

View File

@@ -1,19 +0,0 @@
package app.revanced.patches.music.layout.premium.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object HideGetPremiumParentFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
Opcode.IGET_BOOLEAN,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC
),
listOf("FEmusic_history"),
)

View File

@@ -1,71 +1,84 @@
package app.revanced.patches.music.layout.upgradebutton
import app.revanced.patcher.data.BytecodeContext
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.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.newLabel
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.music.layout.upgradebutton.fingerprints.PivotBarConstructorFingerprint
import app.revanced.util.exception
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch(
name = "Remove upgrade button",
description = "Removes the upgrade tab from the pivot bar.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
)
@Suppress("unused")
object RemoveUpgradeButtonPatch : BytecodePatch(
setOf(PivotBarConstructorFingerprint)
setOf(PivotBarConstructorFingerprint),
) {
override fun execute(context: BytecodeContext) {
val result = PivotBarConstructorFingerprint.result!!
val implementation = result.mutableMethod.implementation!!
PivotBarConstructorFingerprint.result?.let {
it.mutableMethod.apply {
val pivotBarElementFieldReference = getInstruction(it.scanResult.patternScanResult!!.endIndex - 1)
.getReference<FieldReference>()
val pivotBarElementFieldRef =
(implementation.instructions[result.scanResult.patternScanResult!!.endIndex - 1] as Instruction22c).reference
val register = (getInstructions().first() as Instruction35c).registerC
val register = (implementation.instructions.first() as Instruction35c).registerC
// first compile all the needed instructions
val instructionList = """
invoke-interface { v0 }, Ljava/util/List;->size()I
move-result v1
const/4 v2, 0x4
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
iput-object v0, v$register, $pivotBarElementFieldRef
""".toInstructions().toMutableList()
// First compile all the needed instructions.
val instructionList = """
invoke-interface { v0 }, Ljava/util/List;->size()I
move-result v1
const/4 v2, 0x4
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
iput-object v0, v$register, $pivotBarElementFieldReference
""".toInstructions().toMutableList()
val endIndex = it.scanResult.patternScanResult!!.endIndex
val endIndex = result.scanResult.patternScanResult!!.endIndex
// Replace the instruction to retain the label at given index.
replaceInstruction(
endIndex - 1,
instructionList[0], // invoke-interface.
)
// Do not forget to remove this instruction since we added it already.
instructionList.removeFirst()
// replace the instruction to retain the label at given index
implementation.replaceInstruction(
endIndex - 1, instructionList[0] // invoke-interface
)
// do not forget to remove this instruction since we added it already
instructionList.removeFirst()
val exitInstruction = instructionList.last() // iput-object
addInstruction(
endIndex,
exitInstruction,
)
// Do not forget to remove this instruction since we added it already.
instructionList.removeLast()
val exitInstruction = instructionList.last() // iput-object
implementation.addInstruction(
endIndex, exitInstruction
)
// do not forget to remove this instruction since we added it already
instructionList.removeLast()
// Add the necessary if statement to remove the upgrade tab button in case it exists.
instructionList.add(
2, // if-le.
BuilderInstruction22t(
Opcode.IF_LE,
1,
2,
newLabel(endIndex),
),
)
// add the necessary if statement to remove the upgrade tab button in case it exists
instructionList.add(
2, // if-le
BuilderInstruction22t(
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(endIndex)
)
)
implementation.addInstructions(
endIndex, instructionList
)
addInstructions(
endIndex,
instructionList,
)
}
} ?: throw PivotBarConstructorFingerprint.exception
}
}

View File

@@ -1,62 +1,20 @@
package app.revanced.patches.music.layout.upgradebutton.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
internal object PivotBarConstructorFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf("L", "Z"),
listOf(
Opcode.INVOKE_DIRECT,
Opcode.CONST_4,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.GOTO,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.GOTO,
Opcode.SGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.NOP,
Opcode.IPUT_OBJECT,
Opcode.RETURN_VOID
)
)
Opcode.RETURN_VOID,
),
)

View File

@@ -5,7 +5,6 @@ import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.music.misc.gms.fingerprints.*
import app.revanced.patches.music.misc.integrations.IntegrationsPatch
import app.revanced.patches.music.misc.integrations.fingerprints.ApplicationInitFingerprint
import app.revanced.patches.shared.fingerprints.CastContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
@@ -21,7 +20,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
),
mainActivityOnCreateFingerprint = ApplicationInitFingerprint,
mainActivityOnCreateFingerprint = MusicActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(CompatiblePackage("com.google.android.apps.youtube.music")),
@@ -34,5 +33,5 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
PrimeMethodFingerprint,
),
) {
override val gmsCoreVendor by gmsCoreVendorGroupIdOption
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
}

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object MusicActivityOnCreateFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Landroid/os/Bundle;"),
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.type.endsWith("/MusicActivity;")
}
)

View File

@@ -1,26 +1,12 @@
package app.revanced.patches.music.premium.backgroundplay
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.premium.backgroundplay.fingerprints.BackgroundPlaybackDisableFingerprint
@Patch(
name = "Background play",
description = "Enables playing music in the background.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
)
@Suppress("unused")
object BackgroundPlayPatch : BytecodePatch(setOf(BackgroundPlaybackDisableFingerprint)) {
override fun execute(context: BytecodeContext) =
BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions(
0, """
const/4 v0, 0x1
return v0
"""
) ?: throw BackgroundPlaybackDisableFingerprint.exception
}
import app.revanced.patches.music.layout.minimizedplayback.MinimizedPlaybackPatch
@Deprecated("This patch has been merged into MinimizedPlaybackPatch.")
object BackgroundPlayPatch : BytecodePatch(
dependencies = setOf(MinimizedPlaybackPatch::class),
) {
override fun execute(context: BytecodeContext) {
}
}

View File

@@ -1,43 +0,0 @@
package app.revanced.patches.music.premium.backgroundplay.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
internal object BackgroundPlaybackDisableFingerprint : MethodFingerprint(
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET,
Opcode.AND_INT_LIT16,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.GOTO,
Opcode.SGET_OBJECT,
Opcode.GOTO,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ,
Opcode.CONST_4,
Opcode.RETURN,
Opcode.RETURN,
Opcode.RETURN
)
)

View File

@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.gms
import app.revanced.patcher.PatchClass
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
@@ -12,7 +12,7 @@ import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.AC
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.AUTHORITIES
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.PERMISSIONS
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_METHOD_NAME
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.returnEarly
@@ -32,7 +32,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
* @param primeMethodFingerprint The fingerprint of the "prime" method that needs to be patched.
* @param earlyReturnFingerprints The fingerprints of methods that need to be returned early.
* @param mainActivityOnCreateFingerprint The fingerprint of the main activity's onCreate method.
* @param mainActivityOnCreateFingerprint The fingerprint of the main activity onCreate method.
* @param integrationsPatchDependency The patch responsible for the integrations.
* @param gmsCoreSupportResourcePatch The corresponding resource patch that is used to patch the resources.
* @param dependencies Additional dependencies of this patch.
@@ -60,7 +60,10 @@ abstract class BaseGmsCoreSupportPatch(
integrationsPatchDependency,
) + dependencies,
compatiblePackages = compatiblePackages,
fingerprints = setOf(GmsCoreSupportFingerprint, mainActivityOnCreateFingerprint) + fingerprints,
fingerprints = setOf(
GmsCoreSupportFingerprint,
mainActivityOnCreateFingerprint,
) + fingerprints,
requiresIntegrations = true,
) {
init {
@@ -68,7 +71,7 @@ abstract class BaseGmsCoreSupportPatch(
gmsCoreSupportResourcePatch.options.values.forEach(options::register)
}
internal abstract val gmsCoreVendor: String?
internal abstract val gmsCoreVendorGroupId: String?
override fun execute(context: BytecodeContext) {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
@@ -93,16 +96,17 @@ abstract class BaseGmsCoreSupportPatch(
// Return these methods early to prevent the app from crashing.
earlyReturnFingerprints.toList().returnEarly()
// Check the availability of GmsCore.
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstruction(
1, // Hack to not disturb other patches (such as the integrations patch).
"invoke-static {}, Lapp/revanced/integrations/youtube/patches/GmsCoreSupport;->checkAvailability()V",
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
0,
"invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" +
"checkGmsCore(Landroid/app/Activity;)V",
) ?: throw mainActivityOnCreateFingerprint.exception
// Change the vendor of GmsCore in ReVanced Integrations.
GmsCoreSupportFingerprint.result?.mutableClass?.methods
?.single { it.name == GET_GMS_CORE_VENDOR_METHOD_NAME }
?.replaceInstruction(0, "const-string v0, \"$gmsCoreVendor\"")
?.single { it.name == GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME }
?.replaceInstruction(0, "const-string v0, \"$gmsCoreVendorGroupId\"")
?: throw GmsCoreSupportFingerprint.exception
}
@@ -146,10 +150,10 @@ abstract class BaseGmsCoreSupportPatch(
in PERMISSIONS,
in ACTIONS,
in AUTHORITIES,
-> referencedString.replace("com.google", gmsCoreVendor!!)
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
// No vendor prefix for whatever reason...
"subscribedfeeds" -> "$gmsCoreVendor.subscribedfeeds"
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
else -> null
}
@@ -162,7 +166,7 @@ abstract class BaseGmsCoreSupportPatch(
if (str.startsWith(uriPrefix)) {
return str.replace(
uriPrefix,
"content://${authority.replace("com.google", gmsCoreVendor!!)}",
"content://${authority.replace("com.google", gmsCoreVendorGroupId!!)}",
)
}
}
@@ -170,7 +174,7 @@ abstract class BaseGmsCoreSupportPatch(
// gms also has a 'subscribedfeeds' authority, check for that one too
val subFeedsUriPrefix = "content://subscribedfeeds"
if (str.startsWith(subFeedsUriPrefix)) {
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendor.subscribedfeeds")
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendorGroupId.subscribedfeeds")
}
}

View File

@@ -121,7 +121,6 @@ abstract class BaseGmsCoreSupportResourcePatch(
}
private companion object {
private const val VANCED_VENDOR = "com.mgoogle"
private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$"
}
}

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
internal object GmsCoreSupportFingerprint : MethodFingerprint(
customFingerprint = { _, classDef ->
classDef.type.endsWith("GmsCoreSupport;")
}
},
) {
const val GET_GMS_CORE_VENDOR_METHOD_NAME = "getGmsCoreVendor"
const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId"
}

View File

@@ -0,0 +1,120 @@
package app.revanced.patches.shared.misc.hex
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.RawResourcePatch
import kotlin.math.max
abstract class BaseHexPatch : RawResourcePatch() {
internal abstract val replacements: List<Replacement>
override fun execute(context: ResourceContext) {
replacements.groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) ->
val targetFile = try {
context[targetFilePath, true]
} catch (e: Exception) {
throw PatchException("Could not find target file: $targetFilePath")
}
// TODO: Use a file channel to read and write the file instead of reading the whole file into memory,
// in order to reduce memory usage.
val targetFileBytes = targetFile.readBytes()
replacements.forEach { replacement ->
replacement.replacePattern(targetFileBytes)
}
targetFile.writeBytes(targetFileBytes)
}
}
/**
* Represents a pattern to search for and its replacement pattern.
*
* @property pattern The pattern to search for.
* @property replacementPattern The pattern to replace the [pattern] with.
* @property targetFilePath The path to the file to make the changes in relative to the APK root.
*/
class Replacement(
private val pattern: String,
replacementPattern: String,
internal val targetFilePath: String,
) {
private val patternBytes = pattern.toByteArrayPattern()
private val replacementPattern = replacementPattern.toByteArrayPattern()
init {
if (this.patternBytes.size != this.replacementPattern.size) {
throw PatchException("Pattern and replacement pattern must have the same length: $pattern")
}
}
/**
* Replaces the [patternBytes] with the [replacementPattern] in the [targetFileBytes].
*
* @param targetFileBytes The bytes of the file to make the changes in.
*/
fun replacePattern(targetFileBytes: ByteArray) {
val startIndex = indexOfPatternIn(targetFileBytes)
if (startIndex == -1) {
throw PatchException("Pattern not found in target file: $pattern")
}
replacementPattern.copyInto(targetFileBytes, startIndex)
}
// TODO: Allow searching in a file channel instead of a byte array to reduce memory usage.
/**
* Returns the index of the first occurrence of [patternBytes] in the haystack
* using the Boyer-Moore algorithm.
*
* @param haystack The array to search in.
*
* @return The index of the first occurrence of the [patternBytes] in the haystack or -1
* if the [patternBytes] is not found.
*/
private fun indexOfPatternIn(haystack: ByteArray): Int {
val needle = patternBytes
val haystackLength = haystack.size - 1
val needleLength = needle.size - 1
val right = IntArray(256) { -1 }
for (i in 0 until needleLength) right[needle[i].toInt().and(0xFF)] = i
var skip: Int
for (i in 0..haystackLength - needleLength) {
skip = 0
for (j in needleLength - 1 downTo 0)
if (needle[j] != haystack[i + j]) {
skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)])
break
}
if (skip == 0) return i
}
return -1
}
companion object {
/**
* Convert a string representing a pattern of hexadecimal bytes to a byte array.
*
* @return The byte array representing the pattern.
* @throws PatchException If the pattern is invalid.
*/
private fun String.toByteArrayPattern() = try {
split(" ").map { it.toInt(16).toByte() }.toByteArray()
} catch (e: NumberFormatException) {
throw PatchException(
"Could not parse pattern: $this. A pattern is a sequence of case insensitive strings " +
"representing hexadecimal bytes separated by spaces",
e,
)
}
}
}
}

View File

@@ -2,17 +2,21 @@ package app.revanced.patches.shared.misc.integrations
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint.IRegisterResolver
import app.revanced.patches.shared.misc.integrations.fingerprints.ReVancedUtilsPatchesVersionFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import java.util.jar.JarFile
abstract class BaseIntegrationsPatch(
private val hooks: Set<IntegrationsFingerprint>,
) : BytecodePatch(hooks) {
) : BytecodePatch(hooks + setOf(ReVancedUtilsPatchesVersionFingerprint)) {
@Deprecated(
"Use the constructor without the integrationsDescriptor parameter",
@@ -34,6 +38,46 @@ abstract class BaseIntegrationsPatch(
hooks.forEach { hook ->
hook.invoke(INTEGRATIONS_CLASS_DESCRIPTOR)
}
// Modify Utils method to include the patches release version version.
ReVancedUtilsPatchesVersionFingerprint.resultOrThrow().mutableMethod.apply {
val manifestValue = getPatchesManifestEntry("Version")
addInstructions(
0,
"""
const-string v0, "$manifestValue"
return-object v0
""",
)
}
}
/**
* @return The value for the manifest entry,
* or "Unknown" if the entry does not exist or is blank.
*/
@Suppress("SameParameterValue")
private fun getPatchesManifestEntry(attributeKey: String) = JarFile(getCurrentJarFilePath()).use { jarFile ->
jarFile.manifest.mainAttributes.entries.firstOrNull { it.key.toString() == attributeKey }?.value?.toString()
?: "Unknown"
}
/**
* @return The file path for the jar this classfile is contained inside.
*/
private fun getCurrentJarFilePath(): String {
val className = object {}::class.java.enclosingClass.name.replace('.', '/') + ".class"
val classUrl = object {}::class.java.classLoader.getResource(className)
if (classUrl != null) {
val urlString = classUrl.toString()
if (urlString.startsWith("jar:file:")) {
val end = urlString.indexOf('!')
return urlString.substring("jar:file:".length, end)
}
}
throw IllegalStateException("Not running from inside a JAR file.")
}
/**
@@ -50,7 +94,7 @@ abstract class BaseIntegrationsPatch(
strings: Iterable<String>? = null,
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
private val insertIndexResolver: ((Method) -> Int) = object : IHookInsertIndexResolver {},
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {}
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
) : MethodFingerprint(
returnType,
accessFlags,
@@ -59,9 +103,11 @@ abstract class BaseIntegrationsPatch(
strings,
customFingerprint,
) {
@Deprecated("Previous constructor that is missing the insert index." +
@Deprecated(
"Previous constructor that is missing the insert index." +
"Here only for binary compatibility, " +
"and this can be removed after the next major version update.")
"and this can be removed after the next major version update.",
)
constructor(
returnType: String? = null,
accessFlags: Int? = null,
@@ -69,7 +115,7 @@ abstract class BaseIntegrationsPatch(
opcodes: Iterable<Opcode?>? = null,
strings: Iterable<String>? = null,
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {}
contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
) : this(
returnType,
accessFlags,
@@ -78,7 +124,7 @@ abstract class BaseIntegrationsPatch(
strings,
customFingerprint,
object : IHookInsertIndexResolver {},
contextRegisterResolver
contextRegisterResolver,
)
fun invoke(integrationsDescriptor: String) {
@@ -103,7 +149,7 @@ abstract class BaseIntegrationsPatch(
}
}
private companion object {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
internal companion object {
internal const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.shared.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
import com.android.tools.smali.dexlib2.AccessFlags
internal object ReVancedUtilsPatchesVersionFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
returnType = "Ljava/lang/String;",
parameters = listOf(),
customFingerprint = { methodDef, classDef ->
methodDef.name == "getPatchesReleaseVersion" &&
classDef.type == BaseIntegrationsPatch.INTEGRATIONS_CLASS_DESCRIPTOR
}
)

View File

@@ -16,10 +16,19 @@ import org.w3c.dom.Document
@Suppress("MemberVisibilityCanBePrivate")
class NonInteractivePreference(
key: String,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
tag: String = "Preference",
val selectable: Boolean = false
) : BasePreference(null, "${key}_title", summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, tag) {
@Deprecated("Here only for binary compatibility, and should be removed after the next major version update.")
constructor(
key: String,
summaryKey: String? = "${key}_summary",
tag: String = "Preference",
selectable: Boolean = false
) : this(key, "${key}_title", summaryKey, tag, selectable)
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -1,17 +1,16 @@
package app.revanced.patches.strava.subscription
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.strava.subscription.fingerprints.GetSubscribedFingerprint
import app.revanced.util.exception
@Patch(
name = "Unlock subscription features",
description = "Unlocks \"Routes\", \"Matched Runs\" and \"Segment Efforts\".",
compatiblePackages = [CompatiblePackage("com.strava", ["320.12"])]
compatiblePackages = [CompatiblePackage("com.strava")],
)
@Suppress("unused")
object UnlockSubscriptionPatch : BytecodePatch(setOf(GetSubscribedFingerprint)) {

View File

@@ -1,9 +1,12 @@
package app.revanced.patches.tumblr.annoyances.popups.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// This method is responsible for loading and displaying the visual Layout of the Gift Message Popup.
internal object ShowGiftMessagePopupFingerprint : MethodFingerprint(
strings = listOf("activity", "anchorView"),
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("GiftMessagePopup;") }
strings = listOf("activity", "anchorView", "textMessage"),
returnType = "V",
accessFlags = AccessFlags.FINAL or AccessFlags.PUBLIC
)

View File

@@ -28,6 +28,9 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
internal lateinit var addOverride: (name: String, value: String) -> Unit private set
override fun execute(context: BytecodeContext) = GetFeatureValueFingerprint.result?.let {
val configurationClass = it.method.definingClass
val featureClass = it.method.parameterTypes[0].toString()
// The method we want to inject into does not have enough registers, so we inject a helper method
// and inject more instructions into it later, see addOverride.
// This is not in an integration since the unused variable would get compiled away and the method would
@@ -35,7 +38,7 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
val helperMethod = ImmutableMethod(
it.method.definingClass,
"getValueOverride",
listOf(ImmutableMethodParameter("Lcom/tumblr/configuration/Feature;", null, "feature")),
listOf(ImmutableMethodParameter(featureClass, null, "feature")),
"Ljava/lang/String;",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null,
@@ -50,7 +53,7 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
0,
"""
# toString() the enum value
invoke-virtual {p1}, Lcom/tumblr/configuration/Feature;->toString()Ljava/lang/String;
invoke-virtual {p1}, $featureClass->toString()Ljava/lang/String;
move-result-object v0
# !!! If you add more instructions above this line, update helperInsertIndex below!
@@ -75,7 +78,7 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
getFeatureIndex,
"""
# Call the Helper Method with the Feature
invoke-virtual {p0, p1}, Lcom/tumblr/configuration/Configuration;->getValueOverride(Lcom/tumblr/configuration/Feature;)Ljava/lang/String;
invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String;
move-result-object v0
# If it returned null, skip
if-eqz v0, :is_null

View File

@@ -1,6 +1,8 @@
package app.revanced.patches.tumblr.featureflags.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
// This fingerprint targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature".
@@ -19,5 +21,7 @@ internal object GetFeatureValueFingerprint : MethodFingerprint(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT
),
customFingerprint = { method, _ -> method.definingClass == "Lcom/tumblr/configuration/Configuration;" }
returnType = "Ljava/lang/String;",
parameters = listOf("L", "Z"),
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL
)

View File

@@ -0,0 +1,67 @@
package app.revanced.patches.tumblr.fixes
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.tumblr.fixes.fingerprints.AddQueryParamFingerprint
import app.revanced.patches.tumblr.fixes.fingerprints.HttpPathParserFingerprint
import app.revanced.util.exception
@Patch(
name = "Fix old versions",
description = "Fixes old versions of the app (v33.2 and earlier) breaking due to Tumblr removing remnants of Tumblr" +
" Live from the API, which causes many requests to fail. This patch has no effect on newer versions of the app.",
compatiblePackages = [CompatiblePackage("com.tumblr")],
use = false,
)
@Suppress("unused")
object FixOldVersionsPatch : BytecodePatch(
setOf(HttpPathParserFingerprint, AddQueryParamFingerprint),
) {
override fun execute(context: BytecodeContext) {
val liveQueryParameters = listOf(
",?live_now",
",?live_streaming_user_id",
)
HttpPathParserFingerprint.result?.let {
val endIndex = it.scanResult.patternScanResult!!.endIndex
// Remove the live query parameters from the path when it's specified via a @METHOD annotation.
for (liveQueryParameter in liveQueryParameters) {
it.mutableMethod.addInstructions(
endIndex + 1,
"""
# urlPath = urlPath.replace(liveQueryParameter, "")
const-string p1, "$liveQueryParameter"
const-string p3, ""
invoke-virtual {p2, p1, p3}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
move-result-object p2
""",
)
}
} ?: throw HttpPathParserFingerprint.exception
AddQueryParamFingerprint.result?.let {
// Remove the live query parameters when passed via a parameter which has the @Query annotation.
// e.g. an API call could be defined like this:
// @GET("api/me/info")
// ApiResponse getCurrentUserInfo(@Query("fields[blog]") String value)
// which would result in the path "api/me/inf0?fields[blog]=${value}"
// Here we make sure that this value doesn't contain the broken query parameters.
for (liveQueryParameter in liveQueryParameters) {
it.mutableMethod.addInstructions(
0,
"""
# queryParameterValue = queryParameterValue.replace(liveQueryParameter, "")
const-string v0, "$liveQueryParameter"
const-string v1, ""
invoke-virtual {p2, v0, v1}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
move-result-object p2
""",
)
}
} ?: throw AddQueryParamFingerprint.exception
}
}

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.tumblr.fixes.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
// Fingerprint for the addQueryParam method from retrofit2
// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186
// Injecting here allows modifying dynamically set query parameters
internal object AddQueryParamFingerprint : MethodFingerprint(
strings = listOf("Malformed URL. Base: ", ", Relative: "),
parameters = listOf("Ljava/lang/String;", "Ljava/lang/String;", "Z"),
)

View File

@@ -0,0 +1,15 @@
package app.revanced.patches.tumblr.fixes.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
// Fingerprint for the parseHttpMethodAndPath method from retrofit2
// https://github.com/square/retrofit/blob/ebf87b10997e2136af4d335276fa950221852c64/retrofit/src/main/java/retrofit2/RequestFactory.java#L270-L302
// Injecting here allows modifying the path/query params of API endpoints defined via annotations
internal object HttpPathParserFingerprint : MethodFingerprint(
strings = listOf("Only one HTTP method is allowed. Found: %s and %s."),
opcodes = listOf(
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,
),
)

View File

@@ -8,11 +8,11 @@ import app.revanced.patches.tumblr.featureflags.OverrideFeatureFlagsPatch
import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
@Patch(
name = "Disable Tumblr Live",
description = "Disable the Tumblr Live tab button and dashboard carousel.",
dependencies = [OverrideFeatureFlagsPatch::class, TimelineFilterPatch::class],
compatiblePackages = [CompatiblePackage("com.tumblr")],
)
@Deprecated("Tumblr Live was removed and is no longer served in the feed, making this patch useless.")
@Suppress("unused")
object DisableTumblrLivePatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) {

View File

@@ -0,0 +1,29 @@
package app.revanced.patches.twitter.misc.links
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.twitter.misc.links.fingerprints.SanitizeSharingLinksFingerprint
import app.revanced.util.exception
@Patch(
name = "Sanitize sharing links",
description = "Removes the tracking query parameters from links before they are shared.",
compatiblePackages = [CompatiblePackage("com.twitter.android")],
)
object SanitizeSharingLinksPatch : BytecodePatch(
setOf(SanitizeSharingLinksFingerprint),
) {
override fun execute(context: BytecodeContext) {
SanitizeSharingLinksFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
# Method takes in a link (string, param 0) and then appends the tracking query params,
# so all we need to do is return back the passed-in string
return-object p0
""",
) ?: throw SanitizeSharingLinksFingerprint.exception
}
}

View File

@@ -0,0 +1,8 @@
package app.revanced.patches.twitter.misc.links.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object SanitizeSharingLinksFingerprint : MethodFingerprint(
strings = listOf("<this>", "shareParam", "sessionToken"),
returnType = "Ljava/lang/String;",
)

View File

@@ -30,7 +30,7 @@ object HideAdsResourcePatch : ResourcePatch() {
SwitchPreference("revanced_hide_general_ads"),
SwitchPreference("revanced_hide_fullscreen_ads"),
SwitchPreference("revanced_hide_buttoned_ads"),
SwitchPreference("revanced_hide_paid_content_ads"),
SwitchPreference("revanced_hide_paid_promotion_label"),
SwitchPreference("revanced_hide_self_sponsor_ads"),
SwitchPreference("revanced_hide_products_banner"),
SwitchPreference("revanced_hide_shopping_links"),

View File

@@ -14,7 +14,7 @@ import app.revanced.util.resultOrThrow
@Patch(
name = "Downloads",
description = "Adds support to download videos with an external downloader app" +
description = "Adds support to download videos with an external downloader app " +
"using the in-app download button or a video player action button.",
dependencies = [
DownloadsResourcePatch::class,

View File

@@ -10,10 +10,10 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.*
import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.injectHook
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.ANDROID_AUTOMOTIVE_STRING
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.AddCreateButtonViewFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@@ -24,8 +24,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
ResolvePivotBarFingerprintsPatch::class,
AddResourcesPatch::class,
NavigationBarHookPatch::class,
],
compatiblePackages = [
CompatiblePackage(
@@ -47,7 +47,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.37"
"19.09.37",
],
),
],
@@ -76,53 +76,7 @@ object NavigationButtonsPatch : BytecodePatch(
),
)
/*
* Resolve fingerprints
*/
val initializeButtonsResult = InitializeButtonsFingerprint.result!!
val fingerprintResults =
arrayOf(PivotBarEnumFingerprint, PivotBarButtonsViewFingerprint)
.onEach {
if (!it.resolve(
context,
initializeButtonsResult.mutableMethod,
initializeButtonsResult.mutableClass,
)
) {
throw it.exception
}
}
.map { it.result!!.scanResult.patternScanResult!! }
val enumScanResult = fingerprintResults[0]
val buttonViewResult = fingerprintResults[1]
val enumHookInsertIndex = enumScanResult.startIndex + 2
val buttonHookInsertIndex = buttonViewResult.endIndex
/*
* Inject hooks
*/
val enumHook = "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->lastNavigationButton:Ljava/lang/Enum;"
val buttonHook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideButton(Landroid/view/View;)V"
// Inject bottom to top to not mess up the indices
mapOf(
buttonHook to buttonHookInsertIndex,
enumHook to enumHookInsertIndex,
).forEach { (hook, insertIndex) ->
initializeButtonsResult.mutableMethod.injectHook(hook, insertIndex)
}
/*
* Hide create or switch it with notifications buttons.
*/
// Switch create with notifications button.
AddCreateButtonViewFingerprint.result?.let {
it.mutableMethod.apply {
val stringIndex = it.scanResult.stringsScanResult!!.matches.find { match ->
@@ -130,7 +84,8 @@ object NavigationButtonsPatch : BytecodePatch(
}!!.index
val conditionalCheckIndex = stringIndex - 1
val conditionRegister = getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
val conditionRegister =
getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
addInstructions(
conditionalCheckIndex,
@@ -142,26 +97,7 @@ object NavigationButtonsPatch : BytecodePatch(
}
} ?: throw AddCreateButtonViewFingerprint.exception
/*
* Resolve fingerprints
*/
InitializeButtonsFingerprint.result!!.let {
if (!PivotBarCreateButtonViewFingerprint.resolve(context, it.mutableMethod, it.mutableClass)) {
throw PivotBarCreateButtonViewFingerprint.exception
}
}
PivotBarCreateButtonViewFingerprint.result!!.apply {
val insertIndex = scanResult.patternScanResult!!.endIndex
/*
* Inject hooks
*/
val hook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideCreateButton(Landroid/view/View;)V"
mutableMethod.injectHook(hook, insertIndex)
}
// Hook navigation button created, in order to hide them.
NavigationBarHookPatch.hookNavigationButtonCreated(INTEGRATIONS_CLASS_DESCRIPTOR)
}
}

View File

@@ -1,37 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.navigation
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.InitializeButtonsFingerprint
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.PivotBarConstructorFingerprint
@Patch(
description = "Resolves necessary fingerprints.",
dependencies = [ResourceMappingPatch::class]
)
internal object ResolvePivotBarFingerprintsPatch : BytecodePatch(
setOf(PivotBarConstructorFingerprint)
) {
internal var imageOnlyTabResourceId: Long = -1
override fun execute(context: BytecodeContext) {
// imageOnlyTabResourceId is used in InitializeButtonsFingerprint fingerprint
ResourceMappingPatch.resourceMappings.find { it.type == "layout" && it.name == "image_only_tab" }
?.let { imageOnlyTabResourceId = it.id } ?: throw PatchException("Failed to find resource")
PivotBarConstructorFingerprint.result?.let {
// Resolve InitializeButtonsFingerprint on the class of the method
// which PivotBarConstructorFingerprint resolved to
if (!InitializeButtonsFingerprint.resolve(
context,
it.classDef
)
) throw InitializeButtonsFingerprint.exception
} ?: throw PivotBarConstructorFingerprint.exception
}
}

View File

@@ -1,12 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PivotBarButtonsViewFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL_RANGE,
Opcode.MOVE_RESULT_OBJECT, // target reference
Opcode.GOTO,
)
)

View File

@@ -1,13 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PivotBarCreateButtonViewFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_DIRECT_RANGE,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC
)
)

View File

@@ -1,15 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PivotBarEnumFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_NEZ, // target reference
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
)
)

View File

@@ -1,30 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.navigation.utils
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import com.android.tools.smali.dexlib2.Opcode.MOVE_RESULT_OBJECT
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal object InjectionUtils {
const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX"
/**
* Injects an instruction into insertIndex of the hook.
* @param hook The hook to insert.
* @param insertIndex The index to insert the instruction at.
* [MOVE_RESULT_OBJECT] has to be the previous instruction before [insertIndex].
*/
fun MutableMethod.injectHook(hook: String, insertIndex: Int) {
val injectTarget = this
// Register to pass to the hook
val registerIndex = insertIndex - 1 // MOVE_RESULT_OBJECT is always the previous instruction
val register = injectTarget.getInstruction<OneRegisterInstruction>(registerIndex).registerA
injectTarget.addInstruction(
insertIndex,
hook.replace("REGISTER_INDEX", register.toString()),
)
}
}

View File

@@ -1,73 +1,13 @@
package app.revanced.patches.youtube.layout.hide.breakingnews
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.layout.hide.breakingnews.fingerprints.BreakingNewsFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch
@Patch(
name = "Hide breaking news shelf",
description = "Adds an option to hide the breaking news shelf on the homepage tab.",
dependencies = [
IntegrationsPatch::class,
BreakingNewsResourcePatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.37"
]
)
]
)
@Suppress("unused")
@Deprecated("This patch has been merged to HideLayoutComponentsPatch.")
object BreakingNewsPatch : BytecodePatch(
setOf(BreakingNewsFingerprint)
dependencies = setOf(HideLayoutComponentsPatch::class),
) {
override fun execute(context: BytecodeContext) {
BreakingNewsFingerprint.result?.let {
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 1
val moveResultIndex = insertIndex - 1
it.mutableMethod.apply {
val breakingNewsViewRegister =
getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
addInstruction(
insertIndex,
"""
invoke-static {v$breakingNewsViewRegister},
Lapp/revanced/integrations/youtube/patches/HideBreakingNewsPatch;
->
hideBreakingNews(Landroid/view/View;)V
"""
)
}
} ?: throw BreakingNewsFingerprint.exception
}
}

View File

@@ -1,32 +0,0 @@
package app.revanced.patches.youtube.layout.hide.breakingnews
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.settings.SettingsPatch
@Patch(
dependencies = [
SettingsPatch::class,
ResourceMappingPatch::class,
AddResourcesPatch::class
],
)
internal object BreakingNewsResourcePatch : ResourcePatch() {
internal var horizontalCardListId: Long = -1
override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.FEED.addPreferences(
SwitchPreference("revanced_hide_breaking_news")
)
horizontalCardListId = ResourceMappingPatch.resourceMappings.single {
it.type == "layout" && it.name == "horizontal_card_list"
}.id
}
}

View File

@@ -1,20 +0,0 @@
package app.revanced.patches.youtube.layout.hide.breakingnews.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.hide.breakingnews.BreakingNewsResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object BreakingNewsFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IPUT_OBJECT,
),
literalSupplier = { BreakingNewsResourcePatch.horizontalCardListId }
)

View File

@@ -55,9 +55,10 @@ object CommentsPatch : ResourcePatch() {
PreferenceScreen(
"revanced_comments_screen",
preferences = setOf(
SwitchPreference("revanced_hide_comments_section"),
SwitchPreference("revanced_hide_preview_comment")
)
SwitchPreference("revanced_hide_preview_comment"),
SwitchPreference("revanced_hide_comments_section")
),
sorting = PreferenceScreen.Sorting.UNSORTED
)
)

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.layout.hide.general
import app.revanced.patcher.data.BytecodeContext
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
@@ -11,19 +12,19 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.*
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.hide.general.fingerprints.HideShowMoreButtonFingerprint
import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElementFromBufferFingerprint
import app.revanced.patches.youtube.layout.hide.general.fingerprints.PlayerOverlayFingerprint
import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowWatermarkFingerprint
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch(
@@ -33,6 +34,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
LithoFilterPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
HideLayoutComponentsResourcePatch::class,
NavigationBarHookPatch::class,
],
compatiblePackages = [
CompatiblePackage(
@@ -61,7 +64,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
)
@Suppress("unused")
object HideLayoutComponentsPatch : BytecodePatch(
setOf(ParseElementFromBufferFingerprint, PlayerOverlayFingerprint),
setOf(ParseElementFromBufferFingerprint, PlayerOverlayFingerprint, HideShowMoreButtonFingerprint),
) {
private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/components/LayoutComponentsFilter;"
@@ -69,6 +72,8 @@ object HideLayoutComponentsPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;"
private const val CUSTOM_FILTER_CLASS_NAME =
"Lapp/revanced/integrations/youtube/patches/components/CustomFilter;"
private const val KEYWORD_FILTER_CLASS_NAME =
"Lapp/revanced/integrations/youtube/patches/components/KeywordContentFilter;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@@ -96,6 +101,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
SwitchPreference("revanced_hide_info_panels"),
SwitchPreference("revanced_hide_join_membership_button"),
SwitchPreference("revanced_hide_medical_panels"),
SwitchPreference("revanced_hide_playables"),
SwitchPreference("revanced_hide_quick_actions"),
SwitchPreference("revanced_hide_related_videos"),
SwitchPreference("revanced_hide_subscribers_community_guidelines"),
@@ -108,6 +114,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
SwitchPreference("revanced_hide_compact_banner"),
SwitchPreference("revanced_hide_feed_survey"),
SwitchPreference("revanced_hide_for_you_shelf"),
SwitchPreference("revanced_hide_horizontal_shelves"),
SwitchPreference("revanced_hide_image_shelf"),
SwitchPreference("revanced_hide_latest_posts_ads"),
SwitchPreference("revanced_hide_mix_playlists"),
@@ -115,6 +122,18 @@ object HideLayoutComponentsPatch : BytecodePatch(
SwitchPreference("revanced_hide_notify_me_button"),
SwitchPreference("revanced_hide_search_result_recommendations"),
SwitchPreference("revanced_hide_search_result_shelf_header"),
SwitchPreference("revanced_hide_show_more_button"),
PreferenceScreen(
key = "revanced_hide_keyword_content_screen",
sorting = Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_hide_keyword_content_home"),
SwitchPreference("revanced_hide_keyword_content_subscriptions"),
SwitchPreference("revanced_hide_keyword_content_search"),
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
NonInteractivePreference("revanced_hide_keyword_content_about"),
),
),
)
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
@@ -136,19 +155,19 @@ object HideLayoutComponentsPatch : BytecodePatch(
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME)
LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME)
LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_NAME)
// region Mix playlists
ParseElementFromBufferFingerprint.result?.let { result ->
val returnEmptyComponentInstruction =
result.mutableMethod.getInstructions().last { it.opcode == Opcode.INVOKE_STATIC }
ParseElementFromBufferFingerprint.resultOrThrow().let { result ->
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex
result.mutableMethod.apply {
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex
val conversionContextRegister =
getInstruction<TwoRegisterInstruction>(consumeByteBufferIndex - 2).registerA
val byteBufferRegister = getInstruction<FiveRegisterInstruction>(consumeByteBufferIndex).registerD
val returnEmptyComponentInstruction = getInstructions().last { it.opcode == Opcode.INVOKE_STATIC }
addInstructionsWithLabels(
consumeByteBufferIndex,
@@ -160,15 +179,15 @@ object HideLayoutComponentsPatch : BytecodePatch(
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
)
}
} ?: throw ParseElementFromBufferFingerprint.exception
}
// endregion
// region Watermark (legacy code for old versions of YouTube)
ShowWatermarkFingerprint.also {
it.resolve(context, PlayerOverlayFingerprint.result?.classDef ?: throw PlayerOverlayFingerprint.exception)
}.result?.mutableMethod?.apply {
it.resolve(context, PlayerOverlayFingerprint.resultOrThrow().classDef)
}.resultOrThrow().mutableMethod.apply {
val index = implementation!!.instructions.size - 5
removeInstruction(index)
@@ -179,7 +198,26 @@ object HideLayoutComponentsPatch : BytecodePatch(
move-result p2
""",
)
} ?: throw ShowWatermarkFingerprint.exception
}
// endregion
// region Show more button
HideShowMoreButtonFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val moveRegisterIndex = it.scanResult.patternScanResult!!.endIndex
val viewRegister =
getInstruction<OneRegisterInstruction>(moveRegisterIndex).registerA
val insertIndex = moveRegisterIndex + 1
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, " +
"$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideShowMoreButton(Landroid/view/View;)V",
)
}
}
// endregion
}

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.hide.loadmorebutton
package app.revanced.patches.youtube.layout.hide.general
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
@@ -15,16 +15,10 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
AddResourcesPatch::class
]
)
internal object HideLoadMoreButtonResourcePatch : ResourcePatch() {
internal object HideLayoutComponentsResourcePatch : ResourcePatch() {
internal var expandButtonDownId: Long = -1
override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.FEED.addPreferences(
SwitchPreference("revanced_hide_load_more_button")
)
expandButtonDownId = ResourceMappingPatch.resourceMappings.single {
it.type == "layout" && it.name == "expand_button_down"
}.id

View File

@@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.hide.general.fingerprints
import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object HideShowMoreButtonFingerprint : LiteralValueFingerprint(
opcodes = listOf(
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT
),
literalSupplier = { HideLayoutComponentsResourcePatch.expandButtonDownId }
)

View File

@@ -1,65 +1,12 @@
package app.revanced.patches.youtube.layout.hide.loadmorebutton
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.layout.hide.loadmorebutton.fingerprints.HideLoadMoreButtonFingerprint
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch
@Patch(
name = "Hide \'Load more\' button",
description = "Adds an option to hide the button under videos that loads similar videos.",
dependencies = [HideLoadMoreButtonResourcePatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.37"
]
)
]
)
@Suppress("unused")
@Deprecated("This patch class has been merged into HideLayoutComponentsPatch.")
object HideLoadMoreButtonPatch : BytecodePatch(
setOf(HideLoadMoreButtonFingerprint)
dependencies = setOf(HideLayoutComponentsPatch::class)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/HideLoadMoreButtonPatch;"
override fun execute(context: BytecodeContext) {
HideLoadMoreButtonFingerprint.result?.let {
it.mutableMethod.apply {
val moveRegisterIndex = it.scanResult.patternScanResult!!.endIndex
val viewRegister =
getInstruction<OneRegisterInstruction>(moveRegisterIndex).registerA
val insertIndex = moveRegisterIndex + 1
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideLoadMoreButton(Landroid/view/View;)V"
)
}
} ?: throw HideLoadMoreButtonFingerprint.exception
}
}
override fun execute(context: BytecodeContext) {}
}

View File

@@ -1,15 +0,0 @@
package app.revanced.patches.youtube.layout.hide.loadmorebutton.fingerprints
import app.revanced.patches.youtube.layout.hide.loadmorebutton.HideLoadMoreButtonResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object HideLoadMoreButtonFingerprint : LiteralValueFingerprint(
opcodes = listOf(
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT
),
literalSupplier = { HideLoadMoreButtonResourcePatch.expandButtonDownId }
)

View File

@@ -46,7 +46,6 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
)
@Suppress("unused")
object HidePlayerFlyoutMenuPatch : ResourcePatch() {
private const val KEY = "revanced_hide_player_flyout"
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/components/PlayerFlyoutMenuItemsFilter;"
@@ -55,21 +54,22 @@ object HidePlayerFlyoutMenuPatch : ResourcePatch() {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = KEY,
preferences = setOf(
SwitchPreference("${KEY}_captions"),
SwitchPreference("${KEY}_additional_settings"),
SwitchPreference("${KEY}_loop_video"),
SwitchPreference("${KEY}_ambient_mode"),
SwitchPreference("${KEY}_report"),
SwitchPreference("${KEY}_help"),
SwitchPreference("${KEY}_speed"),
SwitchPreference("${KEY}_more_info"),
SwitchPreference("${KEY}_audio_track"),
SwitchPreference("${KEY}_watch_in_vr"),
),
)
PreferenceScreen(
key = "revanced_hide_player_flyout",
preferences = setOf(
SwitchPreference("revanced_hide_player_flyout_captions"),
SwitchPreference("revanced_hide_player_flyout_additional_settings"),
SwitchPreference("revanced_hide_player_flyout_loop_video"),
SwitchPreference("revanced_hide_player_flyout_ambient_mode"),
SwitchPreference("revanced_hide_player_flyout_report"),
SwitchPreference("revanced_hide_player_flyout_help"),
SwitchPreference("revanced_hide_player_flyout_speed"),
SwitchPreference("revanced_hide_player_flyout_lock_screen"),
SwitchPreference("revanced_hide_player_flyout_more_info"),
SwitchPreference("revanced_hide_player_flyout_audio_track"),
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
)
)
)
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)

View File

@@ -14,6 +14,7 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.layout.hide.shorts.fingerprints.*
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
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
@@ -25,7 +26,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
IntegrationsPatch::class,
LithoFilterPatch::class,
HideShortsComponentsResourcePatch::class,
ResourceMappingPatch::class
ResourceMappingPatch::class,
NavigationBarHookPatch::class
],
compatiblePackages = [
CompatiblePackage(

View File

@@ -17,22 +17,36 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.SHORTS.addPreferences(
SwitchPreference("revanced_hide_shorts"),
SwitchPreference("revanced_hide_shorts_join_button"),
SwitchPreference("revanced_hide_shorts_subscribe_button"),
SwitchPreference("revanced_hide_shorts_subscribe_button_paused"),
SwitchPreference("revanced_hide_shorts_thanks_button"),
SwitchPreference("revanced_hide_shorts_home"),
SwitchPreference("revanced_hide_shorts_subscriptions"),
SwitchPreference("revanced_hide_shorts_search"),
// Shorts player components.
// Ideally each group should be ordered similar to how they appear in the UI
// since this Setting menu currently uses the ordering used here.
// Vertical row of buttons on right side of the screen.
SwitchPreference("revanced_hide_shorts_like_button"),
SwitchPreference("revanced_hide_shorts_dislike_button"),
SwitchPreference("revanced_hide_shorts_comments_button"),
SwitchPreference("revanced_hide_shorts_remix_button"),
SwitchPreference("revanced_hide_shorts_share_button"),
SwitchPreference("revanced_hide_shorts_info_panel"),
SwitchPreference("revanced_hide_shorts_remix_button"),
SwitchPreference("revanced_hide_shorts_sound_button"),
// Everything else.
SwitchPreference("revanced_hide_shorts_join_button"),
SwitchPreference("revanced_hide_shorts_subscribe_button"),
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),
SwitchPreference("revanced_hide_shorts_save_sound_button"),
SwitchPreference("revanced_hide_shorts_shop_button"),
SwitchPreference("revanced_hide_shorts_tagged_products"),
SwitchPreference("revanced_hide_shorts_search_suggestions"),
SwitchPreference("revanced_hide_shorts_location_label"),
SwitchPreference("revanced_hide_shorts_channel_bar"),
SwitchPreference("revanced_hide_shorts_info_panel"),
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
SwitchPreference("revanced_hide_shorts_video_title"),
SwitchPreference("revanced_hide_shorts_sound_metadata_label"),
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
SwitchPreference("revanced_hide_shorts_sound_button"),
SwitchPreference("revanced_hide_shorts_navigation_bar"),
)

View File

@@ -43,8 +43,10 @@ internal object SponsorBlockResourcePatch : ResourcePatch() {
// required resource for back button, because when the base APK is used, this resource will not exist
"drawable",
"revanced_sb_adjust.xml",
"revanced_sb_backward.xml",
"revanced_sb_compare.xml",
"revanced_sb_edit.xml",
"revanced_sb_forward.xml",
"revanced_sb_logo.xml",
"revanced_sb_publish.xml",
"revanced_sb_voting.xml",

View File

@@ -10,11 +10,14 @@ import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatc
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.seekbar.SeekbarColorBytecodePatch
import app.revanced.patches.youtube.layout.theme.fingerprints.ThemeHelperDarkColorFingerprint
import app.revanced.patches.youtube.layout.theme.fingerprints.ThemeHelperLightColorFingerprint
import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoadingScreenFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
@@ -54,7 +57,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
)
@Suppress("unused")
object ThemeBytecodePatch : BytecodePatch(
setOf(UseGradientLoadingScreenFingerprint)
setOf(
UseGradientLoadingScreenFingerprint,
ThemeHelperLightColorFingerprint,
ThemeHelperDarkColorFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/theme/ThemePatch;"
@@ -121,6 +128,21 @@ object ThemeBytecodePatch : BytecodePatch(
)
} ?: throw UseGradientLoadingScreenFingerprint.exception
mapOf(
ThemeHelperLightColorFingerprint to lightThemeBackgroundColor,
ThemeHelperDarkColorFingerprint to darkThemeBackgroundColor
).forEach { (fingerprint, color) ->
fingerprint.resultOrThrow().mutableMethod.apply {
addInstructions(
0, """
const-string v0, "$color"
return-object v0
"""
)
}
}
LithoColorHookPatch.lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getValue")
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.theme.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import com.android.tools.smali.dexlib2.AccessFlags
internal object ThemeHelperDarkColorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
returnType = "Ljava/lang/String;",
parameters = listOf(),
customFingerprint = { methodDef, classDef ->
methodDef.name == "darkThemeResourceName" &&
classDef.type == SettingsPatch.THEME_HELPER_DESCRIPTOR
}
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.theme.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import com.android.tools.smali.dexlib2.AccessFlags
internal object ThemeHelperLightColorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
returnType = "Ljava/lang/String;",
parameters = listOf(),
customFingerprint = { methodDef, classDef ->
methodDef.name == "lightThemeResourceName" &&
classDef.type == SettingsPatch.THEME_HELPER_DESCRIPTOR
}
)

View File

@@ -22,6 +22,7 @@ import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.reques
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnSucceededFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
@@ -38,6 +39,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
NavigationBarHookPatch::class
],
compatiblePackages = [
CompatiblePackage(
@@ -127,25 +129,45 @@ object AlternativeThumbnailsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
val entries = "revanced_alt_thumbnail_options_entries"
val values = "revanced_alt_thumbnail_options_entry_values"
SettingsPatch.PreferenceScreen.ALTERNATIVE_THUMBNAILS.addPreferences(
NonInteractivePreference(
"revanced_alt_thumbnail_about",
null, // Summary is dynamically updated based on the current settings.
tag = "app.revanced.integrations.youtube.settings.preference.AlternativeThumbnailsStatusPreference",
ListPreference("revanced_alt_thumbnail_home",
summaryKey = null,
entriesKey = entries,
entryValuesKey = values
),
ListPreference("revanced_alt_thumbnail_subscription",
summaryKey = null,
entriesKey = entries,
entryValuesKey = values
),
ListPreference("revanced_alt_thumbnail_library",
summaryKey = null,
entriesKey = entries,
entryValuesKey = values
),
ListPreference("revanced_alt_thumbnail_player",
summaryKey = null,
entriesKey = entries,
entryValuesKey = values
),
ListPreference("revanced_alt_thumbnail_search",
summaryKey = null,
entriesKey = entries,
entryValuesKey = values
),
SwitchPreference("revanced_alt_thumbnail_dearrow"),
SwitchPreference("revanced_alt_thumbnail_dearrow_connection_toast"),
TextPreference("revanced_alt_thumbnail_dearrow_api_url"),
NonInteractivePreference(
"revanced_alt_thumbnail_dearrow_about",
// Custom about preference with link to the DeArrow website.
tag = "app.revanced.integrations.youtube.settings.preference.AlternativeThumbnailsAboutDeArrowPreference",
selectable = true,
),
SwitchPreference("revanced_alt_thumbnail_stills"),
ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null),
SwitchPreference("revanced_alt_thumbnail_stills_fast"),
SwitchPreference("revanced_alt_thumbnail_dearrow_connection_toast"),
TextPreference("revanced_alt_thumbnail_dearrow_api_url"),
NonInteractivePreference("revanced_alt_thumbnail_stills_about"),
SwitchPreference("revanced_alt_thumbnail_stills_fast"),
ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null)
)
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =

View File

@@ -2,16 +2,14 @@ package app.revanced.patches.youtube.misc.announcements
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.shared.fingerprints.MainActivityFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.Opcode
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
import app.revanced.util.resultOrThrow
@Patch(
name = "Announcements",
@@ -21,7 +19,7 @@ import com.android.tools.smali.dexlib2.Opcode
)
@Suppress("unused")
object AnnouncementsPatch : BytecodePatch(
setOf(MainActivityFingerprint)
setOf(MainActivityOnCreateFingerprint)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch;"
@@ -33,16 +31,11 @@ object AnnouncementsPatch : BytecodePatch(
SwitchPreference("revanced_announcements")
)
val onCreateMethod = MainActivityFingerprint.result?.let {
it.mutableClass.methods.find { method -> method.name == "onCreate" }
} ?: throw MainActivityFingerprint.exception
val superCallIndex = onCreateMethod.getInstructions().indexOfFirst { it.opcode == Opcode.INVOKE_SUPER_RANGE }
onCreateMethod.addInstructions(
superCallIndex + 1,
"invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V"
MainActivityOnCreateFingerprint.resultOrThrow().mutableMethod.addInstructions(
// Insert index must be great than the insert index used by GmsCoreSupport,
// as both patch the same method and GmsCore check should be first.
1,
"invoke-static/range { p0 .. p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V"
)
}
}

View File

@@ -14,7 +14,7 @@ import app.revanced.util.exception
@Patch(
name = "Spoof device dimensions",
description = "Adds an option to spoof the device dimensions which unlocks higher video qualities if they aren't available on the device.",
description = "Adds an option to spoof the device dimensions which can unlock higher video qualities.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -9,7 +9,7 @@ import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.youtube.misc.gms.fingerprints.*
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.shared.fingerprints.HomeActivityFingerprint
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
@Suppress("unused")
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
@@ -23,7 +23,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
),
mainActivityOnCreateFingerprint = HomeActivityFingerprint,
mainActivityOnCreateFingerprint = MainActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
dependencies = setOf(
HideCastButtonPatch::class,
@@ -57,5 +57,5 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
PrimeMethodFingerprint,
),
) {
override val gmsCoreVendor by gmsCoreVendorGroupIdOption
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
}

View File

@@ -0,0 +1,157 @@
package app.revanced.patches.youtube.misc.navigation
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.navigation.fingerprints.*
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@Patch(
description = "Hooks the active navigation or search bar.",
dependencies = [
IntegrationsPatch::class,
NavigationBarHookResourcePatch::class,
PlayerTypeHookPatch::class // Required to detect the search bar in all situations.
],
)
@Suppress("unused")
object NavigationBarHookPatch : BytecodePatch(
setOf(
PivotBarConstructorFingerprint,
NavigationEnumFingerprint,
PivotBarButtonsCreateDrawableViewFingerprint,
PivotBarButtonsCreateResourceViewFingerprint,
PivotBarButtonsViewSetSelectedFingerprint,
NavigationBarHookCallbackFingerprint,
MainActivityOnBackPressedFingerprint,
ActionBarSearchResultsFingerprint,
),
) {
internal const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/shared/NavigationBar;"
internal const val INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/shared/NavigationBar\$NavigationButton;"
override fun execute(context: BytecodeContext) {
fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) {
val filtered = getInstructions().filter(insertPredicate)
if (filtered.isEmpty()) throw PatchException("Could not find insert indexes")
filtered.forEach {
val insertIndex = it.location.index + 2
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
addInstruction(
insertIndex,
"invoke-static { v$register }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
)
}
}
InitializeButtonsFingerprint.apply {
resolve(context, PivotBarConstructorFingerprint.resultOrThrow().classDef)
}.resultOrThrow().mutableMethod.apply {
// Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value.
val navigationEnumClassName = NavigationEnumFingerprint.resultOrThrow().mutableClass.type
addHook(Hook.SET_LAST_APP_NAVIGATION_ENUM) {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.definingClass == navigationEnumClassName
}
// Hook the creation of navigation tab views.
val drawableTabMethod = PivotBarButtonsCreateDrawableViewFingerprint.resultOrThrow().mutableMethod
addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{
MethodUtil.methodSignaturesMatch(
getReference<MethodReference>() ?: return@predicate false,
drawableTabMethod,
)
}
val imageResourceTabMethod = PivotBarButtonsCreateResourceViewFingerprint.resultOrThrow().method
addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{
MethodUtil.methodSignaturesMatch(
getReference<MethodReference>() ?: return@predicate false,
imageResourceTabMethod,
)
}
}
PivotBarButtonsViewSetSelectedFingerprint.resultOrThrow().mutableMethod.apply {
val index = PivotBarButtonsViewSetSelectedFingerprint.indexOfSetViewSelectedInstruction(this)
val instruction = getInstruction<FiveRegisterInstruction>(index)
val viewRegister = instruction.registerC
val isSelectedRegister = instruction.registerD
addInstruction(
index + 1,
"invoke-static { v$viewRegister, v$isSelectedRegister }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V",
)
}
// Hook onto back button pressed. Needed to fix race problem with
// litho filtering based on navigation tab before the tab is updated.
MainActivityOnBackPressedFingerprint.resultOrThrow().mutableMethod.apply {
addInstruction(
0,
"invoke-static { p0 }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V",
)
}
// Hook the search bar.
// Two different layouts are used at the hooked code.
// Insert before the first ViewGroup method call after inflating,
// so this works regardless which layout is used.
ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply {
val instructionIndex = indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setLayoutDirection"
}
val viewRegister = getInstruction<FiveRegisterInstruction>(instructionIndex).registerC
addInstruction(
instructionIndex,
"invoke-static { v$viewRegister }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V",
)
}
}
val hookNavigationButtonCreated: (String) -> Unit by lazy {
val method = NavigationBarHookCallbackFingerprint.resultOrThrow().mutableMethod
{ integrationsClassDescriptor ->
method.addInstruction(
0,
"invoke-static { p0, p1 }, " +
"$integrationsClassDescriptor->navigationTabCreated" +
"(${INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
)
}
}
private enum class Hook(val methodName: String, val parameters: String) {
SET_LAST_APP_NAVIGATION_ENUM("setLastAppNavigationEnum", "Ljava/lang/Enum;"),
NAVIGATION_TAB_LOADED("navigationTabLoaded", "Landroid/view/View;"),
NAVIGATION_IMAGE_RESOURCE_TAB_LOADED("navigationImageResourceTabLoaded", "Landroid/view/View;"),
SEARCH_BAR_RESULTS_VIEW_LOADED("searchBarResultsViewLoaded", "Landroid/view/View;"),
}
}

View File

@@ -0,0 +1,24 @@
package app.revanced.patches.youtube.misc.navigation
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
@Patch(
dependencies = [ResourceMappingPatch::class]
)
internal object NavigationBarHookResourcePatch : ResourcePatch() {
internal var imageOnlyTabResourceId: Long = -1
internal var actionBarSearchResultsViewMicId: Long = -1
override fun execute(context: ResourceContext) {
imageOnlyTabResourceId = ResourceMappingPatch.resourceMappings.first {
it.type == "layout" && it.name == "image_only_tab"
}.id
actionBarSearchResultsViewMicId = ResourceMappingPatch.resourceMappings.first {
it.type == "layout" && it.name == "action_bar_search_results_view_mic"
}.id
}
}

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object ActionBarSearchResultsFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/view/View;",
parameters = listOf("Landroid/view/LayoutInflater;"),
literalSupplier = { NavigationBarHookResourcePatch.actionBarSearchResultsViewMicId }
)

View File

@@ -1,13 +1,16 @@
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.buttons.navigation.ResolvePivotBarFingerprintsPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves to the class found in [PivotBarConstructorFingerprint].
*/
internal object InitializeButtonsFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf(),
literalSupplier = { ResolvePivotBarFingerprintsPatch.imageOnlyTabResourceId }
literalSupplier = { NavigationBarHookResourcePatch.imageOnlyTabResourceId }
)

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object MainActivityOnBackPressedFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf(),
customFingerprint = { methodDef, _ ->
(methodDef.definingClass.endsWith("MainActivity;") ||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
methodDef.definingClass.endsWith("WatchWhileActivity;"))
&& methodDef.name == "onBackPressed"
}
)

View File

@@ -0,0 +1,21 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.buttons.navigation.NavigationButtonsPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Integrations method, used for callback into to other patches.
* Specifically, [NavigationButtonsPatch].
*/
internal object NavigationBarHookCallbackFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
returnType = "V",
parameters = listOf(NavigationBarHookPatch.INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;"),
customFingerprint = { methodDef, _ ->
methodDef.name == "navigationTabCreatedCallback" &&
methodDef.definingClass == NavigationBarHookPatch.INTEGRATIONS_CLASS_DESCRIPTOR
},
)

View File

@@ -0,0 +1,21 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves to the Enum class that looks up ordinal -> instance.
*/
internal object NavigationEnumFingerprint : MethodFingerprint(
accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR,
strings = listOf(
"PIVOT_HOME",
"TAB_SHORTS",
"CREATION_TAB_LARGE",
"PIVOT_SUBSCRIPTIONS",
"TAB_ACTIVITY",
"VIDEO_LIBRARY_WHITE",
"INCOGNITO_CIRCLE"
)
)

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PivotBarButtonsCreateDrawableViewFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
// Method has different number of parameters in some app targets.
// Parameters are checked in custom fingerprint.
returnType = "Landroid/view/View;",
customFingerprint = { methodDef, classDef ->
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" &&
// Only one method has a Drawable parameter.
methodDef.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;"
}
)

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PivotBarButtonsCreateResourceViewFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "Z", "I", "L"),
returnType = "Landroid/view/View;",
customFingerprint = { _, classDef ->
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
}
)

View File

@@ -0,0 +1,27 @@
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.misc.navigation.fingerprints.PivotBarButtonsViewSetSelectedFingerprint.indexOfSetViewSelectedInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object PivotBarButtonsViewSetSelectedFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("I", "Z"),
returnType = "V",
customFingerprint = { methodDef, classDef ->
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" &&
indexOfSetViewSelectedInstruction(methodDef) >= 0
}
) {
fun indexOfSetViewSelectedInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setSelected"
}
}

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
package app.revanced.patches.youtube.misc.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -12,6 +12,7 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
@@ -39,12 +40,20 @@ object SettingsPatch :
private const val INTEGRATIONS_PACKAGE = "app/revanced/integrations/youtube"
private const val ACTIVITY_HOOK_CLASS_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/settings/LicenseActivityHook;"
private const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/ThemeHelper;"
internal const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/ThemeHelper;"
private const val SET_THEME_METHOD_NAME: String = "setTheme"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
// Add an about preference to the top.
SettingsResourcePatch += NonInteractivePreference(
key = "revanced_settings_screen_00_about",
summaryKey = null,
tag = "app.revanced.integrations.youtube.settings.preference.ReVancedYouTubeAboutPreference",
selectable = true,
)
PreferenceScreen.MISC.addPreferences(
TextPreference(
key = null,
@@ -52,7 +61,7 @@ object SettingsPatch :
summaryKey = "revanced_pref_import_export_summary",
inputType = InputType.TEXT_MULTI_LINE,
tag = "app.revanced.integrations.shared.settings.preference.ImportExportPreference",
),
)
)
SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod ->
@@ -69,7 +78,7 @@ object SettingsPatch :
replaceInstruction(
returnIndex,
"invoke-static { v$register }, " +
"$THEME_HELPER_DESCRIPTOR->$SET_THEME_METHOD_NAME(Ljava/lang/Object;)V",
"$THEME_HELPER_DESCRIPTOR->$SET_THEME_METHOD_NAME(Ljava/lang/Enum;)V",
)
addInstruction(returnIndex + 1, "return-object v$register")
}

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.youtube.shared.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object MainActivityOnCreateFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Landroid/os/Bundle;"),
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" &&
(classDef.type.endsWith("MainActivity;")
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|| classDef.type.endsWith("WatchWhileActivity;"))
}
)

View File

@@ -44,15 +44,28 @@
</string-array>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<string-array name="revanced_alt_thumbnail_options_entries">
<item>@string/revanced_alt_thumbnail_options_entry_1</item>
<item>@string/revanced_alt_thumbnail_options_entry_2</item>
<item>@string/revanced_alt_thumbnail_options_entry_3</item>
<item>@string/revanced_alt_thumbnail_options_entry_4</item>
</string-array>
<string-array name="revanced_alt_thumbnail_options_entry_values">
<!-- Enum names from Integrations -->
<item>ORIGINAL</item>
<item>DEARROW</item>
<item>DEARROW_STILL_IMAGES</item>
<item>STILL_IMAGES</item>
</string-array>
<string-array name="revanced_alt_thumbnail_stills_time_entries">
<item>@string/revanced_alt_thumbnail_stills_time_entry_1</item>
<item>@string/revanced_alt_thumbnail_stills_time_entry_2</item>
<item>@string/revanced_alt_thumbnail_stills_time_entry_3</item>
</string-array>
<string-array name="revanced_alt_thumbnail_stills_time_entry_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>BEGINNING</item>
<item>MIDDLE</item>
<item>END</item>
</string-array>
</patch>
<patch id="video.quality.RememberVideoQualityPatch">

View File

@@ -1,3 +1,31 @@
<!--
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
@@ -13,17 +41,27 @@
<string name="revanced_settings_import_failure_parse">Import failed: %s</string>
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<string name="gms_core_not_installed_warning">GmsCore is not installed. Please install.</string>
<string name="gms_core_not_running_warning">GmsCore is failing to run. Please follow the \"Don\'t kill my app\" guide for GmsCore.</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_installed_message">MicroG GmsCore is not installed. Install it.</string>
<string name="gms_core_dialog_title">Action needed</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">MicroG GmsCore does not have permission to run in the background.\n\nFollow the \"Don\'t kill my app\" guide for your phone, and apply the instructions to your MicroG installation.\n\nThis is required for the app to work.</string>
<string name="gms_core_dialog_open_website_text">Open website</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">MicroG GmsCore battery optimizations must be disabled to prevent issues.\n\nTap on the continue button and disable battery optimizations.</string>
<string name="gms_core_dialog_continue_text">Continue</string>
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
<string name="revanced_settings">ReVanced</string>
<string name="revanced_settings_about_links_body">You are using ReVanced Patches version &lt;i>%s&lt;/i></string>
<string name="revanced_settings_about_links_dev_header">Note</string>
<string name="revanced_settings_about_links_dev_body">This version is a pre-release and you may experience unexpected issues</string>
<string name="revanced_settings_about_links_header">Official links</string>
<string name="revanced_pref_import_export_title">Import / Export</string>
<string name="revanced_pref_import_export_summary">Import / Export ReVanced settings</string>
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_settings_screen_00_about_title">About</string>
<string name="revanced_settings_screen_01_ads_title">Ads</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">Alternative thumbnails</string>
<string name="revanced_settings_screen_03_feed_title">Feed</string>
@@ -56,24 +94,38 @@
<string name="revanced_hide_gray_separator_title">Hide gray separator</string>
<string name="revanced_hide_gray_separator_summary_on">Gray separators are hidden</string>
<string name="revanced_hide_gray_separator_summary_off">Gray separators are shown</string>
<string name="revanced_hide_join_membership_button_title">Hide \'Join\' button</string>
<string name="revanced_hide_join_membership_button_summary_on">Button is hidden</string>
<string name="revanced_hide_join_membership_button_summary_off">Button is shown</string>
<string name="revanced_hide_channel_watermark_title">Hide channel watermark</string>
<string name="revanced_hide_channel_watermark_summary_on">Watermark is hidden</string>
<string name="revanced_hide_channel_watermark_summary_off">Watermark is shown</string>
<string name="revanced_hide_horizontal_shelves_title">Hide horizontal shelves</string>
<string name="revanced_hide_horizontal_shelves_summary_on">Shelves are hidden such as:\n• Breaking news\n• Continue watching\n• Explore more channels\n• Shopping\n• Watch it again</string>
<string name="revanced_hide_horizontal_shelves_summary_off">Shelves are shown</string>
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<string name="revanced_hide_join_membership_button_title">Hide \'Join\' button</string>
<string name="revanced_hide_join_membership_button_summary_on">Button is hidden</string>
<string name="revanced_hide_join_membership_button_summary_off">Button is shown</string>
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_for_you_shelf_title">Hide \'For you\' shelf in channel page</string>
<string name="revanced_hide_for_you_shelf_summary_on">Shelf is hidden</string>
<string name="revanced_hide_for_you_shelf_summary_off">Shelf is shown</string>
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<string name="revanced_hide_notify_me_button_title">Hide \'Notify me\' button</string>
<string name="revanced_hide_notify_me_button_summary_on">Button is hidden</string>
<string name="revanced_hide_notify_me_button_summary_off">Button is shown</string>
<string name="revanced_hide_timed_reactions_title">Hide timed reactions</string>
<string name="revanced_hide_timed_reactions_summary_on">Timed reactions are hidden</string>
<string name="revanced_hide_timed_reactions_summary_off">Timed reactions are shown</string>
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_search_result_recommendations_title">Hide \'People also watched\' recommendations</string>
<string name="revanced_hide_search_result_recommendations_summary_on">Recommendations are hidden</string>
<string name="revanced_hide_search_result_recommendations_summary_off">Recommendations are shown</string>
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<string name="revanced_hide_show_more_button_title">Hide \'Show more\' button</string>
<string name="revanced_hide_show_more_button_summary_on">Button is hidden</string>
<string name="revanced_hide_show_more_button_summary_off">Button is shown</string>
<string name="revanced_hide_timed_reactions_title">Hide timed reactions</string>
<string name="revanced_hide_timed_reactions_summary_on">Timed reactions are hidden</string>
<string name="revanced_hide_timed_reactions_summary_off">Timed reactions are shown</string>
<string name="revanced_hide_search_result_shelf_header_title">Hide search result shelf header</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">Shelf header is hidden</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">Shelf header is shown</string>
@@ -119,6 +171,9 @@
<string name="revanced_hide_channel_bar_title">Hide channel bar</string>
<string name="revanced_hide_channel_bar_summary_on">Channel bar is hidden</string>
<string name="revanced_hide_channel_bar_summary_off">Channel bar is shown</string>
<string name="revanced_hide_playables_title">Hide playables</string>
<string name="revanced_hide_playables_summary_on">Playables are hidden</string>
<string name="revanced_hide_playables_summary_off">Playables are shown</string>
<string name="revanced_hide_quick_actions_title">Hide quick actions in fullscreen</string>
<string name="revanced_hide_quick_actions_summary_on">Quick actions are hidden</string>
<string name="revanced_hide_quick_actions_summary_off">Quick actions are shown</string>
@@ -166,10 +221,28 @@
<string name="revanced_custom_filter_summary_on">Custom filter is enabled</string>
<string name="revanced_custom_filter_summary_off">Custom filter is disabled</string>
<string name="revanced_custom_filter_strings_title">Custom filter</string>
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<string name="revanced_custom_filter_strings_summary">List of component path builder strings to filter separated by new line</string>
<string name="revanced_custom_filter_toast_invalid_characters">Invalid custom filter (must be ASCII only): %s</string>
<string name="revanced_custom_filter_toast_invalid_syntax">Invalid custom filter: %s</string>
<string name="revanced_custom_filter_toast_reset">Custom filter reset to default</string>
<string name="revanced_hide_keyword_content_screen_title">Hide keyword content</string>
<string name="revanced_hide_keyword_content_screen_summary">Hide search and feed videos using keyword filters</string>
<string name="revanced_hide_keyword_content_home_title">Hide home videos by keywords</string>
<string name="revanced_hide_keyword_content_home_summary_on">Videos in the home tab are filtered by keywords</string>
<string name="revanced_hide_keyword_content_home_summary_off">Videos in the home tab are not filtered by keywords</string>
<string name="revanced_hide_keyword_content_subscriptions_title">Hide subscription videos by keywords</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">Videos in the subscriptions tab are filtered by keywords</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Videos in the subscriptions tab are not filtered by keywords</string>
<string name="revanced_hide_keyword_content_search_title">Hide search results by keywords</string>
<string name="revanced_hide_keyword_content_search_summary_on">Search results are filtered by keywords</string>
<string name="revanced_hide_keyword_content_search_summary_off">Search results are not filtered by keywords</string>
<string name="revanced_hide_keyword_content_phrases_title">Keywords to hide</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<string name="revanced_hide_keyword_content_phrases_summary">Keywords and phrases to hide, separated by new lines\n\nWords with uppercase letters in the middle must be entered with the casing (ie: iPhone, TikTok, LeBlanc)</string>
<string name="revanced_hide_keyword_content_about_title">About keyword filtering</string>
<string name="revanced_hide_keyword_content_about_summary">Home/Subscription/Search results are filtered to hide content that matches keyword phrases\n\nLimitations\n• Some Shorts may not be hidden\n• Some UI components may not be hidden\n• Searching for a keyword may show no results</string>
<string name="revanced_hide_keyword_toast_invalid_common">Invalid keyword. Cannot use: \'%s\' as a filter</string>
<string name="revanced_hide_keyword_toast_invalid_length">Invalid keyword. \'%1$s\' is less than %2$d characters</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Hide general ads</string>
@@ -178,12 +251,12 @@
<string name="revanced_hide_fullscreen_ads_title">Hide fullscreen ads</string>
<string name="revanced_hide_fullscreen_ads_summary_on">Fullscreen ads are hidden</string>
<string name="revanced_hide_fullscreen_ads_summary_off">Fullscreen ads are shown</string>
<string name="revanced_hide_buttoned_ads_title">Hide buttoned ad</string>
<string name="revanced_hide_buttoned_ads_title">Hide buttoned ads</string>
<string name="revanced_hide_buttoned_ads_summary_on">Buttoned ads are hidden</string>
<string name="revanced_hide_buttoned_ads_summary_off">Buttoned ads are shown</string>
<string name="revanced_hide_paid_content_ads_title">Hide paid content</string>
<string name="revanced_hide_paid_content_ads_summary_on">Paid content is hidden</string>
<string name="revanced_hide_paid_content_ads_summary_off">Paid content is shown</string>
<string name="revanced_hide_paid_promotion_label_title">Hide paid promotion label</string>
<string name="revanced_hide_paid_promotion_label_summary_on">Paid promotion label is hidden</string>
<string name="revanced_hide_paid_promotion_label_summary_off">Paid promotion label is shown</string>
<string name="revanced_hide_self_sponsor_ads_title">Hide self sponsored cards</string>
<string name="revanced_hide_self_sponsor_ads_summary_on">Self sponsored cards are hidden</string>
<string name="revanced_hide_self_sponsor_ads_summary_off">Self sponsored cards are shown</string>
@@ -199,7 +272,7 @@
<string name="revanced_hide_merchandise_banners_title">Hide merchandise banners</string>
<string name="revanced_hide_merchandise_banners_summary_on">Merchandise banners are hidden</string>
<string name="revanced_hide_merchandise_banners_summary_off">Merchandise banners are shown</string>
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Failed to hide full-screen ad. Disabling to prevent issues</string>
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Could not hide fullscreen ad. Hide setting disabled to prevent issues.</string>
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
<string name="revanced_hide_get_premium_title">Hide YouTube Premium promotions</string>
@@ -233,6 +306,7 @@
<string name="revanced_external_downloader_title">Show external download button</string>
<string name="revanced_external_downloader_summary_on">Download button shown in player</string>
<string name="revanced_external_downloader_summary_off">Download button not shown in player</string>
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
<string name="revanced_external_downloader_action_button_title">Override download action button</string>
<string name="revanced_external_downloader_action_button_summary_on">Download button opens your external downloader</string>
<string name="revanced_external_downloader_action_button_summary_off">Download button opens the native in-app downloader</string>
@@ -286,27 +360,37 @@
<string name="revanced_hide_like_dislike_button_title">Hide Like and Dislike</string>
<string name="revanced_hide_like_dislike_button_summary_on">Like and Dislike buttons are hidden</string>
<string name="revanced_hide_like_dislike_button_summary_off">Like and Dislike buttons are shown</string>
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_share_button_title">Hide Share</string>
<string name="revanced_hide_share_button_summary_on">Share button is hidden</string>
<string name="revanced_hide_share_button_summary_off">Share button is shown</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<string name="revanced_hide_report_button_title">Hide Report</string>
<string name="revanced_hide_report_button_summary_on">Report button is hidden</string>
<string name="revanced_hide_report_button_summary_off">Report button is shown</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_remix_button_title">Hide Remix</string>
<string name="revanced_hide_remix_button_summary_on">Remix button is hidden</string>
<string name="revanced_hide_remix_button_summary_off">Remix button is shown</string>
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_download_button_title">Hide Download</string>
<string name="revanced_hide_download_button_summary_on">Download button is hidden</string>
<string name="revanced_hide_download_button_summary_off">Download button is shown</string>
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_thanks_button_title">Hide Thanks</string>
<string name="revanced_hide_thanks_button_summary_on">Thanks button is hidden</string>
<string name="revanced_hide_thanks_button_summary_off">Thanks button is shown</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Hide Clip</string>
<string name="revanced_hide_clip_button_summary_on">Clip button is hidden</string>
<string name="revanced_hide_clip_button_summary_off">Clip button is shown</string>
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_playlist_button_title">Hide Save to playlist</string>
<string name="revanced_hide_playlist_button_summary_on">Save to playlist button is hidden</string>
<string name="revanced_hide_playlist_button_summary_off">Save to playlist button is shown</string>
<!-- 'Shop' should be translated with the same localized wording that YouTube displays.
Shop button appears only for some videos in certain regions. Translate the button name normally if this menu is never shown. -->
<string name="revanced_hide_shop_button_title">Hide Shop</string>
<string name="revanced_hide_shop_button_summary_on">Shop button is hidden</string>
<string name="revanced_hide_shop_button_summary_off">Shop button is shown</string>
@@ -317,6 +401,7 @@
<string name="revanced_hide_autoplay_button_summary_off">Autoplay button is shown</string>
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
<string name="revanced_hide_captions_button_title">Hide captions button</string>
<string name="revanced_hide_captions_button_summary_on">Captions button is hidden</string>
<string name="revanced_hide_captions_button_summary_off">Captions button is shown</string>
@@ -329,18 +414,23 @@
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<string name="revanced_navigation_buttons_screen_title">Navigation buttons</string>
<string name="revanced_navigation_buttons_screen_summary">Hide or change buttons in the navigation bar</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<string name="revanced_hide_home_button_title">Hide Home</string>
<string name="revanced_hide_home_button_summary_on">Home button is hidden</string>
<string name="revanced_hide_home_button_summary_off">Home button is shown</string>
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_hide_shorts_button_title">Hide Shorts</string>
<string name="revanced_hide_shorts_button_summary_on">Shorts button is hidden</string>
<string name="revanced_hide_shorts_button_summary_off">Shorts button is shown</string>
<!-- The Create button has no display name. Translate normally. -->
<string name="revanced_hide_create_button_title">Hide Create</string>
<string name="revanced_hide_create_button_summary_on">Create button is hidden</string>
<string name="revanced_hide_create_button_summary_off">Create button is shown</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_hide_subscriptions_button_title">Hide Subscriptions</string>
<string name="revanced_hide_subscriptions_button_summary_on">Subscriptions button is hidden</string>
<string name="revanced_hide_subscriptions_button_summary_off">Subscriptions button is shown</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">Switch Create with Notifications</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">Create button is switched with Notifications button</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Create button is not switched with Notifications button</string>
@@ -348,33 +438,49 @@
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<string name="revanced_hide_player_flyout_title">Flyout menu</string>
<string name="revanced_hide_player_flyout_summary">Hide or show player flyout menu items</string>
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_captions_title">Hide Captions</string>
<string name="revanced_hide_player_flyout_captions_summary_on">Captions menu is hidden</string>
<string name="revanced_hide_player_flyout_captions_summary_off">Captions menu is shown</string>
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_additional_settings_title">Hide Additional settings</string>
<string name="revanced_hide_player_flyout_additional_settings_summary_on">Additional settings menu is hidden</string>
<string name="revanced_hide_player_flyout_additional_settings_summary_off">Additional settings menu is shown</string>
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_loop_video_title">Hide Loop video</string>
<string name="revanced_hide_player_flyout_loop_video_summary_on">Loop video menu is hidden</string>
<string name="revanced_hide_player_flyout_loop_video_summary_off">Loop video menu is shown</string>
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_ambient_mode_title">Hide Ambient mode</string>
<string name="revanced_hide_player_flyout_ambient_mode_summary_on">Ambient mode menu is hidden</string>
<string name="revanced_hide_player_flyout_ambient_mode_summary_off">Ambient mode menu is shown</string>
<!-- 'Report' should be translated using the same localized wording YouTube displays for the menu item.
This item may not appear in some regions. Translate the name normally if the menu cannot be found. -->
<string name="revanced_hide_player_flyout_report_title">Hide Report</string>
<string name="revanced_hide_player_flyout_report_summary_on">Report menu is hidden</string>
<string name="revanced_hide_player_flyout_report_summary_off">Report menu is shown</string>
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_help_title">Hide Help &amp; feedback</string>
<string name="revanced_hide_player_flyout_help_summary_on">Help &amp; feedback menu is hidden</string>
<string name="revanced_hide_player_flyout_help_summary_off">Help &amp; feedback menu is shown</string>
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_speed_title">Hide Playback speed</string>
<string name="revanced_hide_player_flyout_speed_summary_on">Playback speed menu is hidden</string>
<string name="revanced_hide_player_flyout_speed_summary_off">Playback speed menu is shown</string>
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<string name="revanced_hide_player_flyout_more_info_title">Hide More info</string>
<string name="revanced_hide_player_flyout_more_info_summary_on">More info menu is hidden</string>
<string name="revanced_hide_player_flyout_more_info_summary_off">More info menu is shown</string>
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_lock_screen_title">Hide Lock screen</string>
<string name="revanced_hide_player_flyout_lock_screen_summary_on">Lock screen menu is hidden</string>
<string name="revanced_hide_player_flyout_lock_screen_summary_off">Lock screen menu is shown</string>
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_audio_track_title">Hide Audio track</string>
<string name="revanced_hide_player_flyout_audio_track_summary_on">Audio track menu is hidden</string>
<string name="revanced_hide_player_flyout_audio_track_summary_off">Audio track menu is shown</string>
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_watch_in_vr_title">Hide Watch in VR</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Watch in VR menu is hidden</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_off">Watch in VR menu is shown</string>
@@ -389,20 +495,15 @@
<string name="revanced_hide_album_cards_summary_on">Album cards are hidden</string>
<string name="revanced_hide_album_cards_summary_off">Album cards are shown</string>
</patch>
<patch id="layout.hide.breakingnews.BreakingNewsResourcePatch">
<string name="revanced_hide_breaking_news_title">Hide breaking news</string>
<string name="revanced_hide_breaking_news_summary_on">Breaking news are hidden</string>
<string name="revanced_hide_breaking_news_summary_off">Breaking news are shown</string>
</patch>
<patch id="layout.hide.comments.CommentsPatch">
<string name="revanced_comments_screen_title">Comments</string>
<string name="revanced_comments_screen_summary">Hide or show comments section components</string>
<string name="revanced_hide_comments_section_title">Hide comments section</string>
<string name="revanced_hide_comments_section_summary_on">Comment section is hidden</string>
<string name="revanced_hide_comments_section_summary_off">Comment section is shown</string>
<string name="revanced_hide_preview_comment_title">Hide preview comment</string>
<string name="revanced_hide_preview_comment_summary_on">Preview comment is hidden</string>
<string name="revanced_hide_preview_comment_summary_off">Preview comment is shown</string>
<string name="revanced_hide_comments_section_title">Hide comments section</string>
<string name="revanced_hide_comments_section_summary_on">Comment section is hidden</string>
<string name="revanced_hide_comments_section_summary_off">Comment section is shown</string>
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
<string name="revanced_hide_crowdfunding_box_title">Hide crowdfunding box</string>
@@ -442,14 +543,6 @@
<string name="revanced_hide_info_cards_summary_on">Info cards are hidden</string>
<string name="revanced_hide_info_cards_summary_off">Info cards are shown</string>
</patch>
<patch id="layout.hide.loadmorebutton.HideLoadMoreButtonResourcePatch">
<string name="revanced_hide_load_more_button_title">Hide \'Load More\' button</string>
<string name="revanced_hide_load_more_button_summary_on">Button is hidden</string>
<string name="revanced_hide_load_more_button_summary_off">Button is shown</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<string name="video_title">Video</string>
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
<string name="revanced_disable_rolling_number_animations_title">Disable rolling number animations</string>
<string name="revanced_disable_rolling_number_animations_summary_on">Rolling numbers are not animated</string>
@@ -464,21 +557,43 @@
<string name="revanced_hide_seekbar_thumbnail_summary_off">Thumbnail seekbar is shown</string>
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<string name="revanced_hide_shorts_title">Hide Shorts in feed</string>
<string name="revanced_hide_shorts_summary_on">Shorts are hidden</string>
<string name="revanced_hide_shorts_summary_off">Shorts are shown</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">Hide Shorts in home feed</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts in home feed are hidden</string>
<string name="revanced_hide_shorts_home_summary_off">Shorts in home feed are shown</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">Hide Shorts in subscription feed</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts in subscription feed are hidden</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts in subscription feed are shown</string>
<string name="revanced_hide_shorts_search_title">Hide Shorts in search results</string>
<string name="revanced_hide_shorts_search_summary_on">Shorts in search results are hidden</string>
<string name="revanced_hide_shorts_search_summary_off">Shorts in search results are shown</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">Hide join button</string>
<string name="revanced_hide_shorts_join_button_summary_on">Join button is hidden</string>
<string name="revanced_hide_shorts_join_button_summary_off">Join button is shown</string>
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_subscribe_button_title">Hide subscribe button</string>
<string name="revanced_hide_shorts_subscribe_button_summary_on">Subscribe button is hidden</string>
<string name="revanced_hide_shorts_subscribe_button_summary_off">Subscribe button is shown</string>
<string name="revanced_hide_shorts_subscribe_button_paused_title">Hide subscribe button when paused</string>
<string name="revanced_hide_shorts_subscribe_button_paused_summary_on">Subscribe button is hidden</string>
<string name="revanced_hide_shorts_subscribe_button_paused_summary_off">Subscribe button is shown</string>
<string name="revanced_hide_shorts_thanks_button_title">Hide thanks button</string>
<string name="revanced_hide_shorts_thanks_button_summary_on">Thanks button is hidden</string>
<string name="revanced_hide_shorts_thanks_button_summary_off">Thanks button is shown</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_title">Hide paused overlay buttons</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_on">Paused overlay buttons are hidden</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_off">Paused overlay buttons are shown</string>
<string name="revanced_hide_shorts_shop_button_title">Hide shop button</string>
<string name="revanced_hide_shorts_shop_button_summary_on">Shop button is hidden</string>
<string name="revanced_hide_shorts_shop_button_summary_off">Shop button is shown</string>
<string name="revanced_hide_shorts_tagged_products_title">Hide tagged products</string>
<string name="revanced_hide_shorts_tagged_products_summary_on">Tagged products are hidden</string>
<string name="revanced_hide_shorts_tagged_products_summary_off">Tagged products are shown</string>
<string name="revanced_hide_shorts_location_label_title">Hide location label</string>
<string name="revanced_hide_shorts_location_label_summary_on">Location label is hidden</string>
<string name="revanced_hide_shorts_location_label_summary_off">Location label is shown</string>
<string name="revanced_hide_shorts_save_sound_button_title">Hide save sound to playlist button</string>
<string name="revanced_hide_shorts_save_sound_button_summary_on">Save sound to playlist is hidden</string>
<string name="revanced_hide_shorts_save_sound_button_summary_off">Save sound to playlist is shown</string>
<string name="revanced_hide_shorts_search_suggestions_title">Hide search suggestions</string>
<string name="revanced_hide_shorts_search_suggestions_summary_on">Search suggestions are hidden</string>
<string name="revanced_hide_shorts_search_suggestions_summary_off">Search suggestions are shown</string>
<string name="revanced_hide_shorts_like_button_title">Hide like button</string>
<string name="revanced_hide_shorts_like_button_summary_on">Like button is hidden</string>
<string name="revanced_hide_shorts_like_button_summary_off">Like button is shown</string>
@@ -488,9 +603,11 @@
<string name="revanced_hide_shorts_comments_button_title">Hide comments button</string>
<string name="revanced_hide_shorts_comments_button_summary_on">Comments button is hidden</string>
<string name="revanced_hide_shorts_comments_button_summary_off">Comments button is shown</string>
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_remix_button_title">Hide remix button</string>
<string name="revanced_hide_shorts_remix_button_summary_on">Remix button is hidden</string>
<string name="revanced_hide_shorts_remix_button_summary_off">Remix button is shown</string>
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_share_button_title">Hide share button</string>
<string name="revanced_hide_shorts_share_button_summary_on">Share button is hidden</string>
<string name="revanced_hide_shorts_share_button_summary_off">Share button is shown</string>
@@ -504,11 +621,11 @@
<string name="revanced_hide_shorts_video_title_summary_on">Title is hidden</string>
<string name="revanced_hide_shorts_video_title_summary_off">Title is shown</string>
<string name="revanced_hide_shorts_sound_metadata_label_title">Hide sound metadata label</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_on">Label is hidden</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_off">Label is shown</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_on">Metadata label is hidden</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_off">Metadata label is shown</string>
<string name="revanced_hide_shorts_full_video_link_label_title">Hide full video link label</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_on">Label is hidden</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_off">Label is shown</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_on">Video link label is hidden</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_off">Video link label is shown</string>
<string name="revanced_hide_shorts_sound_button_title">Hide sound button</string>
<string name="revanced_hide_shorts_sound_button_summary_on">Sound button is hidden</string>
<string name="revanced_hide_shorts_sound_button_summary_off">Sound button is shown</string>
@@ -538,23 +655,24 @@
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<string name="revanced_ryd_settings_title">Return YouTube Dislike</string>
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Hidden</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_ryd_failure_connection_timeout">Dislikes temporarily not available (API timed out)</string>
<string name="revanced_ryd_failure_connection_status_code" formatted="false">Dislikes not available (status %d)</string>
<string name="revanced_ryd_failure_connection_status_code">Dislikes not available (status %d)</string>
<string name="revanced_ryd_failure_client_rate_limit_requested">Dislikes not available (client API limit reached)</string>
<string name="revanced_ryd_failure_generic">Dislikes not available (%s)</string>
<!-- corner case situation, where user enables RYD while video is playing and then tries
to vote for the video -->
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Reload video to vote using ReturnYouTubeDislike</string>
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Reload video to vote using Return YouTube Dislike</string>
<string name="revanced_ryd_enable_title">Return YouTube Dislike</string>
<string name="revanced_ryd_enable_summary_on">Dislikes are shown</string>
<string name="revanced_ryd_enable_summary_off">Dislikes are not shown</string>
<string name="revanced_ryd_shorts_title">Show dislikes on Shorts</string>
<string name="revanced_ryd_shorts_summary_on">Dislikes shown on Shorts %s</string>
<string name="revanced_ryd_shorts_summary_on">Dislikes shown on Shorts</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">Dislikes shown on Shorts\n\nLimitation: Dislikes may not appear in incognito mode</string>
<string name="revanced_ryd_shorts_summary_off">Dislikes hidden on Shorts</string>
<string name="revanced_ryd_shorts_summary_disclaimer">Limitation: Dislikes may not appear in incognito mode</string>
<string name="revanced_ryd_dislike_percentage_title">Dislikes as percentage</string>
<string name="revanced_ryd_dislike_percentage_summary_on">Dislikes shown as percentage</string>
<string name="revanced_ryd_dislike_percentage_summary_off">Dislikes shown as number</string>
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_compact_layout_title">Compact like button</string>
<string name="revanced_ryd_compact_layout_summary_on">Like button styled for minimum width</string>
<string name="revanced_ryd_compact_layout_summary_off">Like button styled for best appearance</string>
@@ -564,6 +682,7 @@
<string name="revanced_ryd_about">About</string>
<string name="revanced_ryd_attribution_title">ReturnYouTubeDislike.com</string>
<string name="revanced_ryd_attribution_summary">Data is provided by the Return YouTube Dislike API. Tap here to learn more</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
<string name="revanced_ryd_statistics_category_title">ReturnYouTubeDislike API statistics of this device</string>
<string name="revanced_ryd_statistics_getFetchCallResponseTimeAverage_title">API response time, average</string>
<string name="revanced_ryd_statistics_getFetchCallResponseTimeMin_title">API response time, minimum</string>
@@ -574,8 +693,7 @@
<string name="revanced_ryd_statistics_getFetchCallCount_zero_summary">No network calls made</string>
<string name="revanced_ryd_statistics_getFetchCallCount_non_zero_summary">%d network calls made</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_title">API fetch votes, number of timeouts</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_zero_summary">No
network calls timed out</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_zero_summary">No network calls timed out</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_non_zero_summary">%d network calls timed out</string>
<string name="revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_title">API client rate limits</string>
<string name="revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_zero_summary">No client rate limits encountered</string>
@@ -600,6 +718,7 @@
<string name="revanced_sb_enable_voting">Show voting button</string>
<string name="revanced_sb_enable_voting_sum_on">Segment voting button is shown</string>
<string name="revanced_sb_enable_voting_sum_off">Segment voting button is not shown</string>
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<string name="revanced_sb_enable_compact_skip_button">Use compact skip button</string>
<string name="revanced_sb_enable_compact_skip_button_sum_on">Skip button styled for minimum width</string>
<string name="revanced_sb_enable_compact_skip_button_sum_off">Skip button styled for best appearance</string>
@@ -633,19 +752,19 @@
<string name="revanced_sb_general_skipcount_sum_on">Lets the SponsorBlock leaderboard know how much time is saved. A message is sent to the leaderboard each time a segment is skipped</string>
<string name="revanced_sb_general_skipcount_sum_off">Skip count tracking is not enabled</string>
<string name="revanced_sb_general_min_duration">Minimum segment duration</string>
<string name="revanced_sb_general_min_duration_sum">Segments shorter than this value (in seconds) will not be shown or skipped </string>
<string name="revanced_sb_general_min_duration_sum">Segments shorter than this value (in seconds) will not be shown or skipped</string>
<string name="revanced_sb_general_uuid">Your private user id</string>
<string name="revanced_sb_general_uuid_sum">This should be kept private. This is like a password and should not be shared with anyone. If someone has this, they can impersonate you</string>
<string name="revanced_sb_general_uuid_invalid">Private user id must be at least 30 characters long</string>
<string name="revanced_sb_general_api_url">Change API URL</string>
<string name="revanced_sb_general_api_url_sum">The address SponsorBlock uses to make calls to the server. Do not change this unless you know what you\'re doing</string>
<string name="revanced_sb_general_api_url_sum">The address SponsorBlock uses to make calls to the server</string>
<string name="revanced_sb_api_url_reset">API URL reset</string>
<string name="revanced_sb_api_url_invalid">API URL is invalid</string>
<string name="revanced_sb_api_url_changed">API URL changed</string>
<string name="revanced_sb_settings_ie">Import/Export settings</string>
<string name="revanced_sb_settings_copy">Copy</string>
<string name="revanced_sb_settings_ie_sum">Your SponsorBlock JSON configuration that can be imported/exported to ReVanced and other SponsorBlock platforms %s</string>
<string name="revanced_sb_settings_ie_sum_warning">This includes your private user id. Be sure to share this wisely</string>
<string name="revanced_sb_settings_ie_sum">Your SponsorBlock JSON configuration that can be imported/exported to ReVanced and other SponsorBlock platforms</string>
<string name="revanced_sb_settings_ie_sum_warning">Your SponsorBlock JSON configuration that can be imported/exported to ReVanced and other SponsorBlock platforms. This includes your private user id. Be sure to share this wisely</string>
<string name="revanced_sb_settings_import_successful">Settings imported successfully</string>
<string name="revanced_sb_settings_import_failed">Failed to import: %s</string>
<string name="revanced_sb_settings_export_failed">Failed to export: %s</string>
@@ -706,19 +825,20 @@
<string name="revanced_sb_skip_showbutton">Show a skip button</string>
<string name="revanced_sb_skip_seekbaronly">Show in seek bar</string>
<string name="revanced_sb_skip_ignore">Disable</string>
<string name="revanced_sb_submit_failed_invalid" formatted="false">Unable to submit segment: %s</string>
<string name="revanced_sb_submit_failed_invalid">Unable to submit segment: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock is temporarily down</string>
<string name="revanced_sb_submit_failed_unknown_error" formatted="false">Unable to submit segment (status: %d %s)</string>
<string name="revanced_sb_submit_failed_unknown_error">Unable to submit segment (status: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">Unable to submit segment.\nRate Limited (too many from the same user or IP)</string>
<string name="revanced_sb_submit_failed_forbidden" formatted="false">Can\'t submit the segment: %s</string>
<string name="revanced_sb_submit_failed_forbidden">Can\'t submit the segment: %s</string>
<string name="revanced_sb_submit_failed_duplicate">Can\'t submit the segment.\nAlready exists</string>
<string name="revanced_sb_submit_succeeded">Segment submitted successfully</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock temporarily not available</string>
<string name="revanced_sb_sponsorblock_connection_failure_status" formatted="false">SponsorBlock temporarily not available (status %d)</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock temporarily not available (API timed out)</string>
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock temporarily not available (status %d)</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock temporarily not available</string>
<string name="revanced_sb_vote_failed_timeout">Unable to vote for segment (API timed out)</string>
<string name="revanced_sb_vote_failed_unknown_error" formatted="false">Unable to vote for segment (status: %d %s)</string>
<string name="revanced_sb_vote_failed_forbidden" formatted="false">Unable to vote for segment: %s</string>
<string name="revanced_sb_vote_failed_unknown_error">Unable to vote for segment (status: %1$d %2$s)</string>
<string name="revanced_sb_vote_failed_forbidden">Unable to vote for segment: %s</string>
<string name="revanced_sb_vote_upvote">Upvote</string>
<string name="revanced_sb_vote_downvote">Downvote</string>
<string name="revanced_sb_vote_category">Change category</string>
@@ -726,14 +846,18 @@
<string name="revanced_sb_new_segment_choose_category">Choose the segment category</string>
<string name="revanced_sb_new_segment_disabled_category">Category is disabled in settings. Enable category to submit.</string>
<string name="revanced_sb_new_segment_title">New SponsorBlock segment</string>
<string name="revanced_sb_new_segment_mark_time_as_question" formatted="false">Set %02d:%02d:%03d as the start or end of a new segment?</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Set %1$02d:%2$02d:%3$03d as the start or end of a new segment?</string>
<string name="revanced_sb_new_segment_mark_start">start</string>
<string name="revanced_sb_new_segment_mark_end">end</string>
<string name="revanced_sb_new_segment_now">now</string>
<string name="revanced_sb_new_segment_time_start">Time the segment begins at</string>
<string name="revanced_sb_new_segment_time_end">Time the segment ends at</string>
<string name="revanced_sb_new_segment_confirm_title">Are the times correct?</string>
<string name="revanced_sb_new_segment_confirm_content" formatted="false">The segment lasts from %02d:%02d to %02d:%02d (%d minutes %02d seconds)\nIs it ready to submit?</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_confirm_content">The segment lasts from %1$02d:%2$02d to %3$02d:%4$02d (%5$d minutes %6$02d seconds)\nIs it ready to submit?</string>
<string name="revanced_sb_new_segment_start_is_before_end">Start must be before the end</string>
<string name="revanced_sb_new_segment_mark_locations_first">Mark two locations on the time bar first</string>
<string name="revanced_sb_new_segment_preview_segment_first">Preview the segment, and ensure it skips smoothly</string>
@@ -741,25 +865,26 @@
<string name="revanced_sb_new_segment_edit_by_hand_content">Do you want to edit the timing for the start or end of the segment?</string>
<string name="revanced_sb_new_segment_edit_by_hand_parse_error">Invalid time given</string>
<string name="revanced_sb_stats">Stats</string>
<!-- Shown in the settings preferences, and translations can be any text length. -->
<string name="revanced_sb_stats_connection_failure">Stats temporarily not available (API is down)</string>
<string name="revanced_sb_stats_loading">Loading...</string>
<string name="revanced_sb_stats_sb_disabled">SponsorBlock is disabled</string>
<string name="revanced_sb_stats_username" formatted="false">Your username: &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_username">Your username: &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_username_change">Tap here to change your username</string>
<string name="revanced_sb_stats_username_change_unknown_error" formatted="false">Unable to change username: Status: %d %s</string>
<string name="revanced_sb_stats_username_change_unknown_error">Unable to change username: Status: %1$d %2$s</string>
<string name="revanced_sb_stats_username_changed">Username successfully changed</string>
<string name="revanced_sb_stats_reputation" formatted="false">Your reputation is &lt;b>%.2f&lt;/b></string>
<string name="revanced_sb_stats_submissions" formatted="false">You\'ve created &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_reputation">Your reputation is &lt;b>%.2f&lt;/b></string>
<string name="revanced_sb_stats_submissions">You\'ve created &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_saved_zero">SponsorBlock leaderboard</string>
<string name="revanced_sb_stats_saved" formatted="false">You\'ve saved people from &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_saved">You\'ve saved people from &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_saved_sum_zero">Tap here to see the global stats and top contributors</string>
<string name="revanced_sb_stats_saved_sum" formatted="false">That\'s &lt;b>%s&lt;/b> of their lives.&lt;br>Tap here to see the leaderboard</string>
<string name="revanced_sb_stats_self_saved" formatted="false">You\'ve skipped &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_self_saved_sum" formatted="false">That\'s &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_saved_sum">That\'s &lt;b>%s&lt;/b> of their lives.&lt;br>Tap here to see the leaderboard</string>
<string name="revanced_sb_stats_self_saved">You\'ve skipped &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_self_saved_sum">That\'s &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_self_saved_reset_title">Reset skipped segments counter?</string>
<string name="revanced_sb_stats_saved_hour_format" formatted="false">%s hours %s minutes</string>
<string name="revanced_sb_stats_saved_minute_format" formatted="false">%s minutes %s seconds</string>
<string name="revanced_sb_stats_saved_second_format" formatted="false">%s seconds</string>
<string name="revanced_sb_stats_saved_hour_format">%1$s hours %2$s minutes</string>
<string name="revanced_sb_stats_saved_minute_format">%1$s minutes %2$s seconds</string>
<string name="revanced_sb_stats_saved_second_format">%s seconds</string>
<string name="revanced_sb_color_dot_label">Color:</string>
<string name="revanced_sb_color_changed">Color changed</string>
<string name="revanced_sb_color_reset">Color reset</string>
@@ -775,7 +900,10 @@
<string name="revanced_spoof_app_version_summary_on">Version spoofed</string>
<string name="revanced_spoof_app_version_summary_off">Version not spoofed</string>
<string name="revanced_spoof_app_version_user_dialog_message">App version will be spoofed to an older version of YouTube.\n\nThis will change the appearance and features of the app, but unknown side effects may occur.\n\nIf later turned off, it is recommended to clear the app data to prevent UI bugs.</string>
<!-- It is ideal, but not required, if the text here appears alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">Spoof app version target</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_entry_1">18.33.40 - Restore RYD on Shorts incognito mode</string>
<string name="revanced_spoof_app_version_target_entry_2">18.20.39 - Restore wide video speed &amp; quality menu</string>
<string name="revanced_spoof_app_version_target_entry_3">18.09.39 - Restore library tab</string>
@@ -785,13 +913,17 @@
<patch id="layout.startpage.ChangeStartPagePatch">
<string name="revanced_start_page_title">Set start page</string>
<string name="revanced_start_page_entry_0">Default</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_start_page_entry_1">Home</string>
<string name="revanced_start_page_entry_2">Search</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<string name="revanced_start_page_entry_3">Subscriptions</string>
<string name="revanced_start_page_entry_4">Explore</string>
<string name="revanced_start_page_entry_5">Shorts</string>
<string name="revanced_start_page_entry_6">Library</string>
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<string name="revanced_start_page_entry_6">You tab</string>
<string name="revanced_start_page_entry_7">Liked videos</string>
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
<string name="revanced_start_page_entry_8">History</string>
<string name="revanced_start_page_entry_9">Trending</string>
</patch>
@@ -802,7 +934,6 @@
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">Enable tablet layout</string>
<string name="layout_summary">Settings related to the layout</string>
<string name="revanced_tablet_layout_summary_on">Tablet layout is enabled</string>
<string name="revanced_tablet_layout_summary_off">Tablet layout is disabled</string>
<string name="revanced_tablet_layout_user_dialog_message">Community posts do not show up on tablet layouts</string>
@@ -826,33 +957,35 @@
<string name="revanced_seekbar_custom_color_invalid">Invalid seekbar color value. Using default value.</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<string name="revanced_alt_thumbnail_about_title">Thumbnails in use</string>
<string name="revanced_alt_thumbnail_dearrow_title">Enable DeArrow thumbnails</string>
<string name="revanced_alt_thumbnail_dearrow_summary_on">Using DeArrow thumbnails</string>
<string name="revanced_alt_thumbnail_dearrow_summary_off">Not using DeArrow thumbnails</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Home tab</string>
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_alt_thumbnail_subscription_title">Subscription tab</string>
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<string name="revanced_alt_thumbnail_library_title">You tab</string>
<string name="revanced_alt_thumbnail_player_title">Player playlists, recommendations</string>
<string name="revanced_alt_thumbnail_search_title">Search results</string>
<string name="revanced_alt_thumbnail_options_entry_1">Original thumbnails</string>
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow &amp; Original thumbnails</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; Still captures</string>
<string name="revanced_alt_thumbnail_options_entry_4">Still captures</string>
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow provides crowd-sourced thumbnails for YouTube videos. These thumbnails are often more relevant than those provided by YouTube\n\nIf enabled, video URLs will be sent to the API server and no other data is sent. If a video does not have DeArrow thumbnails, then the original or still captures are shown\n\nTap here to learn more about DeArrow</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">Show a toast if API is not available</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Toast is shown if DeArrow is not available</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Toast is not shown if DeArrow is not available</string>
<string name="revanced_alt_thumbnail_dearrow_api_url_title">DeArrow API endpoint</string>
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">The URL of the DeArrow thumbnail cache endpoint. Do not change this unless you know what you\'re doing</string>
<string name="revanced_alt_thumbnail_dearrow_about_title">About DeArrow</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow provides crowd-sourced thumbnails for YouTube videos. These thumbnails are often more relevant than those provided by YouTube. If enabled, video URLs will be sent to the API server and no other data is sent\n\nTap here to learn more about DeArrow</string>
<string name="revanced_alt_thumbnail_stills_title">Enable still video captures</string>
<string name="revanced_alt_thumbnail_stills_summary_on">Using YouTube still video captures</string>
<string name="revanced_alt_thumbnail_stills_summary_off">Not using YouTube still video captures</string>
<string name="revanced_alt_thumbnail_stills_time_title">Video time to take the still from</string>
<string name="revanced_alt_thumbnail_stills_time_entry_1">Beginning of video</string>
<string name="revanced_alt_thumbnail_stills_time_entry_2">Middle of video</string>
<string name="revanced_alt_thumbnail_stills_time_entry_3">End of video</string>
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">The URL of the DeArrow thumbnail cache endpoint</string>
<string name="revanced_alt_thumbnail_stills_about_title">Still video captures</string>
<string name="revanced_alt_thumbnail_stills_about_summary">Still captures are taken from the beginning/middle/end of each video. These images are built into YouTube and no external API is used</string>
<string name="revanced_alt_thumbnail_stills_fast_title">Use fast still captures</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_on">Using medium quality still captures. Thumbnails will load faster, but live streams, unreleased, or very old videos may show blank thumbnails</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_off">Using high quality still captures</string>
<string name="revanced_alt_thumbnail_stills_about_title">About still video captures</string>
<string name="revanced_alt_thumbnail_stills_about_summary">Still captures are taken from the beginning/middle/end of each video. These images are built into YouTube and no external API is used</string>
<string name="revanced_alt_thumbnail_about_status_disabled">Showing original YouTube thumbnails</string>
<string name="revanced_alt_thumbnail_about_status_stills">Showing still video captures</string>
<string name="revanced_alt_thumbnail_about_status_dearrow">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then the original YouTube thumbnails are shown</string>
<string name="revanced_alt_thumbnail_about_status_dearrow_stills">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then still video captures are shown</string>
<string name="revanced_alt_thumbnail_stills_time_title">Video time to take still captures from</string>
<string name="revanced_alt_thumbnail_stills_time_entry_1">Beginning of video</string>
<string name="revanced_alt_thumbnail_stills_time_entry_2">Middle of video</string>
<string name="revanced_alt_thumbnail_stills_time_entry_3">End of video</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_alt_thumbnail_dearrow_error">DeArrow temporarily not available (status code: %s)</string>
<string name="revanced_alt_thumbnail_dearrow_error_generic">DeArrow temporarily not available</string>
</patch>
@@ -862,6 +995,7 @@
<string name="revanced_announcements_summary_off">Announcements are not shown on startup</string>
<string name="revanced_announcements_enabled_summary">Show announcements on startup</string>
<string name="revanced_announcements_connection_failed">Failed connecting to announcements provider</string>
<string name="revanced_announcements_dialog_dismiss">Dismiss</string>
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
<string name="revanced_auto_repeat_title">Enable auto-repeat</string>
@@ -870,24 +1004,9 @@
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
<string name="revanced_spoof_device_dimensions_title">Spoof device dimensions</string>
<string name="revanced_spoof_device_dimensions_summary_on">Device dimensions spoofed</string>
<string name="revanced_spoof_device_dimensions_summary_off">Device dimensions not spoofed\n\nSpoofing the device dimensions can unlock higher video qualities but unknown side effects may occur</string>
</patch>
<patch id="misc.fix.playback.SpoofSignaturePatch">
<string name="revanced_spoof_signature_verification_screen_title">Spoof app signature</string>
<string name="revanced_spoof_signature_verification_screen_summary">Spoof the app signature to prevent playback issues</string>
<string name="revanced_spoof_signature_verification_enabled_title">Spoof app signature</string>
<string name="revanced_spoof_signature_verification_enabled_summary_on">App signature spoofed\n\nSide effects include:\n• Enhanced bitrate is not available\n• Videos cannot be downloaded\n• No seekbar thumbnails for paid videos</string>
<string name="revanced_spoof_signature_verification_enabled_summary_off">App signature not spoofed\n\nVideo playback may not work</string>
<string name="revanced_spoof_signature_verification_enabled_user_dialog_message">Turning off this setting will cause video playback issues.</string>
<string name="revanced_spoof_signature_in_feed_enabled_title">Spoof app signature in feed</string>
<string name="revanced_spoof_signature_in_feed_enabled_summary_on">App signature spoofed\n\nSide effects include:\n• Feed videos are missing subtitles\n• Automatically played feed videos will show up in your watch history</string>
<string name="revanced_spoof_signature_in_feed_enabled_summary_off">App signature not spoofed for feed videos\n\nFeed videos will play for less than 1 minute before encountering playback issues</string>
<string name="revanced_spoof_storyboard_title">Spoof storyboard</string>
<string name="revanced_spoof_storyboard_summary_on">Storyboard spoofed</string>
<string name="revanced_spoof_storyboard_summary_off">Storyboard not spoofed\n\nSide effects include:\n• No ambient mode\n• Seekbar thumbnails are hidden</string>
<string name="revanced_spoof_storyboard_timeout">Spoof storyboard temporarily not available (API timed out)</string>
<string name="revanced_spoof_storyboard_io_exception">Spoof storyboard temporarily not available: %s</string>
<string name="revanced_spoof_device_dimensions_summary_on">Device dimensions spoofed\n\nHigher video qualities might be unlocked but you may experience video playback stuttering, worse battery life, and unknown side effects</string>
<string name="revanced_spoof_device_dimensions_summary_off">Device dimensions not spoofed\n\nEnabling this can unlock higher video qualities</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Enabling this can cause video playback stuttering, worse battery life, and unknown side effects.</string>
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
<string name="microg_settings_title">GmsCore Settings</string>
@@ -917,11 +1036,6 @@
<string name="revanced_disable_zoom_haptics_summary_on">Haptics are disabled</string>
<string name="revanced_disable_zoom_haptics_summary_off">Haptics are enabled</string>
</patch>
<patch id="video.hdrbrightness.HDRBrightnessPatch">
<string name="revanced_hdr_auto_brightness_title">Enable auto HDR brightness</string>
<string name="revanced_hdr_auto_brightness_summary_on">Auto HDR brightness is enabled</string>
<string name="revanced_hdr_auto_brightness_summary_off">Auto HDR brightness is disabled</string>
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
<string name="revanced_video_quality_default_entry_1">Automatic quality</string>
<string name="revanced_video_quality_default_entry_2">2160p</string>
@@ -939,12 +1053,12 @@
<string name="revanced_video_quality_default_mobile_title">Default video quality on mobile network</string>
<string name="revanced_remember_video_quality_mobile">mobile</string>
<string name="revanced_remember_video_quality_wifi">wifi</string>
<string name="revanced_remember_video_quality_toast" formatted="false">Changed default %s quality to: %s</string>
<string name="revanced_remember_video_quality_toast">Changed default %1$s quality to: %2$s</string>
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
<string name="revanced_custom_playback_speeds_invalid" formatted="false">Custom speeds must be less than %s Using default values.</string>
<string name="revanced_custom_playback_speeds_invalid">Custom speeds must be less than %s. Using default values.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
@@ -952,7 +1066,7 @@
<string name="revanced_remember_playback_speed_last_selected_summary_on">Playback speed changes apply to all videos</string>
<string name="revanced_remember_playback_speed_last_selected_summary_off">Playback speed changes only apply to the current video</string>
<string name="revanced_playback_speed_default_title">Default playback speed</string>
<string name="revanced_remember_playback_speed_toast" formatted="false">Changed default speed to: %s</string>
<string name="revanced_remember_playback_speed_toast">Changed default speed to: %s</string>
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
<string name="revanced_restore_old_video_quality_menu_title">Restore old video quality menu</string>
@@ -964,6 +1078,29 @@
<string name="revanced_slide_to_seek_summary_on">Slide to seek is enabled</string>
<string name="revanced_slide_to_seek_summary_off">Slide to seek is not enabled</string>
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
<string name="revanced_spoof_signature_verification_screen_title">Spoof app signature</string>
<string name="revanced_spoof_signature_verification_screen_summary">Spoof the app signature to prevent playback issues</string>
<string name="revanced_spoof_signature_verification_enabled_title">Spoof app signature</string>
<string name="revanced_spoof_signature_verification_enabled_summary_on">App signature spoofed\n\nSide effects include:\n• Enhanced bitrate is not available\n• Videos cannot be downloaded\n• No seekbar thumbnails for paid videos</string>
<string name="revanced_spoof_signature_verification_enabled_summary_off">App signature not spoofed\n\nVideo playback may not work</string>
<string name="revanced_spoof_signature_verification_enabled_user_dialog_message">Turning off this setting will cause video playback issues.</string>
<string name="revanced_spoof_signature_in_feed_enabled_title">Spoof app signature in feed</string>
<string name="revanced_spoof_signature_in_feed_enabled_summary_on">App signature spoofed\n\nSide effects include:\n• Feed videos are missing subtitles\n• Automatically played feed videos will show up in your watch history</string>
<string name="revanced_spoof_signature_in_feed_enabled_summary_off">App signature not spoofed for feed videos\n\nFeed videos will play for less than 1 minute before encountering playback issues</string>
<string name="revanced_spoof_storyboard_title">Spoof storyboard</string>
<string name="revanced_spoof_storyboard_summary_on">Storyboard spoofed</string>
<string name="revanced_spoof_storyboard_summary_off">Storyboard not spoofed\n\nSide effects include:\n• No ambient mode\n• Seekbar thumbnails are hidden</string>
<string name="revanced_spoof_storyboard_timeout">Spoof storyboard temporarily not available (API timed out)</string>
<string name="revanced_spoof_storyboard_io_exception">Spoof storyboard temporarily not available: %s</string>
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
<string name="revanced_hdr_auto_brightness_title">Enable auto HDR brightness</string>
<string name="revanced_hdr_auto_brightness_summary_on">Auto HDR brightness is enabled</string>
<string name="revanced_hdr_auto_brightness_summary_off">Auto HDR brightness is disabled</string>
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
@@ -997,6 +1134,7 @@
<string name="revanced_auto_claim_channel_points_summary_off">Channel Points are not claimed automatically</string>
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
<string name="revanced_twitch_debug_mode_title">Enable Twitch debug mode</string>
<string name="revanced_twitch_debug_mode_summary_on">Twitch debug mode is enabled (not recommended)</string>
<string name="revanced_twitch_debug_mode_summary_off">Twitch debug mode is disabled</string>

View File

@@ -1,10 +1,30 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!--
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/content_copy/materialsymbolsoutlined/content_copy_wght200gradN25_24px.xml
The icon has been resized
Copyright 2022 Google
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#FFFFFF"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M 6.8 19.5 C 6.5 19.5 6.2 19.399 6 19.199 C 5.8 19 5.7 18.699 5.7 18.399 L 5.7 7.099 L 6.8 7.099 L 6.8 18.399 L 15.7 18.399 L 15.7 19.5 L 6.8 19.5 Z M 9 17.2 C 8.7 17.2 8.4 17.1 8.2 16.9 C 8 16.7 7.9 16.4 7.9 16.1 L 7.9 5.6 C 7.9 5.3 8 5 8.2 4.8 C 8.4 4.6 8.7 4.5 9 4.5 L 17.2 4.5 C 17.5 4.5 17.8 4.6 18 4.8 C 18.2 5 18.3 5.3 18.3 5.6 L 18.3 16.1 C 18.3 16.4 18.2 16.7 18 16.9 C 17.8 17.1 17.5 17.2 17.2 17.2 L 9 17.2 Z M 9 16.1 L 17.2 16.1 L 17.2 5.6 L 9 5.6 L 9 16.1 Z M 9 16.1 L 9 5.6 L 9 16.1 Z"
android:fillColor="#ffffffff"/>
android:fillColor="@android:color/white"
android:pathData="m 9.7348145,16.94238 q -0.5966913,0 -1.0062178,-0.39988 Q 8.319101,16.14259 8.319101,15.5599 V 5.88248 q 0,-0.58269 0.4094957,-0.9826 Q 9.1381232,4.5 9.7348145,4.5 h 7.1494615 q 0.596701,0 1.006218,0.39988 Q 18.3,5.29979 18.3,5.88248 v 9.67742 q 0,0.58269 -0.409506,0.9826 -0.409517,0.39988 -1.006218,0.39988 z m 0,-0.82949 h 7.1494615 q 0.212367,0 0.389322,-0.1728 0.176965,-0.17283 0.176965,-0.38019 V 5.88248 q 0,-0.20738 -0.176965,-0.38019 -0.176955,-0.1728 -0.389322,-0.1728 H 9.7348145 q -0.212336,0 -0.389322,0.1728 Q 9.1685373,5.6751 9.1685373,5.88248 v 9.67742 q 0,0.20736 0.1769552,0.38019 0.176986,0.1728 0.389322,0.1728 z M 7.1157237,19.5 Q 6.519022,19.5 6.1094957,19.10009 5.7,18.70021 5.7,18.1175 V 7.61058 H 6.5494363 V 18.1175 q 0,0.20738 0.1769552,0.38018 0.1769655,0.17281 0.3893322,0.17281 H 15.114611 V 19.5 Z M 9.1685373,16.11289 V 5.32949 Z"/>
</vector>

View File

@@ -1,11 +1,31 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!--
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/content_copy/materialsymbolsoutlined/content_copy_wght200gradN25_24px.xml
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/schedule/materialsymbolsoutlined/schedule_wght300_24px.xml
This icon is the result of a combination of "content copy" and "schedule" icons.
Copyright 2022 Google
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#FFFFFF"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:pathData="M 6.8 19.5 C 6.5 19.5 6.2 19.4 6 19.2 C 5.8 19 5.7 18.7 5.7 18.4 L 5.7 7.1 L 6.8 7.1 L 6.8 18.4 L 15.7 18.4 L 15.7 19.5 L 6.8 19.5 Z M 9 17.2 C 8.7 17.2 8.4 17.1 8.2 16.9 C 8 16.7 7.9 16.4 7.9 16.1 L 7.9 5.6 C 7.9 5.3 8 5 8.2 4.8 C 8.4 4.6 8.7 4.5 9 4.5 L 17.2 4.5 C 17.5 4.5 17.8 4.6 18 4.8 C 18.2 5 18.3 5.3 18.3 5.6 L 18.3 16.1 C 18.3 16.4 18.2 16.7 18 16.9 C 17.8 17.1 17.5 17.2 17.2 17.2 L 9 17.2 Z M 9 16.1 L 17.2 16.1 L 17.2 5.6 L 9 5.6 L 9 16.1 Z M 9 16.1 L 9 5.6 L 9 16.1 Z M 13.1 8 C 11.5 8 10.2 9.3 10.2 10.9 C 10.2 12.5 11.5 13.8 13.1 13.8 C 14.7 13.8 16 12.5 16 10.9 C 16 9.3 14.7 8 13.1 8 Z M 13.1 13.3 C 11.8 13.3 10.7 12.2 10.7 10.9 C 10.7 9.6 11.8 8.5 13.1 8.5 C 14.4 8.5 15.5 9.6 15.5 10.9 C 15.5 12.2 14.5 13.3 13.1 13.3 Z M 14.5 12 L 14.2 12.3 L 12.9 11 L 12.9 9.4 L 13.3 9.4 L 13.3 10.8 L 14.5 12 Z"
android:fillColor="#ffffffff" />
android:fillColor="@android:color/white"
android:pathData="M 9.734375 4.5 C 9.3365813 4.5 9.0015328 4.6338045 8.7285156 4.9003906 C 8.4555191 5.1669968 8.3183594 5.4943533 8.3183594 5.8828125 L 8.3183594 15.560547 C 8.3183594 15.949005 8.4555191 16.276362 8.7285156 16.542969 C 9.0015328 16.809554 9.3365813 16.941406 9.734375 16.941406 L 11.259766 16.941406 C 11.220306 16.699026 11.199219 16.450296 11.199219 16.195312 C 11.199219 16.167903 11.200672 16.140551 11.201172 16.113281 L 9.734375 16.113281 C 9.5928176 16.113281 9.4636936 16.054651 9.3457031 15.939453 C 9.2277332 15.824233 9.1679687 15.698787 9.1679688 15.560547 L 9.1679688 5.8828125 C 9.1679688 5.7445594 9.2277329 5.6171596 9.3457031 5.5019531 C 9.4636936 5.3867533 9.5928176 5.3300781 9.734375 5.3300781 L 16.884766 5.3300781 C 17.026342 5.3300781 17.155468 5.3867532 17.273438 5.5019531 C 17.391413 5.6171596 17.451172 5.7445594 17.451172 5.8828125 L 17.451172 12.052734 C 17.453072 12.053553 17.455131 12.053788 17.457031 12.054688 C 17.760014 12.185563 18.041487 12.342729 18.300781 12.525391 L 18.300781 5.8828125 C 18.300781 5.4943533 18.163629 5.1669968 17.890625 4.9003906 C 17.617573 4.6338045 17.282565 4.5 16.884766 4.5 L 9.734375 4.5 z M 5.6992188 7.6113281 L 5.6992188 18.117188 C 5.6992188 18.505659 5.8363784 18.833024 6.109375 19.099609 C 6.382392 19.366216 6.717434 19.5 7.1152344 19.5 L 12.642578 19.5 C 12.600698 19.46116 12.558138 19.42142 12.517578 19.380859 C 12.299781 19.163062 12.107686 18.926178 11.939453 18.669922 L 7.1152344 18.669922 C 6.9736568 18.669922 6.8445393 18.613252 6.7265625 18.498047 C 6.6085926 18.382847 6.5488281 18.25544 6.5488281 18.117188 L 6.5488281 7.6113281 L 5.6992188 7.6113281 z M 15.75 12.5 C 15.231251 12.5 14.743359 12.598047 14.287109 12.794922 C 13.83086 12.991797 13.435156 13.260157 13.097656 13.597656 C 12.760157 13.935156 12.491797 14.33086 12.294922 14.787109 C 12.098047 15.243359 12 15.731251 12 16.25 C 12 16.768749 12.098047 17.256641 12.294922 17.712891 C 12.491797 18.16914 12.760157 18.564844 13.097656 18.902344 C 13.435156 19.239843 13.83086 19.508203 14.287109 19.705078 C 14.743359 19.901953 15.231251 20 15.75 20 C 16.268749 20 16.756641 19.901953 17.212891 19.705078 C 17.66914 19.508203 18.064844 19.239843 18.402344 18.902344 C 18.739843 18.564844 19.008203 18.16914 19.205078 17.712891 C 19.401953 17.256641 19.5 16.768749 19.5 16.25 C 19.5 15.731251 19.401953 15.243359 19.205078 14.787109 C 19.008203 14.33086 18.739843 13.935156 18.402344 13.597656 C 18.064844 13.260157 17.66914 12.991797 17.212891 12.794922 C 16.756641 12.598047 16.268749 12.5 15.75 12.5 z M 15.75 13.25 C 16.581249 13.25 17.288672 13.542578 17.873047 14.126953 C 18.457421 14.711327 18.75 15.418751 18.75 16.25 C 18.75 17.081249 18.457421 17.788672 17.873047 18.373047 C 17.288672 18.957421 16.581249 19.25 15.75 19.25 C 14.918751 19.25 14.211327 18.957421 13.626953 18.373047 C 13.042578 17.788672 12.75 17.081249 12.75 16.25 C 12.75 15.418751 13.042578 14.711327 13.626953 14.126953 C 14.211327 13.542578 14.918751 13.25 15.75 13.25 z M 15.375 14.375 L 15.375 16.400391 L 16.988281 18.011719 L 17.511719 17.488281 L 16.125 16.099609 L 16.125 14.375 L 15.375 14.375 z"/>
</vector>

View File

@@ -1,4 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:tint="#FFFFFF" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="#FFFFFF" android:pathData="M 6.0050001,16.257111 h 0.916667 v 1.666667 H 16.088333 V 16.257111 H 17.005 v 2.777778 H 6.0050001 Z M 16.5,11.4 15.8,10.7 12,14.4 V 4 h -1 v 10.4 l -3.8,-3.8 -0.7,0.7 5,5 z"/>
<!--
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/download/materialsymbolsoutlined/download_wght200gradN25_24px.xml
The icon has been resized
Copyright 2022 Google
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#FFFFFF"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M 12,15.834271 8.2721019,12.095857 8.9799906,11.392186 11.499655,13.91185 V 4.5 h 1.011235 v 9.41185 l 2.519663,-2.519664 0.707862,0.703671 z M 6.1825058,19.5 Q 5.4750279,19.5 4.9875002,19.0125 4.5,18.524973 4.5,17.81607 v -2.619082 h 1.0112344 v 2.617631 q 0,0.252815 0.2106659,0.46348 0.2106658,0.210666 0.4634813,0.210666 H 17.814619 q 0.252815,0 0.46348,-0.210666 0.210666,-0.210665 0.210666,-0.46348 V 15.196988 H 19.5 v 2.619082 q 0,0.708903 -0.4875,1.19643 Q 18.524973,19.5 17.817522,19.5 Z"/>
</vector>

View File

@@ -1,10 +1,30 @@
<!--
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/adjust/materialsymbolsoutlined/adjust_wght200gradN25_24px.xml
The icon has been resized
Copyright 2022 Google
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
android:tint="#FFFFFF"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM15,12c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3z" />
android:fillColor="@android:color/white"
android:pathData="m 12,14.156532 q 0.913052,0 1.534792,-0.62174 0.62174,-0.62174 0.62174,-1.534792 0,-0.913052 -0.62174,-1.534792 Q 12.913052,9.843468 12,9.843468 q -0.913052,0 -1.534792,0.62174 -0.62174,0.62174 -0.62174,1.534792 0,0.913052 0.62174,1.534792 0.62174,0.62174 1.534792,0.62174 z M 12.0052,22 Q 9.930936,22 8.1033049,21.211917 6.2756454,20.423835 4.9237012,19.072851 3.5717572,17.721839 2.7858786,15.899494 2,14.077147 2,12.005172 2,9.930908 2.788083,8.1032766 3.576166,6.2756454 4.9271491,4.9237012 6.2781605,3.5717572 8.100507,2.7858786 9.922853,2 11.994828,2 q 2.074264,0 3.901896,0.788083 1.827631,0.788083 3.179575,2.1390661 1.351944,1.3510114 2.137823,3.1733579 Q 22,9.922853 22,11.994828 q 0,2.074264 -0.788083,3.901896 -0.788082,1.827631 -2.139066,3.179575 -1.351012,1.351944 -3.173357,2.137823 Q 14.077147,22 12.005172,22 Z m -0.0059,-1.043476 q 3.738745,0 6.347988,-2.608508 2.609242,-2.608536 2.609242,-6.347309 0,-3.7387455 -2.608508,-6.347988 -2.608536,-2.6092425 -6.347309,-2.6092425 -3.7387453,0 -6.3479878,2.6085077 -2.6092425,2.6085359 -2.6092425,6.3473088 0,3.738746 2.6085077,6.347988 2.608536,2.609243 6.3473096,2.609243 z M 12,12 Z"/>
</vector>

View File

@@ -0,0 +1,30 @@
<!--
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/fast_forward/materialsymbolsoutlined/fast_forward_wght200gradN25_24px.xml
The icon has been mirrored and resized
Copyright 2022 Google
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#FFFFFF"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M 22,17.799197 V 6.2 l -8.689607,5.799598 z m -11.310393,0 V 6.2 L 2,11.999598 Z m 10.114551,-5.799599 z m -11.3103926,0 z m 11.3103926,3.582383 -5.351233,-3.582383 5.351233,-3.582384 z m -11.3103926,0 -5.3512316,-3.582383 5.3512316,-3.582384 z"/>
</vector>

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