mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-26 02:44:08 +01:00
Compare commits
56 Commits
v5.21.0-de
...
v5.24.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2593c004f4 | ||
|
|
db68c41d5e | ||
|
|
a4f9cb3cef | ||
|
|
9aec1999bb | ||
|
|
26ecbe646e | ||
|
|
46ba0d8a2e | ||
|
|
f454183646 | ||
|
|
d2b440d800 | ||
|
|
494c5f04a4 | ||
|
|
48d5fdf7e1 | ||
|
|
887c9f0d75 | ||
|
|
7de4c9d41d | ||
|
|
7d3b8d9c42 | ||
|
|
25e1a965d6 | ||
|
|
b29c01cee1 | ||
|
|
639850471b | ||
|
|
796c118fe1 | ||
|
|
edf20e397d | ||
|
|
5f0541407c | ||
|
|
56b7ba9ba7 | ||
|
|
f8bdf744ab | ||
|
|
f4f36ff273 | ||
|
|
5028c1acb3 | ||
|
|
555c9a5823 | ||
|
|
777957e2d0 | ||
|
|
b3316a5915 | ||
|
|
2ca2bb7692 | ||
|
|
23fd720fa7 | ||
|
|
1f08586ae8 | ||
|
|
60fdf4c44c | ||
|
|
63f3342815 | ||
|
|
858c59d728 | ||
|
|
5debf9936d | ||
|
|
f1b85d20a1 | ||
|
|
37d0de5e93 | ||
|
|
96d08d5eb7 | ||
|
|
9b1013e1c2 | ||
|
|
75d6cd7c7b | ||
|
|
5a17f5e1c1 | ||
|
|
1d16de6617 | ||
|
|
aee7cba46d | ||
|
|
ec3faf30a8 | ||
|
|
45b5a51da3 | ||
|
|
8abf176bc9 | ||
|
|
ef35ed7335 | ||
|
|
4fd666b667 | ||
|
|
72e0c01922 | ||
|
|
f69eab3e3b | ||
|
|
7c5c2d95bc | ||
|
|
b2453fecfc | ||
|
|
0d54f8bd80 | ||
|
|
fda16fad1a | ||
|
|
ddd43acd73 | ||
|
|
3451318d53 | ||
|
|
2d94ba9df6 | ||
|
|
aaf3437a5a |
184
CHANGELOG.md
184
CHANGELOG.md
@@ -1,3 +1,187 @@
|
||||
# [5.24.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.24.0-dev.3...v5.24.0-dev.4) (2025-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Fix third party launchers widgets` patch ([#4893](https://github.com/ReVanced/revanced-patches/issues/4893)) ([23bfdc9](https://github.com/ReVanced/revanced-patches/commit/23bfdc98fbbcc8ecf0ffbf8704f58dd2272e4af2))
|
||||
|
||||
# [5.24.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.24.0-dev.2...v5.24.0-dev.3) (2025-05-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Fix `Hide video recommendation labels` ([#4956](https://github.com/ReVanced/revanced-patches/issues/4956)) ([ae05ac3](https://github.com/ReVanced/revanced-patches/commit/ae05ac38151ebd3197953af97ca0dd847a04cc2d))
|
||||
|
||||
# [5.24.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.24.0-dev.1...v5.24.0-dev.2) (2025-05-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **GmsCore support:** Open vendor specific DontKillMyApp if available ([#4952](https://github.com/ReVanced/revanced-patches/issues/4952)) ([b89927a](https://github.com/ReVanced/revanced-patches/commit/b89927a10e3b909a3c37fbb75c16a7abbce44560))
|
||||
* **YouTube - Hide player components:** Hide related video overlay in fullscreen ([#4938](https://github.com/ReVanced/revanced-patches/issues/4938)) ([ac9be97](https://github.com/ReVanced/revanced-patches/commit/ac9be9760c9965e54df196b227a310d64ead4bf5))
|
||||
|
||||
# [5.24.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.23.0...v5.24.0-dev.1) (2025-05-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **NU.nl:** Support version `11.3.0` ([#4925](https://github.com/ReVanced/revanced-patches/issues/4925)) ([bedde60](https://github.com/ReVanced/revanced-patches/commit/bedde60fc1a52b0fd491174b3b5b887435eb621a))
|
||||
|
||||
# [5.23.0](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0) (2025-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
|
||||
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([49ca329](https://github.com/ReVanced/revanced-patches/commit/49ca3290a726cdba7bc9b62ffcd8d46e6f04778e))
|
||||
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([00aa200](https://github.com/ReVanced/revanced-patches/commit/00aa2000ba2eef15a0dd827c2bd84c2e85c412e0))
|
||||
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
|
||||
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
|
||||
* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([0cf7a4c](https://github.com/ReVanced/revanced-patches/commit/0cf7a4c6be615ed0a52a6bacf87592f5f43ff575))
|
||||
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([bb672c4](https://github.com/ReVanced/revanced-patches/commit/bb672c4674ddc201b8b2648c3906cfc31ef43f10))
|
||||
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([2e3511d](https://github.com/ReVanced/revanced-patches/commit/2e3511d03c8198bbdb9336888df038a33fb3ab8c))
|
||||
|
||||
# [5.23.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.6...v5.23.0-dev.7) (2025-05-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([49ca329](https://github.com/ReVanced/revanced-patches/commit/49ca3290a726cdba7bc9b62ffcd8d46e6f04778e))
|
||||
|
||||
# [5.23.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.5...v5.23.0-dev.6) (2025-05-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
|
||||
|
||||
# [5.23.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.4...v5.23.0-dev.5) (2025-05-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([00aa200](https://github.com/ReVanced/revanced-patches/commit/00aa2000ba2eef15a0dd827c2bd84c2e85c412e0))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([0cf7a4c](https://github.com/ReVanced/revanced-patches/commit/0cf7a4c6be615ed0a52a6bacf87592f5f43ff575))
|
||||
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([bb672c4](https://github.com/ReVanced/revanced-patches/commit/bb672c4674ddc201b8b2648c3906cfc31ef43f10))
|
||||
|
||||
# [5.23.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.3...v5.23.0-dev.4) (2025-05-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([2e3511d](https://github.com/ReVanced/revanced-patches/commit/2e3511d03c8198bbdb9336888df038a33fb3ab8c))
|
||||
|
||||
# [5.23.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.2...v5.23.0-dev.3) (2025-05-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
|
||||
|
||||
# [5.23.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.1...v5.23.0-dev.2) (2025-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
|
||||
|
||||
# [5.23.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0-dev.1) (2025-05-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
|
||||
|
||||
# [5.22.0](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0) (2025-05-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
|
||||
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
|
||||
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
|
||||
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
|
||||
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
|
||||
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
|
||||
|
||||
# [5.22.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.3...v5.22.0-dev.4) (2025-04-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
|
||||
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
|
||||
|
||||
# [5.22.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.2...v5.22.0-dev.3) (2025-04-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
|
||||
|
||||
# [5.22.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.1...v5.22.0-dev.2) (2025-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
|
||||
|
||||
# [5.22.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0-dev.1) (2025-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
|
||||
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
|
||||
|
||||
# [5.21.0](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0) (2025-04-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
|
||||
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
|
||||
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
|
||||
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
|
||||
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
|
||||
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
|
||||
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
|
||||
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
|
||||
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
|
||||
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
|
||||
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
|
||||
|
||||
# [5.21.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.11...v5.21.0-dev.12) (2025-04-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
|
||||
|
||||
# [5.21.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.10...v5.21.0-dev.11) (2025-04-24)
|
||||
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public class HideAdsPatch {
|
||||
|
||||
// Filter HeaderBlock with known ads until next HeaderBlock.
|
||||
if (currentBlock instanceof HeaderBlock headerBlock) {
|
||||
StyledText headerText = headerBlock.component20();
|
||||
StyledText headerText = headerBlock.getTitle();
|
||||
if (headerText != null) {
|
||||
skipFullHeader = false;
|
||||
for (String blockedHeaderBlock : blockedHeaderBlocks) {
|
||||
|
||||
@@ -3,8 +3,7 @@ package nl.nu.performance.api.client.objects;
|
||||
import nl.nu.performance.api.client.interfaces.Block;
|
||||
|
||||
public class HeaderBlock extends Block {
|
||||
// returns title
|
||||
public final StyledText component20() {
|
||||
public final StyledText getTitle() {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
}
|
||||
|
||||
4
extensions/primevideo/build.gradle.kts
Normal file
4
extensions/primevideo/build.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:primevideo:stub"))
|
||||
}
|
||||
1
extensions/primevideo/src/main/AndroidManifest.xml
Normal file
1
extensions/primevideo/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,36 @@
|
||||
package app.revanced.extension.primevideo.ads;
|
||||
|
||||
import com.amazon.avod.fsm.SimpleTrigger;
|
||||
import com.amazon.avod.media.ads.AdBreak;
|
||||
import com.amazon.avod.media.ads.internal.state.AdBreakTrigger;
|
||||
import com.amazon.avod.media.ads.internal.state.AdEnabledPlayerTriggerType;
|
||||
import com.amazon.avod.media.playback.VideoPlayer;
|
||||
import com.amazon.avod.media.ads.internal.state.ServerInsertedAdBreakState;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class SkipAdsPatch {
|
||||
public static void enterServerInsertedAdBreakState(ServerInsertedAdBreakState state, AdBreakTrigger trigger, VideoPlayer player) {
|
||||
try {
|
||||
AdBreak adBreak = trigger.getBreak();
|
||||
|
||||
// There are two scenarios when entering the original method:
|
||||
// 1. Player naturally entered an ad break while watching a video.
|
||||
// 2. User is skipped/scrubbed to a position on the timeline. If seek position is past an ad break,
|
||||
// user is forced to watch an ad before continuing.
|
||||
//
|
||||
// Scenario 2 is indicated by trigger.getSeekStartPosition() != null, so skip directly to the scrubbing
|
||||
// target. Otherwise, just calculate when the ad break should end and skip to there.
|
||||
if (trigger.getSeekStartPosition() != null)
|
||||
player.seekTo(trigger.getSeekTarget().getTotalMilliseconds());
|
||||
else
|
||||
player.seekTo(player.getCurrentPosition() + adBreak.getDurationExcludingAux().getTotalMilliseconds());
|
||||
|
||||
// Send "end of ads" trigger to state machine so everything doesn't get whacky.
|
||||
state.doTrigger(new SimpleTrigger(AdEnabledPlayerTriggerType.NO_MORE_ADS_SKIP_TRANSITION));
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Failed skipping ads", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
extensions/primevideo/stub/build.gradle.kts
Normal file
17
extensions/primevideo/stub/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
1
extensions/primevideo/stub/src/main/AndroidManifest.xml
Normal file
1
extensions/primevideo/stub/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.amazon.avod.fsm;
|
||||
|
||||
public final class SimpleTrigger<T> implements Trigger<T> {
|
||||
public SimpleTrigger(T triggerType) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.amazon.avod.fsm;
|
||||
|
||||
public abstract class StateBase<S, T> {
|
||||
// This method orginally has protected access (modified in patch code).
|
||||
public void doTrigger(Trigger<T> trigger) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.amazon.avod.fsm;
|
||||
|
||||
public interface Trigger<T> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.amazon.avod.media;
|
||||
|
||||
public final class TimeSpan {
|
||||
public long getTotalMilliseconds() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.amazon.avod.media.ads;
|
||||
|
||||
import com.amazon.avod.media.TimeSpan;
|
||||
|
||||
public interface AdBreak {
|
||||
TimeSpan getDurationExcludingAux();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.amazon.avod.media.ads.internal.state;
|
||||
|
||||
public abstract class AdBreakState extends AdEnabledPlaybackState {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.amazon.avod.media.ads.internal.state;
|
||||
|
||||
import com.amazon.avod.media.ads.AdBreak;
|
||||
import com.amazon.avod.media.TimeSpan;
|
||||
|
||||
public class AdBreakTrigger {
|
||||
public AdBreak getBreak() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public TimeSpan getSeekTarget() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public TimeSpan getSeekStartPosition() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.amazon.avod.media.ads.internal.state;
|
||||
|
||||
import com.amazon.avod.fsm.StateBase;
|
||||
import com.amazon.avod.media.playback.state.PlayerStateType;
|
||||
import com.amazon.avod.media.playback.state.trigger.PlayerTriggerType;
|
||||
|
||||
public class AdEnabledPlaybackState extends StateBase<PlayerStateType, PlayerTriggerType> {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.amazon.avod.media.ads.internal.state;
|
||||
|
||||
public enum AdEnabledPlayerTriggerType {
|
||||
NO_MORE_ADS_SKIP_TRANSITION
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.amazon.avod.media.ads.internal.state;
|
||||
|
||||
public class ServerInsertedAdBreakState extends AdBreakState {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.amazon.avod.media.playback;
|
||||
|
||||
public interface VideoPlayer {
|
||||
long getCurrentPosition();
|
||||
|
||||
void seekTo(long positionMs);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.amazon.avod.media.playback.state;
|
||||
|
||||
public interface PlayerStateType {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.amazon.avod.media.playback.state.trigger;
|
||||
|
||||
public interface PlayerTriggerType {
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.extension.shared;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
@@ -15,14 +16,18 @@ import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
import app.revanced.extension.shared.requests.Requester;
|
||||
import app.revanced.extension.shared.requests.Route;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GmsCoreSupport {
|
||||
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
|
||||
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";
|
||||
@@ -31,10 +36,24 @@ public class GmsCoreSupport {
|
||||
= getGmsCoreVendorGroupId() + ".android.gms";
|
||||
private static final Uri GMS_CORE_PROVIDER
|
||||
= Uri.parse("content://" + getGmsCoreVendorGroupId() + ".android.gsf.gservices/prefix");
|
||||
private static final String DONT_KILL_MY_APP_LINK
|
||||
= "https://dontkillmyapp.com";
|
||||
private static final String DONT_KILL_MY_APP_URL
|
||||
= "https://dontkillmyapp.com/";
|
||||
private static final Route DONT_KILL_MY_APP_MANUFACTURER_API
|
||||
= new Route(GET, "/api/v2/{manufacturer}.json");
|
||||
private static final String DONT_KILL_MY_APP_NAME_PARAMETER
|
||||
= "?app=MicroG";
|
||||
private static final String BUILD_MANUFACTURER
|
||||
= Build.MANUFACTURER.toLowerCase(Locale.ROOT).replace(" ", "-");
|
||||
|
||||
/**
|
||||
* If a manufacturer specific page exists on DontKillMyApp.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
|
||||
private static void open(String queryOrLink) {
|
||||
Logger.printInfo(() -> "Opening link: " + queryOrLink);
|
||||
|
||||
Intent intent;
|
||||
try {
|
||||
// Check if queryOrLink is a valid URL.
|
||||
@@ -88,7 +107,7 @@ public class GmsCoreSupport {
|
||||
|
||||
// Do not exit. If the app exits before launch completes (and without
|
||||
// opening another activity), then on some devices such as Pixel phone Android 10
|
||||
// no toast will be shown and the app will continually be relaunched
|
||||
// no toast will be shown and the app will continually relaunch
|
||||
// with the appearance of a hung app.
|
||||
}
|
||||
|
||||
@@ -124,11 +143,12 @@ public class GmsCoreSupport {
|
||||
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
|
||||
if (client == null) {
|
||||
Logger.printInfo(() -> "GmsCore is not running in the background");
|
||||
checkIfDontKillMyAppSupportsManufacturer();
|
||||
|
||||
showBatteryOptimizationDialog(context,
|
||||
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
||||
"gms_core_dialog_open_website_text",
|
||||
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
|
||||
(dialog, id) -> openDontKillMyApp());
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@@ -143,6 +163,48 @@ public class GmsCoreSupport {
|
||||
activity.startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
private static void checkIfDontKillMyAppSupportsManufacturer() {
|
||||
Utils.runOnBackgroundThread(() -> {
|
||||
try {
|
||||
final long start = System.currentTimeMillis();
|
||||
HttpURLConnection connection = Requester.getConnectionFromRoute(
|
||||
DONT_KILL_MY_APP_URL, DONT_KILL_MY_APP_MANUFACTURER_API, BUILD_MANUFACTURER);
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
|
||||
final boolean supported = connection.getResponseCode() == 200;
|
||||
Logger.printInfo(() -> "Manufacturer is " + (supported ? "" : "NOT ")
|
||||
+ "listed on DontKillMyApp: " + BUILD_MANUFACTURER
|
||||
+ " fetch took: " + (System.currentTimeMillis() - start) + "ms");
|
||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = supported;
|
||||
} catch (Exception ex) {
|
||||
Logger.printInfo(() -> "Could not check if manufacturer is listed on DontKillMyApp: "
|
||||
+ BUILD_MANUFACTURER, ex);
|
||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void openDontKillMyApp() {
|
||||
final Boolean manufacturerSupported = DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
|
||||
String manufacturerPageToOpen;
|
||||
if (manufacturerSupported == null) {
|
||||
// Fetch has not completed yet. Only happens on extremely slow internet connections
|
||||
// and the user spends less than 1 second reading what's on screen.
|
||||
// Instead of waiting for the fetch (which may timeout),
|
||||
// open the website without a vendor.
|
||||
manufacturerPageToOpen = "";
|
||||
} else if (manufacturerSupported) {
|
||||
manufacturerPageToOpen = BUILD_MANUFACTURER;
|
||||
} else {
|
||||
// No manufacturer specific page exists. Open the general page.
|
||||
manufacturerPageToOpen = "general";
|
||||
}
|
||||
|
||||
open(DONT_KILL_MY_APP_URL + manufacturerPageToOpen + DONT_KILL_MY_APP_NAME_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If GmsCore is not whitelisted from battery optimizations.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package app.revanced.extension.spotify.misc.privacy;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class SanitizeSharingLinksPatch {
|
||||
|
||||
/**
|
||||
* Parameters that are considered undesirable and should be stripped away.
|
||||
*/
|
||||
private static final List<String> SHARE_PARAMETERS_TO_REMOVE = List.of(
|
||||
"si", // Share tracking parameter.
|
||||
"utm_source" // Share source, such as "copy-link".
|
||||
);
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String sanitizeUrl(String url) {
|
||||
try {
|
||||
Uri uri = Uri.parse(url);
|
||||
Uri.Builder builder = uri.buildUpon().clearQuery();
|
||||
|
||||
for (String paramName : uri.getQueryParameterNames()) {
|
||||
if (!SHARE_PARAMETERS_TO_REMOVE.contains(paramName)) {
|
||||
for (String value : uri.getQueryParameters(paramName)) {
|
||||
builder.appendQueryParameter(paramName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build().toString();
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "sanitizeUrl failure", ex);
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package app.revanced.extension.tiktok.feedfilter;
|
||||
|
||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||
import com.ss.android.ugc.aweme.feed.model.FeedItemList;
|
||||
import com.ss.android.ugc.aweme.follow.presenter.FollowFeedList;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -13,22 +14,41 @@ public final class FeedItemsFilter {
|
||||
new StoryFilter(),
|
||||
new ImageVideoFilter(),
|
||||
new ViewCountFilter(),
|
||||
new LikeCountFilter()
|
||||
new LikeCountFilter(),
|
||||
new ShopFilter()
|
||||
);
|
||||
|
||||
public static void filter(FeedItemList feedItemList) {
|
||||
Iterator<Aweme> feedItemListIterator = feedItemList.items.iterator();
|
||||
while (feedItemListIterator.hasNext()) {
|
||||
Aweme item = feedItemListIterator.next();
|
||||
if (item == null) continue;
|
||||
filterFeedList(feedItemList.items, item -> item);
|
||||
}
|
||||
|
||||
for (IFilter filter : FILTERS) {
|
||||
boolean enabled = filter.getEnabled();
|
||||
if (enabled && filter.getFiltered(item)) {
|
||||
feedItemListIterator.remove();
|
||||
break;
|
||||
}
|
||||
public static void filter(FollowFeedList followFeedList) {
|
||||
filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null);
|
||||
}
|
||||
|
||||
private static <T> void filterFeedList(List<T> list, AwemeExtractor<T> extractor) {
|
||||
// Could be simplified with removeIf() but requires Android 7.0+ while TikTok supports 4.0+.
|
||||
Iterator<T> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
T container = iterator.next();
|
||||
Aweme item = extractor.extract(container);
|
||||
if (item != null && shouldFilter(item)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldFilter(Aweme item) {
|
||||
for (IFilter filter : FILTERS) {
|
||||
if (filter.getEnabled() && filter.getFiltered(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface AwemeExtractor<T> {
|
||||
Aweme extract(T source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.extension.tiktok.feedfilter;
|
||||
|
||||
import app.revanced.extension.tiktok.settings.Settings;
|
||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||
|
||||
public class ShopFilter implements IFilter {
|
||||
private static final String SHOP_INFO = "placeholder_product_id";
|
||||
@Override
|
||||
public boolean getEnabled() {
|
||||
return Settings.HIDE_SHOP.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFiltered(Aweme item) {
|
||||
return item.getShareUrl().contains(SHOP_INFO);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import app.revanced.extension.shared.settings.StringSetting;
|
||||
public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting REMOVE_ADS = new BooleanSetting("remove_ads", TRUE, true);
|
||||
public static final BooleanSetting HIDE_LIVE = new BooleanSetting("hide_live", FALSE, true);
|
||||
public static final BooleanSetting HIDE_SHOP = new BooleanSetting("hide_shop", FALSE, true);
|
||||
public static final BooleanSetting HIDE_STORY = new BooleanSetting("hide_story", FALSE, true);
|
||||
public static final BooleanSetting HIDE_IMAGE = new BooleanSetting("hide_image", FALSE, true);
|
||||
public static final StringSetting MIN_MAX_VIEWS = new StringSetting("min_max_views", "0-" + Long.MAX_VALUE, true);
|
||||
|
||||
@@ -26,6 +26,11 @@ public class FeedFilterPreferenceCategory extends ConditionalPreferenceCategory
|
||||
"Remove feed ads", "Remove ads from feed.",
|
||||
Settings.REMOVE_ADS
|
||||
));
|
||||
addPreference(new TogglePreference(
|
||||
context,
|
||||
"Hide TikTok Shop", "Hide TikTok shop from feed.",
|
||||
Settings.HIDE_SHOP
|
||||
));
|
||||
addPreference(new TogglePreference(
|
||||
context,
|
||||
"Hide livestreams", "Hide livestreams from feed.",
|
||||
|
||||
@@ -33,4 +33,8 @@ public class Aweme {
|
||||
public AwemeStatistics getStatistics() {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
|
||||
public String getShareUrl() {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ss.android.ugc.aweme.follow.presenter;
|
||||
|
||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||
|
||||
//Dummy class
|
||||
public class FollowFeed {
|
||||
public Aweme aweme;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ss.android.ugc.aweme.follow.presenter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//Dummy class
|
||||
public class FollowFeedList {
|
||||
public List<FollowFeed> mItems;
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package app.revanced.extension.youtube;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -102,4 +104,21 @@ public class ThemeHelper {
|
||||
|
||||
return Utils.getColorFromString(colorName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the system navigation bar color for the activity.
|
||||
* Applies the background color obtained from {@link #getBackgroundColor()} to the navigation bar.
|
||||
* For Android 10 (API 29) and above, enforces navigation bar contrast to ensure visibility.
|
||||
*/
|
||||
public static void setNavigationBarColor(@Nullable Window window) {
|
||||
if (window == null) {
|
||||
Logger.printDebug(() -> "Cannot set navigation bar color, window is null");
|
||||
return;
|
||||
}
|
||||
|
||||
window.setNavigationBarColor(getBackgroundColor());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.setNavigationBarContrastEnforced(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.sf;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class AccountCredentialsInvalidTextPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String getOfflineNetworkErrorString(String original) {
|
||||
try {
|
||||
if (Utils.isNetworkConnected()) {
|
||||
Logger.printDebug(() -> "Network appears to be online, but app is showing offline error");
|
||||
return '\n' + sf("microg_offline_account_login_error").toString();
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "Network is offline");
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "getOfflineNetworkErrorString failure", ex);
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class HideRelatedVideoOverlayPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean hideRelatedVideoOverlay() {
|
||||
return Settings.HIDE_RELATED_VIDEO_OVERLAY.get();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.patches;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -76,7 +78,7 @@ public class ShortsAutoplayPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
||||
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
|
||||
try {
|
||||
final boolean autoplay;
|
||||
|
||||
@@ -98,17 +100,35 @@ public class ShortsAutoplayPatch {
|
||||
: ShortsLoopBehavior.REPEAT;
|
||||
|
||||
if (behavior.ytEnumValue != null) {
|
||||
Logger.printDebug(() -> behavior.ytEnumValue == original
|
||||
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue
|
||||
: "Behavior setting is same as original. Using original: " + original.name()
|
||||
);
|
||||
Logger.printDebug(() -> {
|
||||
String name = (original == null ? "unknown (null)" : original.name());
|
||||
return behavior == original
|
||||
? "Behavior setting is same as original. Using original: " + name
|
||||
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
|
||||
});
|
||||
|
||||
return behavior.ytEnumValue;
|
||||
}
|
||||
|
||||
if (original == null) {
|
||||
// Cannot return null, as null is used to indicate Short was auto played.
|
||||
// Unpatched app replaces null with unknown enum type (appears to fix for bad api data).
|
||||
Enum<?> unknown = ShortsLoopBehavior.UNKNOWN.ytEnumValue;
|
||||
Logger.printDebug(() -> "Original is null, returning: " + unknown.name());
|
||||
return unknown;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "changeShortsRepeatState failure", ex);
|
||||
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean isAutoPlay(Enum<?> original) {
|
||||
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,15 +68,19 @@ final class ButtonsFilter extends Filter {
|
||||
Settings.HIDE_REMIX_BUTTON,
|
||||
"yt_outline_youtube_shorts_plus"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_ASK_BUTTON,
|
||||
"yt_fill_spark"
|
||||
),
|
||||
// Check for clip button both here and using a path filter,
|
||||
// as there's a chance the path is a generic action button and won't contain 'clip_button'
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_CLIP_BUTTON,
|
||||
"yt_outline_scissors"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup singleItemInformationPanel;
|
||||
private final StringFilterGroup expandableMetadata;
|
||||
private final ByteArrayFilterGroup searchResultRecommendations;
|
||||
private final StringFilterGroup searchResultVideo;
|
||||
private final StringFilterGroup compactChannelBarInner;
|
||||
private final StringFilterGroup compactChannelBarInnerButton;
|
||||
private final ByteArrayFilterGroup joinMembershipButton;
|
||||
@@ -75,7 +73,10 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"post_base_wrapper_slim.eml",
|
||||
"poll_post_root.eml",
|
||||
"videos_post_root.eml",
|
||||
"post_shelf_slim.eml"
|
||||
"post_shelf_slim.eml",
|
||||
"videos_post_responsive_root.eml",
|
||||
"text_post_responsive_root.eml",
|
||||
"poll_post_responsive_root.eml"
|
||||
);
|
||||
|
||||
final var communityGuidelines = new StringFilterGroup(
|
||||
@@ -230,14 +231,9 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"mixed_content_shelf"
|
||||
);
|
||||
|
||||
searchResultVideo = new StringFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_RECOMMENDATIONS,
|
||||
"search_video_with_context.eml"
|
||||
);
|
||||
|
||||
searchResultRecommendations = new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_RECOMMENDATIONS,
|
||||
"endorsement_header_footer"
|
||||
final var searchResultRecommendationLabels = new StringFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_RECOMMENDATION_LABELS,
|
||||
"endorsement_header_footer.eml"
|
||||
);
|
||||
|
||||
horizontalShelves = new StringFilterGroup(
|
||||
@@ -255,7 +251,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
compactChannelBar,
|
||||
communityPosts,
|
||||
paidPromotion,
|
||||
searchResultVideo,
|
||||
searchResultRecommendationLabels,
|
||||
latestPosts,
|
||||
channelWatermark,
|
||||
communityGuidelines,
|
||||
@@ -297,13 +293,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedGroup == searchResultVideo) {
|
||||
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The groups are excluded from the filter due to the exceptions list below.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
|
||||
@@ -87,6 +87,10 @@ public final class LithoFilterPatch {
|
||||
* the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads.
|
||||
*/
|
||||
private static final ThreadLocal<ByteBuffer> bufferThreadLocal = new ThreadLocal<>();
|
||||
/**
|
||||
* Results of calling {@link #filter(String, StringBuilder)}.
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> filterResult = new ThreadLocal<>();
|
||||
|
||||
static {
|
||||
for (Filter filter : filters) {
|
||||
@@ -140,11 +144,22 @@ public final class LithoFilterPatch {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean shouldFilter() {
|
||||
Boolean shouldFilter = filterResult.get();
|
||||
return shouldFilter != null && shouldFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Called off the main thread, and commonly called by multiple threads at the same time.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) {
|
||||
public static void filter(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
|
||||
filterResult.set(handleFiltering(lithoIdentifier, pathBuilder));
|
||||
}
|
||||
|
||||
private static boolean handleFiltering(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
|
||||
try {
|
||||
if (pathBuilder.length() == 0) {
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.view.View;
|
||||
@@ -52,6 +51,7 @@ public final class ShortsFilter extends Filter {
|
||||
private final StringFilterGroup suggestedAction;
|
||||
private final ByteArrayFilterGroupList suggestedActionsGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
private final StringFilterGroup shortsActionBar;
|
||||
private final StringFilterGroup actionButton;
|
||||
private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
@@ -141,6 +141,16 @@ public final class ShortsFilter extends Filter {
|
||||
"like_fountain.eml"
|
||||
);
|
||||
|
||||
StringFilterGroup likeButton = new StringFilterGroup(
|
||||
Settings.HIDE_SHORTS_LIKE_BUTTON,
|
||||
"shorts_like_button.eml"
|
||||
);
|
||||
|
||||
StringFilterGroup dislikeButton = new StringFilterGroup(
|
||||
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
|
||||
"shorts_dislike_button.eml"
|
||||
);
|
||||
|
||||
joinButton = new StringFilterGroup(
|
||||
Settings.HIDE_SHORTS_JOIN_BUTTON,
|
||||
"sponsor_button"
|
||||
@@ -156,9 +166,15 @@ public final class ShortsFilter extends Filter {
|
||||
"reel_player_disclosure.eml"
|
||||
);
|
||||
|
||||
shortsActionBar = new StringFilterGroup(
|
||||
null,
|
||||
"shorts_action_bar.eml"
|
||||
);
|
||||
|
||||
actionButton = new StringFilterGroup(
|
||||
null,
|
||||
"shorts_video_action_button.eml"
|
||||
// Can be simply 'button.eml' or 'shorts_video_action_button.eml'
|
||||
"button.eml"
|
||||
);
|
||||
|
||||
suggestedAction = new StringFilterGroup(
|
||||
@@ -167,27 +183,16 @@ public final class ShortsFilter extends Filter {
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
shortsCompactFeedVideoPath, suggestedAction, actionButton, joinButton, subscribeButton,
|
||||
paidPromotionButton, pausedOverlayButtons, channelBar, fullVideoLinkLabel, videoTitle,
|
||||
reelSoundMetadata, soundButton, infoPanel, stickers, likeFountain
|
||||
shortsCompactFeedVideoPath, joinButton, subscribeButton, paidPromotionButton,
|
||||
shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar,
|
||||
fullVideoLinkLabel, videoTitle, reelSoundMetadata, soundButton, infoPanel,
|
||||
stickers, likeFountain, likeButton, dislikeButton
|
||||
);
|
||||
|
||||
//
|
||||
// Action buttons
|
||||
// All other action buttons.
|
||||
//
|
||||
videoActionButtonGroupList.addAll(
|
||||
// This also appears as the path item 'shorts_like_button.eml'
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_LIKE_BUTTON,
|
||||
"reel_like_button",
|
||||
"reel_like_toggled_button"
|
||||
),
|
||||
// This also appears as the path item 'shorts_dislike_button.eml'
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
|
||||
"reel_dislike_button",
|
||||
"reel_dislike_toggled_button"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
|
||||
"reel_comment_button"
|
||||
@@ -286,9 +291,11 @@ public final class ShortsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Video action buttons (like, dislike, comment, share, remix) have the same path.
|
||||
if (matchedGroup == actionButton) {
|
||||
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
|
||||
// Video action buttons (comment, share, remix) have the same path.
|
||||
// Like and dislike are separate path filters and don't require buffer searching.
|
||||
if (matchedGroup == shortsActionBar) {
|
||||
if (actionButton.check(path).isFiltered()
|
||||
&& videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
@@ -392,37 +399,6 @@ public final class ShortsFilter extends Filter {
|
||||
return original;
|
||||
}
|
||||
|
||||
// region Hide the buttons in older versions of YouTube. New versions use Litho.
|
||||
|
||||
public static void hideLikeButton(final View likeButtonView) {
|
||||
// Cannot set the visibility to gone for like/dislike,
|
||||
// as some other unknown YT code also sets the visibility after this hook.
|
||||
//
|
||||
// Setting the view to 0dp works, but that leaves a blank space where
|
||||
// the button was (only relevant for dislikes button).
|
||||
//
|
||||
// Instead remove the view from the parent.
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
|
||||
}
|
||||
|
||||
public static void hideDislikeButton(final View dislikeButtonView) {
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsCommentsButton(final View commentsButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_COMMENTS_BUTTON, commentsButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsRemixButton(final View remixButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_REMIX_BUTTON, remixButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsShareButton(final View shareButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_SHARE_BUTTON, shareButtonView);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
public static void setNavigationBar(PivotBar view) {
|
||||
pivotBarRef = new WeakReference<>(view);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ public class LicenseActivityHook {
|
||||
public static void initialize(Activity licenseActivity) {
|
||||
try {
|
||||
ThemeHelper.setActivityTheme(licenseActivity);
|
||||
ThemeHelper.setNavigationBarColor(licenseActivity.getWindow());
|
||||
licenseActivity.setContentView(getResourceIdentifier(
|
||||
"revanced_settings_with_toolbar", "layout"));
|
||||
|
||||
@@ -126,7 +127,7 @@ public class LicenseActivityHook {
|
||||
// This is required to fix submenu title alignment issue with Android ASOP 15+
|
||||
ViewGroup toolBarParent = activity.findViewById(
|
||||
getResourceIdentifier("revanced_toolbar_parent", "id"));
|
||||
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
|
||||
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent, "revanced_toolbar");
|
||||
toolbarLayoutParams = dummyToolbar.getLayoutParams();
|
||||
toolBarParent.removeView(dummyToolbar);
|
||||
|
||||
@@ -149,5 +150,4 @@ public class LicenseActivityHook {
|
||||
|
||||
toolBarParent.addView(toolbar, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_MOVIES_SECTION = new BooleanSetting("revanced_hide_movies_section", TRUE);
|
||||
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATION_LABELS = new BooleanSetting("revanced_hide_search_result_recommendation_labels", TRUE);
|
||||
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
|
||||
// Alternative thumbnails
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
||||
@@ -139,6 +139,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
||||
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_END_SCREEN_SUGGESTED_VIDEO = new BooleanSetting("revanced_end_screen_suggested_video", FALSE, true);
|
||||
public static final BooleanSetting HIDE_RELATED_VIDEO_OVERLAY = new BooleanSetting("revanced_hide_related_video_overlay", FALSE, true);
|
||||
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
||||
@@ -199,6 +200,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
||||
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
||||
public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE);
|
||||
// Player flyout menu items
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||
@@ -477,4 +479,3 @@ public class Settings extends BaseSettings {
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
.findViewById(android.R.id.content)
|
||||
.getParent();
|
||||
|
||||
// Fix the system navigation bar color for submenus.
|
||||
ThemeHelper.setNavigationBarColor(preferenceScreenDialog.getWindow());
|
||||
|
||||
// Fix edge-to-edge screen with Android 15 and YT 19.45+
|
||||
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.21.0-dev.11
|
||||
version = 5.24.0-dev.4
|
||||
|
||||
@@ -240,6 +240,10 @@ public final class app/revanced/patches/instagram/ads/HideAdsPatchKt {
|
||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/misc/signature/SignatureCheckPatchKt {
|
||||
public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
|
||||
public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -380,6 +384,14 @@ public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatc
|
||||
public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/pandora/ads/DisableAudioAdsPatchKt {
|
||||
public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatchKt {
|
||||
public static final fun getEnableUnlimitedSkipsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt {
|
||||
public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -412,6 +424,14 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
|
||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/primevideo/ads/SkipAdsPatchKt {
|
||||
public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/primevideo/misc/extension/ExtensionPatchKt {
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt {
|
||||
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
@@ -642,14 +662,12 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
|
||||
public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getIcon ()Ljava/lang/String;
|
||||
public final fun getKey ()Ljava/lang/String;
|
||||
public final fun getLayout ()Ljava/lang/String;
|
||||
public final fun getSummaryKey ()Ljava/lang/String;
|
||||
public final fun getTag ()Ljava/lang/String;
|
||||
public final fun getTitleKey ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
|
||||
}
|
||||
|
||||
@@ -854,6 +872,14 @@ public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
|
||||
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgetsKt {
|
||||
public static final fun getFixThirdPartyLaunchersWidgets ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
|
||||
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1198,6 +1224,10 @@ public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupan
|
||||
public static final fun getHidePlayerFlyoutMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatchKt {
|
||||
public static final fun getHideRelatedVideoOverlayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatchKt {
|
||||
public static final fun getDisableRollingNumberAnimationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1536,6 +1566,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
||||
|
||||
public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
||||
|
||||
@@ -9,6 +9,5 @@ internal val adInjectorFingerprint = fingerprint {
|
||||
parameters("L", "L")
|
||||
strings(
|
||||
"SponsoredContentController.insertItem",
|
||||
"SponsoredContentController::Delivery",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package app.revanced.patches.instagram.misc.signature
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val isValidSignatureClassFingerprint = fingerprint {
|
||||
strings("The provider for uri '", "' is not trusted: ")
|
||||
}
|
||||
|
||||
internal val isValidSignatureMethodFingerprint = fingerprint {
|
||||
parameters("L", "Z")
|
||||
returns("Z")
|
||||
custom { method, _ ->
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<MethodReference>()?.name == "keySet"
|
||||
} >= 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package app.revanced.patches.instagram.misc.signature
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val signatureCheckPatch = bytecodePatch(
|
||||
name = "Disable signature check",
|
||||
description = "Disables the signature check that causes the app to crash on startup."
|
||||
) {
|
||||
compatibleWith("com.instagram.android"("378.0.0.52.68"))
|
||||
|
||||
execute {
|
||||
isValidSignatureMethodFingerprint
|
||||
.match(isValidSignatureClassFingerprint.classDef)
|
||||
.method
|
||||
.returnEarly(true)
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.bytecodePatch
|
||||
val disableMandatoryLoginPatch = bytecodePatch(
|
||||
name = "Disable mandatory login",
|
||||
) {
|
||||
compatibleWith("com.adobe.lrmobile")
|
||||
compatibleWith("com.adobe.lrmobile"("10.0.2"))
|
||||
|
||||
execute {
|
||||
isLoggedInFingerprint.method.apply {
|
||||
|
||||
@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.bytecodePatch
|
||||
val unlockPremiumPatch = bytecodePatch(
|
||||
name = "Unlock premium",
|
||||
) {
|
||||
compatibleWith("com.adobe.lrmobile")
|
||||
compatibleWith("com.adobe.lrmobile"("10.0.2"))
|
||||
|
||||
execute {
|
||||
// Set hasPremium = true.
|
||||
|
||||
@@ -4,10 +4,10 @@ import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val jwUtilCreateAdvertisementFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
internal val jwPlayerConfigFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
custom { methodDef, classDef ->
|
||||
classDef.type == "Lnl/sanomamedia/android/nu/video/util/JWUtil;" && methodDef.name == "createAdvertising"
|
||||
classDef.type == "Lcom/jwplayer/pub/api/configuration/PlayerConfig${'$'}Builder;" && methodDef.name == "advertisingConfig"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ package app.revanced.patches.nunl.ads
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -11,23 +14,15 @@ val hideAdsPatch = bytecodePatch(
|
||||
name = "Hide ads",
|
||||
description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.",
|
||||
) {
|
||||
compatibleWith("nl.sanomamedia.android.nu"("11.0.0", "11.0.1", "11.1.0"))
|
||||
compatibleWith("nl.sanomamedia.android.nu"("11.3.0"))
|
||||
|
||||
dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook))
|
||||
|
||||
execute {
|
||||
// Disable video pre-roll ads.
|
||||
// Whenever the app tries to create an ad via JWUtils.createAdvertising, don't actually tell the underlying JWPlayer library to do so => JWPlayer will not display ads.
|
||||
jwUtilCreateAdvertisementFingerprint.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
new-instance v0, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;
|
||||
invoke-direct { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;-><init>()V
|
||||
invoke-virtual { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;->build()Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig;
|
||||
move-result-object v0
|
||||
return-object v0
|
||||
""",
|
||||
)
|
||||
// Whenever the app tries to define the advertising config for JWPlayer, don't set the advertising config and directly return.
|
||||
val iputInstructionIndex = jwPlayerConfigFingerprint.method.indexOfFirstInstructionOrThrow(Opcode.IPUT_OBJECT)
|
||||
jwPlayerConfigFingerprint.method.removeInstructions(iputInstructionIndex, 1)
|
||||
|
||||
// Filter injected content from API calls out of lists.
|
||||
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package app.revanced.patches.pandora.ads
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.pandora.shared.constructUserDataFingerprint
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val disableAudioAdsPatch = bytecodePatch(
|
||||
name = "Disable audio ads",
|
||||
) {
|
||||
compatibleWith("com.pandora.android")
|
||||
|
||||
execute {
|
||||
constructUserDataFingerprint.method.apply {
|
||||
// First match is "hasAudioAds".
|
||||
val hasAudioAdsStringIndex = constructUserDataFingerprint.stringMatches!!.first().index
|
||||
val moveResultIndex = indexOfFirstInstructionOrThrow(hasAudioAdsStringIndex, Opcode.MOVE_RESULT)
|
||||
val hasAudioAdsRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
moveResultIndex + 1,
|
||||
"const/4 v$hasAudioAdsRegister, 0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package app.revanced.patches.pandora.misc
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.pandora.shared.constructUserDataFingerprint
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val enableUnlimitedSkipsPatch = bytecodePatch(
|
||||
name = "Enable unlimited skips",
|
||||
) {
|
||||
compatibleWith("com.pandora.android")
|
||||
|
||||
execute {
|
||||
constructUserDataFingerprint.method.apply {
|
||||
// Last match is "skipLimitBehavior".
|
||||
val skipLimitBehaviorStringIndex = constructUserDataFingerprint.stringMatches!!.last().index
|
||||
val moveResultObjectIndex =
|
||||
indexOfFirstInstructionOrThrow(skipLimitBehaviorStringIndex, Opcode.MOVE_RESULT_OBJECT)
|
||||
val skipLimitBehaviorRegister = getInstruction<OneRegisterInstruction>(moveResultObjectIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
moveResultObjectIndex + 1,
|
||||
"const-string v$skipLimitBehaviorRegister, \"unlimited\""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package app.revanced.patches.pandora.shared
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val constructUserDataFingerprint = fingerprint {
|
||||
strings("hasAudioAds", "skipLimitBehavior")
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package app.revanced.patches.primevideo.ads
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val enterServerInsertedAdBreakStateFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
parameters("Lcom/amazon/avod/fsm/Trigger;")
|
||||
returns("V")
|
||||
opcodes(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4
|
||||
)
|
||||
custom { method, classDef ->
|
||||
method.name == "enter" && classDef.type == "Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;"
|
||||
}
|
||||
}
|
||||
|
||||
internal val doTriggerFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PROTECTED)
|
||||
returns("V")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
custom { method, classDef ->
|
||||
method.name == "doTrigger" && classDef.type == "Lcom/amazon/avod/fsm/StateBase;"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package app.revanced.patches.primevideo.ads
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.primevideo.misc.extension.sharedExtensionPatch
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val skipAdsPatch = bytecodePatch(
|
||||
name = "Skip ads",
|
||||
description = "Automatically skips video stream ads.",
|
||||
) {
|
||||
compatibleWith("com.amazon.avod.thirdpartyclient"("3.0.403.257"))
|
||||
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
// Skip all the logic in ServerInsertedAdBreakState.enter(), which plays all the ad clips in this
|
||||
// ad break. Instead, force the video player to seek over the entire break and reset the state machine.
|
||||
execute {
|
||||
// Force doTrigger() access to public so we can call it from our extension.
|
||||
doTriggerFingerprint.method.accessFlags = AccessFlags.PUBLIC.value;
|
||||
|
||||
val getPlayerIndex = enterServerInsertedAdBreakStateFingerprint.patternMatch!!.startIndex
|
||||
enterServerInsertedAdBreakStateFingerprint.method.apply {
|
||||
// Get register that stores VideoPlayer:
|
||||
// invoke-virtual ->getPrimaryPlayer()
|
||||
// move-result-object { playerRegister }
|
||||
val playerRegister = getInstruction<OneRegisterInstruction>(getPlayerIndex + 1).registerA
|
||||
|
||||
// Reuse the params from the original method:
|
||||
// p0 = ServerInsertedAdBreakState
|
||||
// p1 = AdBreakTrigger
|
||||
addInstructions(
|
||||
getPlayerIndex + 2,
|
||||
"""
|
||||
invoke-static { p0, p1, v$playerRegister }, Lapp/revanced/extension/primevideo/ads/SkipAdsPatch;->enterServerInsertedAdBreakState(Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;Lcom/amazon/avod/media/ads/internal/state/AdBreakTrigger;Lcom/amazon/avod/media/playback/VideoPlayer;)V
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package app.revanced.patches.primevideo.misc.extension
|
||||
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
|
||||
val sharedExtensionPatch = sharedExtensionPatch("primevideo", applicationInitHook)
|
||||
@@ -0,0 +1,9 @@
|
||||
package app.revanced.patches.primevideo.misc.extension
|
||||
|
||||
import app.revanced.patches.shared.misc.extension.extensionHook
|
||||
|
||||
internal val applicationInitHook = extensionHook {
|
||||
custom { method, classDef ->
|
||||
method.name == "onCreate" && classDef.endsWith("/SplashScreenActivity;")
|
||||
}
|
||||
}
|
||||
@@ -51,26 +51,6 @@ abstract class BasePreference(
|
||||
layout?.let { setAttribute("android:layout", layout) }
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = key?.hashCode() ?: 0
|
||||
result = 31 * result + titleKey.hashCode()
|
||||
result = 31 * result + tag.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BasePreference
|
||||
|
||||
if (key != other.key) return false
|
||||
if (titleKey != other.titleKey) return false
|
||||
if (tag != other.tag) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun Element.addSummary(summaryKey: String, summaryType: SummaryType = SummaryType.DEFAULT) =
|
||||
setAttribute("android:${summaryType.type}", "@string/$summaryKey")
|
||||
|
||||
@@ -2,8 +2,12 @@ package app.revanced.patches.spotify.misc
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
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.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
|
||||
internal val accountAttributeFingerprint = fingerprint {
|
||||
custom { _, classDef ->
|
||||
@@ -15,7 +19,7 @@ internal val accountAttributeFingerprint = fingerprint {
|
||||
}
|
||||
}
|
||||
|
||||
internal val productStateProtoFingerprint = fingerprint {
|
||||
internal val productStateProtoGetMapFingerprint = fingerprint {
|
||||
returns("Ljava/util/Map;")
|
||||
custom { _, classDef ->
|
||||
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
@@ -56,16 +60,40 @@ internal val readPlayerOptionOverridesFingerprint = fingerprint {
|
||||
}
|
||||
}
|
||||
|
||||
internal val homeSectionFingerprint = fingerprint {
|
||||
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
|
||||
}
|
||||
|
||||
internal val protobufListsFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
custom { method, _ -> method.name == "emptyProtobufList" }
|
||||
}
|
||||
|
||||
internal val homeStructureFingerprint = fingerprint {
|
||||
opcodes(Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT)
|
||||
custom { _, classDef -> classDef.endsWith("homeapi/proto/HomeStructure;") }
|
||||
internal val protobufListRemoveFingerprint = fingerprint {
|
||||
custom { method, _ -> method.name == "remove" }
|
||||
}
|
||||
|
||||
internal val homeSectionFingerprint = fingerprint {
|
||||
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
|
||||
}
|
||||
|
||||
internal val homeStructureGetSectionsFingerprint = fingerprint {
|
||||
custom { method, classDef ->
|
||||
classDef.endsWith("homeapi/proto/HomeStructure;") && method.indexOfFirstInstruction {
|
||||
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "sections_"
|
||||
} >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reactivexFunctionApplyWithClassInitFingerprint(className: String) = fingerprint {
|
||||
returns("Ljava/lang/Object;")
|
||||
parameters("Ljava/lang/Object;")
|
||||
custom { method, _ -> method.name == "apply" && method.indexOfFirstInstruction {
|
||||
opcode == Opcode.NEW_INSTANCE && getReference<TypeReference>()?.type?.endsWith(className) == true
|
||||
} >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal const val PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME = "FetchMessageRequest;"
|
||||
internal val pendragonJsonFetchMessageRequestFingerprint =
|
||||
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME)
|
||||
|
||||
internal const val PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME = "FetchMessageListRequest;"
|
||||
internal val pendragonProtoFetchMessageListRequestFingerprint =
|
||||
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME)
|
||||
|
||||
@@ -60,4 +60,4 @@ val spoofPackageInfoPatch = bytecodePatch(
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package app.revanced.patches.spotify.misc.privacy
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val shareCopyUrlFingerprint = fingerprint {
|
||||
returns("Ljava/lang/Object;")
|
||||
parameters("Ljava/lang/Object;")
|
||||
strings("clipboard", "Spotify Link")
|
||||
custom { method, _ ->
|
||||
method.name == "invokeSuspend"
|
||||
}
|
||||
}
|
||||
|
||||
internal val shareCopyUrlLegacyFingerprint = fingerprint {
|
||||
returns("Ljava/lang/Object;")
|
||||
parameters("Ljava/lang/Object;")
|
||||
strings("clipboard", "createNewSession failed")
|
||||
custom { method, _ ->
|
||||
method.name == "apply"
|
||||
}
|
||||
}
|
||||
|
||||
internal val formatAndroidShareSheetUrlFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters("L", "Ljava/lang/String;")
|
||||
literal {
|
||||
'\n'.code.toLong()
|
||||
}
|
||||
}
|
||||
|
||||
internal val formatAndroidShareSheetUrlLegacyFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;")
|
||||
literal {
|
||||
'\n'.code.toLong()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package app.revanced.patches.spotify.misc.privacy
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val sanitizeSharingLinksPatch = bytecodePatch(
|
||||
name = "Sanitize sharing links",
|
||||
description = "Removes the tracking query parameters from links before they are shared.",
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
execute {
|
||||
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"sanitizeUrl(Ljava/lang/String;)Ljava/lang/String;"
|
||||
|
||||
val copyFingerprint = if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
shareCopyUrlLegacyFingerprint
|
||||
} else {
|
||||
shareCopyUrlFingerprint
|
||||
}
|
||||
|
||||
copyFingerprint.method.apply {
|
||||
val newPlainTextInvokeIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.name == "newPlainText"
|
||||
}
|
||||
val register = getInstruction<FiveRegisterInstruction>(newPlainTextInvokeIndex).registerD
|
||||
|
||||
addInstructions(
|
||||
newPlainTextInvokeIndex,
|
||||
"""
|
||||
invoke-static { v$register }, $extensionMethodDescriptor
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// Android native share sheet is used for all other quick share types (X, WhatsApp, etc).
|
||||
val shareUrlParameter : String
|
||||
val shareSheetFingerprint : Fingerprint
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
shareSheetFingerprint = formatAndroidShareSheetUrlLegacyFingerprint
|
||||
shareUrlParameter = "p2"
|
||||
} else {
|
||||
shareSheetFingerprint = formatAndroidShareSheetUrlFingerprint
|
||||
shareUrlParameter = "p1"
|
||||
}
|
||||
|
||||
shareSheetFingerprint.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { $shareUrlParameter }, $extensionMethodDescriptor
|
||||
move-result-object $shareUrlParameter
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.patches.spotify.misc.widgets
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val canBindAppWidgetPermissionFingerprint = fingerprint {
|
||||
strings("android.permission.BIND_APPWIDGET")
|
||||
opcodes(Opcode.AND_INT_LIT8)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.spotify.misc.widgets
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val fixThirdPartyLaunchersWidgets = bytecodePatch(
|
||||
name = "Fix third party launchers widgets",
|
||||
description = "Fixes Spotify widgets not working in third party launchers, like Nova Launcher.",
|
||||
) {
|
||||
execute {
|
||||
// Only system app launchers are granted the BIND_APPWIDGET permission.
|
||||
// Override the method that checks for it to always return true, as this permission is not actually required
|
||||
// for the widgets to work.
|
||||
canBindAppWidgetPermissionFingerprint.method.returnEarly(true)
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,4 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
|
||||
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|
||||
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/feedfilter/FeedItemsFilter;"
|
||||
|
||||
@Suppress("unused")
|
||||
val feedFilterPatch = bytecodePatch(
|
||||
name = "Feed filter",
|
||||
@@ -26,14 +28,15 @@ val feedFilterPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
execute {
|
||||
feedApiServiceLIZFingerprint.method.apply {
|
||||
val returnFeedItemInstruction = instructions.first { it.opcode == Opcode.RETURN_OBJECT }
|
||||
val feedItemsRegister = (returnFeedItemInstruction as OneRegisterInstruction).registerA
|
||||
|
||||
addInstruction(
|
||||
returnFeedItemInstruction.location.index,
|
||||
"invoke-static { v$feedItemsRegister }, " +
|
||||
"Lapp/revanced/extension/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
|
||||
arrayOf(
|
||||
feedApiServiceLIZFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
|
||||
followFeedFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V"
|
||||
).forEach { (method, filterSignature) ->
|
||||
val returnInstruction = method.instructions.first { it.opcode == Opcode.RETURN_OBJECT }
|
||||
val register = (returnInstruction as OneRegisterInstruction).registerA
|
||||
method.addInstruction(
|
||||
returnInstruction.location.index,
|
||||
"invoke-static { v$register }, $filterSignature"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,4 +45,5 @@ val feedFilterPatch = bytecodePatch(
|
||||
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V",
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
package app.revanced.patches.tiktok.feedfilter
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val feedApiServiceLIZFingerprint = fingerprint {
|
||||
custom { method, classDef ->
|
||||
classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList"
|
||||
}
|
||||
}
|
||||
|
||||
internal val followFeedFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;")
|
||||
strings("getFollowFeedList")
|
||||
opcodes(
|
||||
Opcode.INVOKE_INTERFACE_RANGE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE
|
||||
)
|
||||
}
|
||||
@@ -46,10 +46,11 @@ val hideButtonsPatch = resourcePatch(
|
||||
SwitchPreference("revanced_hide_remix_button"),
|
||||
SwitchPreference("revanced_hide_download_button"),
|
||||
SwitchPreference("revanced_hide_thanks_button"),
|
||||
SwitchPreference("revanced_hide_ask_button"),
|
||||
SwitchPreference("revanced_hide_clip_button"),
|
||||
SwitchPreference("revanced_hide_playlist_button"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")
|
||||
|
||||
@@ -222,7 +222,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
SwitchPreference("revanced_hide_movies_section"),
|
||||
SwitchPreference("revanced_hide_notify_me_button"),
|
||||
SwitchPreference("revanced_hide_playables"),
|
||||
SwitchPreference("revanced_hide_search_result_recommendations"),
|
||||
SwitchPreference("revanced_hide_search_result_recommendation_labels"),
|
||||
SwitchPreference("revanced_hide_show_more_button"),
|
||||
SwitchPreference("revanced_hide_doodles"),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package app.revanced.patches.youtube.layout.hide.relatedvideooverlay
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
|
||||
internal val relatedEndScreenResultsParentFingerprint = fingerprint {
|
||||
returns("V")
|
||||
literal{ appRelatedEndScreenResults }
|
||||
}
|
||||
|
||||
internal val relatedEndScreenResultsFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters(
|
||||
"I",
|
||||
"Z",
|
||||
"I",
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package app.revanced.patches.youtube.layout.hide.relatedvideooverlay
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
|
||||
internal var appRelatedEndScreenResults = -1L
|
||||
private set
|
||||
|
||||
private val hideRelatedVideoOverlayResourcePatch = resourcePatch {
|
||||
dependsOn(
|
||||
resourceMappingPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
appRelatedEndScreenResults = resourceMappings[
|
||||
"layout",
|
||||
"app_related_endscreen_results",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/HideRelatedVideoOverlayPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideRelatedVideoOverlayPatch = bytecodePatch(
|
||||
name = "Hide related video overlay",
|
||||
description = "Adds an option to hide the related video overlay shown when swiping up in fullscreen.",
|
||||
) {
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
sharedExtensionPatch,
|
||||
addResourcesPatch,
|
||||
hideRelatedVideoOverlayResourcePatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_hide_related_video_overlay")
|
||||
)
|
||||
|
||||
relatedEndScreenResultsFingerprint.match(
|
||||
relatedEndScreenResultsParentFingerprint.originalClassDef
|
||||
).method.apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideRelatedVideoOverlay()Z
|
||||
move-result v0
|
||||
if-eqz v0, :show
|
||||
return-void
|
||||
""",
|
||||
ExternalLabel("show", getInstruction(0))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,6 @@ internal val shortsBottomBarContainerFingerprint = fingerprint {
|
||||
literal { bottomBarContainer }
|
||||
}
|
||||
|
||||
internal val createShortsButtonsFingerprint = fingerprint {
|
||||
returns("V")
|
||||
literal { reelPlayerRightCellButtonHeight }
|
||||
}
|
||||
|
||||
internal val renderBottomNavigationBarFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters("Ljava/lang/String;")
|
||||
|
||||
@@ -6,7 +6,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.booleanOption
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
@@ -22,14 +21,14 @@ import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.forEachLiteralValueInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal var reelPlayerRightCellButtonHeight = -1L
|
||||
private set
|
||||
internal var bottomBarContainer = -1L
|
||||
private set
|
||||
internal var reelPlayerRightPivotV2Size = -1L
|
||||
@@ -137,11 +136,6 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
|
||||
}
|
||||
}
|
||||
|
||||
reelPlayerRightCellButtonHeight = resourceMappings[
|
||||
"dimen",
|
||||
"reel_player_right_cell_button_height",
|
||||
]
|
||||
|
||||
bottomBarContainer = resourceMappings[
|
||||
"id",
|
||||
"bottom_bar_container",
|
||||
@@ -186,15 +180,6 @@ val hideShortsComponentsPatch = bytecodePatch(
|
||||
hideShortsWidgetOption()
|
||||
|
||||
execute {
|
||||
// region Hide the Shorts buttons in older versions of YouTube.
|
||||
|
||||
// Some Shorts buttons are views, hide them by setting their visibility to GONE.
|
||||
ShortsButtons.entries.forEach { button -> button.injectHideCall(createShortsButtonsFingerprint.method) }
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide the Shorts buttons in newer versions of YouTube.
|
||||
|
||||
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
|
||||
|
||||
forEachLiteralValueInstruction(
|
||||
@@ -211,7 +196,7 @@ val hideShortsComponentsPatch = bytecodePatch(
|
||||
"""
|
||||
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
|
||||
move-result v$sizeRegister
|
||||
""",
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -261,31 +246,10 @@ val hideShortsComponentsPatch = bytecodePatch(
|
||||
"""
|
||||
invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I
|
||||
move-result v$heightRegister
|
||||
""",
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
||||
private enum class ShortsButtons(private val resourceName: String, private val methodName: String) {
|
||||
LIKE("reel_dyn_like", "hideLikeButton"),
|
||||
DISLIKE("reel_dyn_dislike", "hideDislikeButton"),
|
||||
COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"),
|
||||
REMIX("reel_dyn_remix", "hideShortsRemixButton"),
|
||||
SHARE("reel_dyn_share", "hideShortsShareButton"),
|
||||
;
|
||||
|
||||
fun injectHideCall(method: MutableMethod) {
|
||||
val referencedIndex = method.indexOfFirstResourceIdOrThrow(resourceName)
|
||||
|
||||
val setIdIndex = method.indexOfFirstInstructionOrThrow(referencedIndex) {
|
||||
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setId"
|
||||
}
|
||||
|
||||
val viewRegister = method.getInstruction<FiveRegisterInstruction>(setIdIndex).registerC
|
||||
|
||||
method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,6 @@ import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val conversionContextFingerprint = fingerprint {
|
||||
returns("Ljava/lang/String;")
|
||||
parameters()
|
||||
strings(
|
||||
", widthConstraint=",
|
||||
", heightConstraint=",
|
||||
", templateLoggerFactory=",
|
||||
", rootDisposableContainer=",
|
||||
"ConversionContext{containerInternal=",
|
||||
)
|
||||
}
|
||||
|
||||
internal val dislikeFingerprint = fingerprint {
|
||||
returns("V")
|
||||
strings("like/dislike")
|
||||
|
||||
@@ -18,6 +18,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.addSettingPreference
|
||||
import app.revanced.patches.youtube.misc.settings.newIntent
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
|
||||
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint
|
||||
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
|
||||
import app.revanced.patches.youtube.video.videoid.hookVideoId
|
||||
@@ -113,11 +114,11 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
// This hook handles all situations, as it's where the created Spans are stored and later reused.
|
||||
// Find the field name of the conversion context.
|
||||
val conversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find {
|
||||
it.type == conversionContextFingerprint.originalClassDef.type
|
||||
it.type == conversionContextFingerprintToString.originalClassDef.type
|
||||
} ?: throw PatchException("Could not find conversion context field")
|
||||
|
||||
textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef)
|
||||
textComponentLookupFingerprint.method.apply {
|
||||
.method.apply {
|
||||
// Find the instruction for creating the text data object.
|
||||
val textDataClassType = textComponentDataFingerprint.originalClassDef.type
|
||||
|
||||
@@ -160,12 +161,12 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
addInstructionsAtControlFlowLabel(
|
||||
insertIndex,
|
||||
"""
|
||||
# Copy conversion context
|
||||
move-object/from16 v$tempRegister, p0
|
||||
iget-object v$tempRegister, v$tempRegister, $conversionContextField
|
||||
invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$charSequenceRegister
|
||||
""",
|
||||
# Copy conversion context
|
||||
move-object/from16 v$tempRegister, p0
|
||||
iget-object v$tempRegister, v$tempRegister, $conversionContextField
|
||||
invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$charSequenceRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -201,11 +202,9 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
val charSequenceFieldReference =
|
||||
getInstruction<ReferenceInstruction>(dislikesIndex).reference
|
||||
|
||||
val registerCount = implementation!!.registerCount
|
||||
val conversionContextRegister = implementation!!.registerCount - parameters.size + 1
|
||||
|
||||
// This register is being overwritten, so it is free to use.
|
||||
val freeRegister = registerCount - 1
|
||||
val conversionContextRegister = registerCount - parameters.size + 1
|
||||
val freeRegister = findFreeRegister(insertIndex, charSequenceInstanceRegister, conversionContextRegister)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package app.revanced.patches.youtube.layout.shortsautoplay
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
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.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val reelEnumConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
@@ -20,3 +25,27 @@ internal val reelPlaybackRepeatFingerprint = fingerprint {
|
||||
parameters("L")
|
||||
strings("YoutubePlayerState is in throwing an Error.")
|
||||
}
|
||||
|
||||
internal val reelPlaybackFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters("J")
|
||||
custom { method, _ ->
|
||||
indexOfMilliSecondsInstruction(method) >= 0 &&
|
||||
indexOfInitializationInstruction(method) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun indexOfMilliSecondsInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<FieldReference>()?.name == "MILLISECONDS"
|
||||
}
|
||||
|
||||
internal fun indexOfInitializationInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
val reference = getReference<MethodReference>()
|
||||
opcode == Opcode.INVOKE_DIRECT &&
|
||||
reference?.name == "<init>" &&
|
||||
reference.parameterTypes.size == 3 &&
|
||||
reference.parameterTypes.firstOrNull() == "I"
|
||||
}
|
||||
|
||||
@@ -2,21 +2,32 @@ package app.revanced.patches.youtube.layout.shortsautoplay
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;"
|
||||
|
||||
@@ -98,5 +109,84 @@ val shortsAutoplayPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// As of YouTube 20.09, Google has removed the code for 'Autoplay' and 'Pause' from this method.
|
||||
// Manually restore the removed 'Autoplay' code.
|
||||
if (is_20_09_or_greater) {
|
||||
// Variable names are only a rough guess of what these methods do.
|
||||
val userActionMethodIndex = indexOfInitializationInstruction(reelPlaybackFingerprint.method)
|
||||
val userActionMethodReference = reelPlaybackFingerprint.method
|
||||
.getInstruction<ReferenceInstruction>(userActionMethodIndex).reference as MethodReference
|
||||
val reelSequenceControllerMethodIndex = reelPlaybackFingerprint.method
|
||||
.indexOfFirstInstructionOrThrow(userActionMethodIndex, Opcode.INVOKE_VIRTUAL)
|
||||
val reelSequenceControllerMethodReference = reelPlaybackFingerprint.method
|
||||
.getInstruction<ReferenceInstruction>(reelSequenceControllerMethodIndex).reference as MethodReference
|
||||
|
||||
reelPlaybackRepeatFingerprint.method.apply {
|
||||
// Find the first call modified by extension code above.
|
||||
val extensionReturnResultIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
|
||||
} + 1
|
||||
val enumRegister = getInstruction<OneRegisterInstruction>(extensionReturnResultIndex).registerA
|
||||
val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow(extensionReturnResultIndex) {
|
||||
val reference = getReference<FieldReference>()
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
reference?.definingClass == definingClass &&
|
||||
reference.type == reelSequenceControllerMethodReference.definingClass
|
||||
}
|
||||
val getReelSequenceControllerReference =
|
||||
getInstruction<ReferenceInstruction>(getReelSequenceControllerIndex).reference
|
||||
|
||||
// Add a helper method to avoid finding multiple free registers.
|
||||
// If enum is autoplay then method performs autoplay and returns null,
|
||||
// otherwise returns the same enum.
|
||||
val helperClass = definingClass
|
||||
val helperName = "patch_handleAutoPlay"
|
||||
val helperReturnType = "Ljava/lang/Enum;"
|
||||
val helperMethod = ImmutableMethod(
|
||||
helperClass,
|
||||
helperName,
|
||||
listOf(ImmutableMethodParameter("Ljava/lang/Enum;", null, null)),
|
||||
helperReturnType,
|
||||
AccessFlags.PRIVATE.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(7),
|
||||
).toMutable().apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->isAutoPlay(Ljava/lang/Enum;)Z
|
||||
move-result v0
|
||||
if-eqz v0, :ignore
|
||||
new-instance v0, ${userActionMethodReference.definingClass}
|
||||
const/4 v1, 0x3
|
||||
const/4 v2, 0x0
|
||||
invoke-direct { v0, v1, v2, v2 }, $userActionMethodReference
|
||||
iget-object v3, p0, $getReelSequenceControllerReference
|
||||
invoke-virtual { v3, v0 }, $reelSequenceControllerMethodReference
|
||||
const/4 v4, 0x0
|
||||
return-object v4
|
||||
:ignore
|
||||
return-object p1
|
||||
"""
|
||||
)
|
||||
}
|
||||
reelPlaybackRepeatFingerprint.classDef.methods.add(helperMethod)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
extensionReturnResultIndex + 1,
|
||||
"""
|
||||
invoke-direct { p0, v$enumRegister }, $helperClass->$helperName(Ljava/lang/Enum;)$helperReturnType
|
||||
move-result-object v$enumRegister
|
||||
if-nez v$enumRegister, :ignore
|
||||
return-void # Autoplay was performed.
|
||||
:ignore
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ val spoofAppVersionPatch = bytecodePatch(
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
// Group the switch and list preference together, since General menu is sorted by name
|
||||
// and the preferences can be scattered apart with non English langauges.
|
||||
// and the preferences can be scattered apart with non English languages.
|
||||
PreferenceCategory(
|
||||
titleKey = null,
|
||||
sorting = Sorting.UNSORTED,
|
||||
@@ -122,16 +122,17 @@ val spoofAppVersionPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
|
||||
val insertIndex = spoofAppVersionFingerprint.patternMatch!!.startIndex + 1
|
||||
val buildOverrideNameRegister =
|
||||
spoofAppVersionFingerprint.method.getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
spoofAppVersionFingerprint.apply {
|
||||
val startIndex = patternMatch!!.startIndex
|
||||
val buildOverrideNameRegister = method.getInstruction<OneRegisterInstruction>(startIndex).registerA
|
||||
|
||||
spoofAppVersionFingerprint.method.addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$buildOverrideNameRegister
|
||||
"""
|
||||
)
|
||||
method.addInstructions(
|
||||
startIndex + 1,
|
||||
"""
|
||||
invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$buildOverrideNameRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package app.revanced.patches.youtube.misc.gms
|
||||
|
||||
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.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/AccountCredentialsInvalidTextPatch;"
|
||||
|
||||
internal var ic_offline_no_content_upside_down = -1L
|
||||
private set
|
||||
internal var offline_no_content_body_text_not_offline_eligible = -1L
|
||||
private set
|
||||
|
||||
private val accountCredentialsInvalidTextResourcePatch = resourcePatch {
|
||||
execute {
|
||||
ic_offline_no_content_upside_down = resourceMappings[
|
||||
"drawable",
|
||||
"ic_offline_no_content_upside_down"
|
||||
]
|
||||
|
||||
offline_no_content_body_text_not_offline_eligible = resourceMappings[
|
||||
"string",
|
||||
"offline_no_content_body_text_not_offline_eligible"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
internal val accountCredentialsInvalidTextPatch = bytecodePatch {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
accountCredentialsInvalidTextResourcePatch,
|
||||
addResourcesPatch
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "misc.gms.accountCredentialsInvalidTextPatch")
|
||||
|
||||
// If the user recently changed their account password,
|
||||
// the app can show "You're offline. Check your internet connection."
|
||||
// even when the internet is available. For this situation
|
||||
// YouTube + MicroG shows an offline error message.
|
||||
//
|
||||
// Change the error text to inform the user to uninstall and reinstall MicroG.
|
||||
// The user can also fix this by deleting the MicroG account but
|
||||
// MicroG accounts look almost identical to Google device accounts
|
||||
// and it's more foolproof to instead uninstall/reinstall.
|
||||
arrayOf(
|
||||
specificNetworkErrorViewControllerFingerprint,
|
||||
loadingFrameLayoutControllerFingerprint
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.method.apply {
|
||||
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(
|
||||
offline_no_content_body_text_not_offline_eligible
|
||||
)
|
||||
val getStringIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.name == "getString"
|
||||
}
|
||||
val register = getInstruction<OneRegisterInstruction>(getStringIndex + 1).registerA
|
||||
|
||||
addInstructions(
|
||||
getStringIndex + 2,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getOfflineNetworkErrorString(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package app.revanced.patches.youtube.misc.gms
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val specificNetworkErrorViewControllerFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters()
|
||||
custom { method, _ ->
|
||||
method.containsLiteralInstruction(ic_offline_no_content_upside_down)
|
||||
&& method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible)
|
||||
}
|
||||
}
|
||||
|
||||
// It's not clear if this second class is ever used and it may be dead code,
|
||||
// but it the layout image/text is identical to the network error fingerprint above.
|
||||
internal val loadingFrameLayoutControllerFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters("L")
|
||||
custom { method, _ ->
|
||||
method.containsLiteralInstruction(ic_offline_no_content_upside_down)
|
||||
&& method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible)
|
||||
}
|
||||
}
|
||||
@@ -68,5 +68,5 @@ private fun gmsCoreSupportResourcePatch(
|
||||
)
|
||||
},
|
||||
) {
|
||||
dependsOn(settingsPatch, addResourcesPatch)
|
||||
dependsOn(settingsPatch, addResourcesPatch, accountCredentialsInvalidTextPatch)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@ import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* In 19.17 and earlier, this resolves to the same method as [readComponentIdentifierFingerprint].
|
||||
* In 19.18+ this resolves to a different method.
|
||||
*/
|
||||
internal val componentContextParserFingerprint = fingerprint {
|
||||
strings(
|
||||
"TreeNode result must be set.",
|
||||
@@ -17,11 +13,21 @@ internal val componentContextParserFingerprint = fingerprint {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the class found in [componentContextParserFingerprint].
|
||||
* When patching 19.16 this fingerprint matches the same method as [componentContextParserFingerprint].
|
||||
*/
|
||||
internal val componentContextSubParserFingerprint = fingerprint {
|
||||
strings(
|
||||
"Number of bits must be positive"
|
||||
)
|
||||
}
|
||||
|
||||
internal val lithoFilterFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
returns("V")
|
||||
custom { _, classDef ->
|
||||
classDef.endsWith("LithoFilterPatch;")
|
||||
classDef.endsWith("/LithoFilterPatch;")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,14 +43,6 @@ internal val protobufBufferReferenceFingerprint = fingerprint {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* In 19.17 and earlier, this resolves to the same method as [componentContextParserFingerprint].
|
||||
* In 19.18+ this resolves to a different method.
|
||||
*/
|
||||
internal val readComponentIdentifierFingerprint = fingerprint {
|
||||
strings("Number of bits must be positive")
|
||||
}
|
||||
|
||||
internal val emptyComponentFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
|
||||
parameters()
|
||||
|
||||
@@ -4,25 +4,25 @@ package app.revanced.patches.youtube.misc.litho.filter
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_18_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_05_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@@ -53,42 +53,33 @@ val lithoFilterPatch = bytecodePatch(
|
||||
* The buffer is a large byte array that represents the component tree.
|
||||
* This byte array is searched for strings that indicate the current component.
|
||||
*
|
||||
* The following pseudocode shows how the patch works:
|
||||
* All modifications done here must allow all the original code to still execute
|
||||
* even when filtering, otherwise memory leaks or poor app performance may occur.
|
||||
*
|
||||
* The following pseudocode shows how this patch works:
|
||||
*
|
||||
* class SomeOtherClass {
|
||||
* // Called before ComponentContextParser.parseBytesToComponentContext method.
|
||||
* // Called before ComponentContextParser.parseComponent() method.
|
||||
* public void someOtherMethod(ByteBuffer byteBuffer) {
|
||||
* ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* When patching 19.17 and earlier:
|
||||
*
|
||||
* class ComponentContextParser {
|
||||
* public ComponentContext ReadComponentIdentifierFingerprint(...) {
|
||||
* public Component parseComponent() {
|
||||
* ...
|
||||
* if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch.
|
||||
*
|
||||
* // Checks if the component should be filtered.
|
||||
* // Sets a thread local with the filtering result.
|
||||
* extensionClass.filter(identifier, pathBuilder); // Inserted by this patch.
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* if (extensionClass.shouldFilter()) { // Inserted by this patch.
|
||||
* return emptyComponent;
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* When patching 19.18 and later:
|
||||
*
|
||||
* class ComponentContextParser {
|
||||
* public ComponentContext parseBytesToComponentContext(...) {
|
||||
* ...
|
||||
* if (ReadComponentIdentifierFingerprint() == null); // Inserted by this patch.
|
||||
* return emptyComponent;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* public ComponentIdentifierObj readComponentIdentifier(...) {
|
||||
* ...
|
||||
* if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch.
|
||||
* return null;
|
||||
* ...
|
||||
* }
|
||||
* return originalUnpatchedComponent; // Original code.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@@ -103,7 +94,7 @@ val lithoFilterPatch = bytecodePatch(
|
||||
2,
|
||||
"""
|
||||
new-instance v1, $classDescriptor
|
||||
invoke-direct {v1}, $classDescriptor-><init>()V
|
||||
invoke-direct { v1 }, $classDescriptor-><init>()V
|
||||
const/16 v2, ${filterCount++}
|
||||
aput-object v1, v0, v2
|
||||
""",
|
||||
@@ -115,110 +106,105 @@ val lithoFilterPatch = bytecodePatch(
|
||||
|
||||
protobufBufferReferenceFingerprint.method.addInstruction(
|
||||
0,
|
||||
" invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
|
||||
"invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hook the method that parses bytes into a ComponentContext.
|
||||
|
||||
val readComponentMethod = readComponentIdentifierFingerprint.originalMethod
|
||||
// Get the only static method in the class.
|
||||
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.first { method ->
|
||||
AccessFlags.STATIC.isSet(method.accessFlags)
|
||||
}
|
||||
// Only one field.
|
||||
val emptyComponentField = classBy { classDef ->
|
||||
builderMethodDescriptor.returnType == classDef.type
|
||||
}!!.immutableClass.fields.single()
|
||||
|
||||
// Returns an empty component instead of the original component.
|
||||
fun createReturnEmptyComponentInstructions(register: Int): String =
|
||||
"""
|
||||
move-object/from16 v$register, p1
|
||||
invoke-static { v$register }, $builderMethodDescriptor
|
||||
move-result-object v$register
|
||||
iget-object v$register, v$register, $emptyComponentField
|
||||
return-object v$register
|
||||
"""
|
||||
|
||||
// Allow the method to run to completion, and override the
|
||||
// return value with an empty component if it should be filtered.
|
||||
// It is important to allow the original code to always run to completion,
|
||||
// otherwise memory leaks and poor app performance can occur.
|
||||
//
|
||||
// The extension filtering result needs to be saved off somewhere, but cannot
|
||||
// save to a class field since the target class is called by multiple threads.
|
||||
// It would be great if there was a way to change the register count of the
|
||||
// method implementation and save the result to a high register to later use
|
||||
// in the method, but there is no simple way to do that.
|
||||
// Instead save the extension filter result to a thread local and check the
|
||||
// filtering result at each method return index.
|
||||
// String field for the litho identifier.
|
||||
componentContextParserFingerprint.method.apply {
|
||||
// 19.18 and later require patching 2 methods instead of one.
|
||||
// Otherwise the modifications done here are the same for all targets.
|
||||
if (is_19_18_or_greater) {
|
||||
// Get the method name of the ReadComponentIdentifierFingerprint call.
|
||||
val readComponentMethodCallIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == readComponentMethod.definingClass &&
|
||||
reference.name == readComponentMethod.name
|
||||
val conversionContextClass = conversionContextFingerprintToString.originalClassDef
|
||||
|
||||
val conversionContextIdentifierField = componentContextSubParserFingerprint.match(
|
||||
componentContextParserFingerprint.originalClassDef
|
||||
).let {
|
||||
// Identifier field is loaded just before the string declaration.
|
||||
val index = it.method.indexOfFirstInstructionReversedOrThrow(
|
||||
it.stringMatches!!.first().index
|
||||
) {
|
||||
val reference = getReference<FieldReference>()
|
||||
reference?.definingClass == conversionContextClass.type
|
||||
&& reference.type == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
// Result of read component, and also a free register.
|
||||
val register = getInstruction<OneRegisterInstruction>(readComponentMethodCallIndex + 1).registerA
|
||||
|
||||
// Insert after 'move-result-object'
|
||||
val insertHookIndex = readComponentMethodCallIndex + 2
|
||||
|
||||
// Return an EmptyComponent instead of the original component if the filterState method returns true.
|
||||
addInstructionsWithLabels(
|
||||
insertHookIndex,
|
||||
"""
|
||||
if-nez v$register, :unfiltered
|
||||
|
||||
# Component was filtered in ReadComponentIdentifierFingerprint hook
|
||||
${createReturnEmptyComponentInstructions(register)}
|
||||
""",
|
||||
ExternalLabel("unfiltered", getInstruction(insertHookIndex)),
|
||||
)
|
||||
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
// StringBuilder field for the litho path.
|
||||
val conversionContextPathBuilderField = conversionContextClass.fields
|
||||
.single { field -> field.type == "Ljava/lang/StringBuilder;" }
|
||||
|
||||
// region Read component then store the result.
|
||||
val conversionContextResultIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.returnType == conversionContextClass.type
|
||||
} + 1
|
||||
|
||||
readComponentIdentifierFingerprint.method.apply {
|
||||
val insertHookIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.IPUT_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/StringBuilder;"
|
||||
}
|
||||
val stringBuilderRegister = getInstruction<TwoRegisterInstruction>(insertHookIndex).registerA
|
||||
|
||||
// Identifier is saved to a field just before the string builder.
|
||||
val identifierRegister = getInstruction<TwoRegisterInstruction>(
|
||||
indexOfFirstInstructionReversedOrThrow(insertHookIndex) {
|
||||
opcode == Opcode.IPUT_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/String;"
|
||||
},
|
||||
val conversionContextResultRegister = getInstruction<OneRegisterInstruction>(
|
||||
conversionContextResultIndex
|
||||
).registerA
|
||||
|
||||
val freeRegister = findFreeRegister(insertHookIndex, identifierRegister, stringBuilderRegister)
|
||||
val invokeFilterInstructions = """
|
||||
invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :unfiltered
|
||||
"""
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertHookIndex,
|
||||
if (is_19_18_or_greater) {
|
||||
"""
|
||||
$invokeFilterInstructions
|
||||
|
||||
# Return null, and the ComponentContextParserFingerprint hook
|
||||
# handles returning an empty component.
|
||||
const/4 v$freeRegister, 0x0
|
||||
return-object v$freeRegister
|
||||
"""
|
||||
} else {
|
||||
"""
|
||||
$invokeFilterInstructions
|
||||
|
||||
${createReturnEmptyComponentInstructions(freeRegister)}
|
||||
"""
|
||||
},
|
||||
ExternalLabel("unfiltered", getInstruction(insertHookIndex)),
|
||||
val identifierRegister = findFreeRegister(
|
||||
conversionContextResultIndex, conversionContextResultRegister
|
||||
)
|
||||
val stringBuilderRegister = findFreeRegister(
|
||||
conversionContextResultIndex, conversionContextResultRegister, identifierRegister
|
||||
)
|
||||
|
||||
// Check if the component should be filtered, and save the result to a thread local.
|
||||
addInstructionsAtControlFlowLabel(
|
||||
conversionContextResultIndex + 1,
|
||||
"""
|
||||
iget-object v$identifierRegister, v$conversionContextResultRegister, $conversionContextIdentifierField
|
||||
iget-object v$stringBuilderRegister, v$conversionContextResultRegister, $conversionContextPathBuilderField
|
||||
invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)V
|
||||
"""
|
||||
)
|
||||
|
||||
// Get the only static method in the class.
|
||||
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
|
||||
method -> AccessFlags.STATIC.isSet(method.accessFlags)
|
||||
}
|
||||
// Only one field.
|
||||
val emptyComponentField = classBy { classDef ->
|
||||
classDef.type == builderMethodDescriptor.returnType
|
||||
}!!.immutableClass.fields.single()
|
||||
|
||||
// Check at each return value if the component is filtered,
|
||||
// and return an empty component if filtering is needed.
|
||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { returnIndex ->
|
||||
val freeRegister = findFreeRegister(returnIndex)
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
returnIndex,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->shouldFilter()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :unfiltered
|
||||
|
||||
move-object/from16 v$freeRegister, p1
|
||||
invoke-static { v$freeRegister }, $builderMethodDescriptor
|
||||
move-result-object v$freeRegister
|
||||
iget-object v$freeRegister, v$freeRegister, $emptyComponentField
|
||||
return-object v$freeRegister
|
||||
|
||||
:unfiltered
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@@ -4,6 +4,21 @@ import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val conversionContextFingerprintToString = fingerprint {
|
||||
parameters()
|
||||
strings(
|
||||
"ConversionContext{containerInternal=",
|
||||
", widthConstraint=",
|
||||
", heightConstraint=",
|
||||
", templateLoggerFactory=",
|
||||
", rootDisposableContainer=",
|
||||
", identifierProperty="
|
||||
)
|
||||
custom { method, _ ->
|
||||
method.name == "toString"
|
||||
}
|
||||
}
|
||||
|
||||
internal val autoRepeatFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
|
||||
@@ -11,12 +11,14 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.util.InstructionUtils.Companion.branchOpcodes
|
||||
import app.revanced.util.InstructionUtils.Companion.returnOpcodes
|
||||
import app.revanced.util.InstructionUtils.Companion.writeOpcodes
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.Opcode.*
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
@@ -168,6 +170,15 @@ internal val Instruction.isBranchInstruction: Boolean
|
||||
internal val Instruction.isReturnInstruction: Boolean
|
||||
get() = this.opcode in returnOpcodes
|
||||
|
||||
/**
|
||||
* Adds public [AccessFlags] and removes private and protected flags (if present).
|
||||
*/
|
||||
internal fun Int.toPublicAccessFlags() : Int {
|
||||
return this.or(AccessFlags.PUBLIC.value)
|
||||
.and(AccessFlags.PROTECTED.value.inv())
|
||||
.and(AccessFlags.PRIVATE.value.inv())
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
|
||||
*
|
||||
@@ -207,6 +218,26 @@ fun MutableMethod.injectHideViewCall(
|
||||
"invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V",
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Inserts instructions at a given index, using the existing control flow label at that index.
|
||||
* Inserted instructions can have it's own control flow labels as well.
|
||||
*
|
||||
* Effectively this changes the code from:
|
||||
* :label
|
||||
* (original code)
|
||||
*
|
||||
* Into:
|
||||
* :label
|
||||
* (patch code)
|
||||
* (original code)
|
||||
*/
|
||||
// TODO: delete this on next major version bump.
|
||||
fun MutableMethod.addInstructionsAtControlFlowLabel(
|
||||
insertIndex: Int,
|
||||
instructions: String
|
||||
) = addInstructionsAtControlFlowLabel(insertIndex, instructions, *arrayOf<ExternalLabel>())
|
||||
|
||||
/**
|
||||
* Inserts instructions at a given index, using the existing control flow label at that index.
|
||||
* Inserted instructions can have it's own control flow labels as well.
|
||||
@@ -223,13 +254,14 @@ fun MutableMethod.injectHideViewCall(
|
||||
fun MutableMethod.addInstructionsAtControlFlowLabel(
|
||||
insertIndex: Int,
|
||||
instructions: String,
|
||||
vararg externalLabels: ExternalLabel
|
||||
) {
|
||||
// Duplicate original instruction and add to +1 index.
|
||||
addInstruction(insertIndex + 1, getInstruction(insertIndex))
|
||||
|
||||
// Add patch code at same index as duplicated instruction,
|
||||
// so it uses the original instruction control flow label.
|
||||
addInstructionsWithLabels(insertIndex + 1, instructions)
|
||||
addInstructionsWithLabels(insertIndex + 1, instructions, *externalLabels)
|
||||
|
||||
// Remove original non duplicated instruction.
|
||||
removeInstruction(insertIndex)
|
||||
@@ -472,7 +504,7 @@ fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): I
|
||||
* @see indexOfFirstInstructionOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstInstruction(startIndex: Int = 0, filter: Instruction.() -> Boolean): Int {
|
||||
var instructions = this.implementation!!.instructions
|
||||
var instructions = this.implementation?.instructions ?: return -1
|
||||
if (startIndex != 0) {
|
||||
instructions = instructions.drop(startIndex)
|
||||
}
|
||||
@@ -538,7 +570,7 @@ fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode
|
||||
* @see indexOfFirstInstructionReversedOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, filter: Instruction.() -> Boolean): Int {
|
||||
var instructions = this.implementation!!.instructions
|
||||
var instructions = this.implementation?.instructions ?: return -1
|
||||
if (startIndex != null) {
|
||||
instructions = instructions.take(startIndex + 1)
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ Second \"item\" text"</string>
|
||||
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||
</patch>
|
||||
@@ -132,6 +134,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
</patch>
|
||||
<patch id="layout.panels.popup.playerPopupPanelsPatch">
|
||||
@@ -195,6 +199,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.openLinksExternallyPatch">
|
||||
|
||||
@@ -84,6 +84,8 @@ Second \"item\" text"</string>
|
||||
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||
</patch>
|
||||
@@ -132,6 +134,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
</patch>
|
||||
<patch id="layout.panels.popup.playerPopupPanelsPatch">
|
||||
@@ -195,6 +199,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.openLinksExternallyPatch">
|
||||
|
||||
@@ -459,21 +459,30 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">تمكين إيماءة السطوع التلقائي</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">التمرير لأسفل إلى أدنى قيمة للسطوع يمكّن السطوع التلقائي</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">لا يؤدي التمرير لأسفل إلى أدنى قيمة إلى تمكين السطوع التلقائي</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">تلقائي</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">مهلة واجهة التمرير</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">مقدار الوقت الذي تظهر فيه واجهة التمرير بعد التغيير بجزء الثانية</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">تعتيم خلفية واجهة التمرير السريع</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">قيمة التعتيم بين 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">يجب أن يكون تعتيم التمرير السريع بين 0-100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">لون شريط تقدم واجهة التمرير</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">لون شريط التقدم لعناصر التحكم في مستوى الصوت والسطوع</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">لون شريط التقدم غير صالح</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">حجم نص واجهة التمرير</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">حجم النص لواجهة التمرير بين 1-30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">يجب أن يكون حجم النص بين 1-30</string>
|
||||
<string name="revanced_swipe_threshold_title">مقدار حد التمرير</string>
|
||||
<string name="revanced_swipe_threshold_summary">الحد الأدنى من التمرير قبل اكتشاف الإيماءة</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">حساسية إيماءة تمرير مستوى الصوت</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">مقدار تغير مستوى الصوت لكل تمريرة</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">عرض الواجهة الدائرية</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">يتم عرض التراكب الدائري</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">يتم عرض التراكب الأفقي</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">تمكين النمط الأدنى</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">تم تمكين النمط الواجهة الأدنى</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">تم تعطيل نمط الواجهة الأدنى</string>
|
||||
<string name="revanced_swipe_overlay_style_title">نمط واجهة التمرير</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">واجهة أفقية</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">واجهة أفقية (الأدنى - الأعلى)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">واجهة أفقية (الأدنى - المنتصف)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">واجهة دائرية</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">واجهة دائرية (الأدنى)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">واجهة عمودية</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">واجهة عمودية (الأدنى)</string>
|
||||
<string name="revanced_swipe_change_video_title">تمكين إيماءة التمرير لتغيير الفيديو</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">سيؤدي التمرير في وضع ملء الشاشة إلى التغيير للفيديو التالي/السابق</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">لن يؤدي التمرير في وضع ملء الشاشة إلى التغيير للفيديو التالي/السابق</string>
|
||||
@@ -513,6 +522,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_thanks_button_title">إخفاء شكرًا</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">تم إخفاء زر شكرًا</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">يتم عرض زر شكرًا</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">إخفاء \"Ask\"</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">تم إخفاء زر \"Ask\"</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">يتم عرض زر \"Ask\"</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">إخفاء المقطع</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">تم إخفاء زر إنشاء مقطع</string>
|
||||
@@ -780,6 +794,11 @@ Second \"item\" text"</string>
|
||||
الإعدادات ← التشغيل ← تشغيل الفيديو التالي تلقائيًا"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">يتم عرض الفيديو المقترح في شاشة النهاية</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">إخفاء واجهة الفيديوهات ذات الصلة في وضع ملء الشاشة</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">تم إخفاء واجهة الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">يتم عرض واجهة الفيديوهات ذات الصلة</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">إخفاء الطابع الزمني للفيديو</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">تم إخفاء الطابع الزمني</string>
|
||||
@@ -1297,6 +1316,9 @@ Second \"item\" text"</string>
|
||||
<string name="microg_settings_title">إعدادات GmsCore</string>
|
||||
<string name="microg_settings_summary">إعدادات لـ GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">إذا قمت مؤخرًا بتغيير تفاصيل تسجيل الدخول إلى حسابك، فأزل تثبيت MicroG ثم أعد تثبيته.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">تجاوز إعادة توجيه URL</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">تم تجاوز إعادة توجيه عنوان URL</string>
|
||||
|
||||
@@ -84,6 +84,8 @@ Second \"item\" text"</string>
|
||||
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||
</patch>
|
||||
@@ -132,6 +134,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
</patch>
|
||||
<patch id="layout.panels.popup.playerPopupPanelsPatch">
|
||||
@@ -197,6 +201,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.openLinksExternallyPatch">
|
||||
|
||||
@@ -459,21 +459,30 @@ Ekranın sağ tərəfində düzünə sürüşdürərək səs səviyyəsini tənz
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Avto-parlaqlıq jestini aktivləşdir</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Parlaqlıq ən aşağı dəyərinə sürüşdürüləndə avto-parlaqlıq aktivləşir</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Parlaqlığı ən aşağı dəyərə sürüşdürəndə avto-parlaqlıq aktivləşmir</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Avtomatik</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Sürüşdürmə örtüyü müddəti</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Örtüyün göründüyü millisaniyələrin sayı</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Sürüşdürmə cildi arxa plan qeyri-şəffaflığı</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 arası qeyri-şəffaflıq dəyəri</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Sürüşmə qeyri-şəffaflığı 0-100 arası olmalıdır</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Sürüşdürmə örtüyü irəliləyiş cizgisi rəngi</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Səs səviyyəsinə və parlaqlığa nəzarət üçün irəliləyiş cizgisi rəngi</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Yanlış irəliləyiş cizgisi rəngi</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Sürüşdürmə örtüyü mətn ölçüsü</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Sürüşmə üçün mətn ölçüsü 1-30 arasındadır</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Mətn ölçüsü 1-30 arası olmalıdır</string>
|
||||
<string name="revanced_swipe_threshold_title">Sürüşdürmə böyüklük həddi</string>
|
||||
<string name="revanced_swipe_threshold_summary">Sürüşdürmənin icra edilməsi üçün son dəyər</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Səs səviyyəsin sürüşdürmə təzyiqi</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Hər sürüşdürmədə səs səviyyəsi nə qədər dəyişir</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Dairəvi örtüyü göstər</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Dairəvi örtük göstərilir</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Düzünə örtük göstərilir</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Ən kiçik üslubu aktivləşdir</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Ən kiçik örtük üslubu aktivləşdirilib</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Ən kiçik örtük üslubu qapalıdır</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Sürüşmə örtüyü üslubu</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Üfüqi örtük</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Üfüqi örtük (ən kiçik- yüksək)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Üfüqi örtük (ən kiçik - mərkəz)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Dairəvi örtük</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Dairəvi örtük (ən kiçik)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Şaquli örtük</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Şaquli örtük (ən kiçik)</string>
|
||||
<string name="revanced_swipe_change_video_title">Videoları ötürmək üçün sürüşdürməni aktiv et</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Tam ekran rejimində sürüşdürmə növbəti/əvvəlki videoya ötürəcək</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Tam ekran rejimində sürüşdürmə növbəti/əvvəlki videoya ötürməyəcək</string>
|
||||
@@ -513,6 +522,11 @@ Ekranın sağ tərəfində düzünə sürüşdürərək səs səviyyəsini tənz
|
||||
<string name="revanced_hide_thanks_button_title">\"Təşəkkürlər\"i gizlət</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Təşəkkür düyməsi gizlidir</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Təşəkkür düyməsi göstərilir</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Soruş\'u Gizlət</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Soruş düyməsi gizlidir</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">\"Soruş\" düyməsi göstərilir</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Kəsmə/ gizlət</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Kəsmə düyməsi gizlidir</string>
|
||||
@@ -779,6 +793,11 @@ Audio trek seçimin göstərmək üçün \"Video axınları saxtalaşdır\"ı iO
|
||||
Avtomatik oynatma YouTube ayarlarında dəyişdirilə bilər: Ayarlar → Oxunuş → Növbəti videonu avtomatik oxudun"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Son ekranda bildirilən video göstərilir</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Əlaqəli video örtüyünü tam ekranda gizlət</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Əlaqəli video örtüyü gizlədilib</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Əlaqəli video örtüyü göstərilir</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Video vaxt möhürünü gizlət</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Vaxt möhürü gizlidir</string>
|
||||
@@ -1241,7 +1260,7 @@ Bunu aktivləşdirmə, bəzi regionlarda əngəllənib silinən şəkilləri dü
|
||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Orijinal miniatürlər</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Kadr çəkilişlər</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">Kadr çəkilişləri</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow YouTube videoları üçün izdiham mənbəli miniatürlər təqdim edir. Bu miniatürlər YouTube tərəfindən təqdim edilənlərdən dəfələrlə daha uyğundur.
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow YouTube videoları üçün çox mənbəli miniatürlər təqdim edir. Bu miniatürlər YouTube tərəfindən təqdim edilənlərdən dəfələrlə daha uyğundur.
|
||||
|
||||
Aktivləşdirilərsə, video URL-lər API alıcısına göndəriləcək və başqa məlumat göndərilməyəcək. Videonun DeArrow miniatürləri yoxdursa, orijinal və ya hələ də çəkilişlər göstərilir.
|
||||
|
||||
@@ -1296,6 +1315,9 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
|
||||
<string name="microg_settings_title">GmsCore Tənzimləmələri</string>
|
||||
<string name="microg_settings_summary">GmsCore üçün Tənzimləmələr</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Bu yaxınlarda hesabınıza giriş məlumatlarınızı dəyişmisinizsə, MicroG-ni silin və təkrar quraşdırın.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">URL yönləndirmələrini ötür</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">URL yönləndirmələri ötürülür</string>
|
||||
|
||||
@@ -459,21 +459,30 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Уключыць жэст аўтаматычнай яркасці</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Правядзіце пальцам уніз да самага нізкага значэння яркасці, каб уключыць аўтаматычную яркасць</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Правядзенне пальцам уніз да самага нізкага значэння не ўключае аўтаматычную яркасць</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Аўто</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Тайм-аўт накладання пальцам</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Працягласць бачнага накладання ў мілісекундах</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Непразрыстасць фону накладкі пракруткі</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Значэнне непразрыстасці паміж 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Непразрыстасць пракруткі павінна быць паміж 0-100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Колер слупка прагрэсу накладкі правядзення</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Колер слупка прагрэсу для рэгулявання гучнасці і яркасці</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Несапраўдны колер слупка прагрэсу</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Памер тэксту накладкі правядзення</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Памер тэксту для накладкі правядзення ад 1 да 30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Памер тэксту павінен быць у межах ад 1 да 30</string>
|
||||
<string name="revanced_swipe_threshold_title">Парог велічыні пальцам</string>
|
||||
<string name="revanced_swipe_threshold_summary">Велічыня парогавага значэння для правядзення пальцам</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Адчувальнасць правядзення для гучнасці</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Наколькі змяняецца гучнасць пры кожным правядзенні</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Паказваць кругавое накладанне</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Кругавое накладанне паказваецца</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Гарызантальнае накладанне паказваецца</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Уключыць мінімальны стыль</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Уключаны мінімальны стыль накладання</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Мінімальны стыль накладання выключаны</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Стыль накладкі правядзення</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Гарызантальная накладка</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Гарызантальная накладка (мінімальная — зверху)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Гарызантальная накладка (мінімальная — па цэнтры)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Кругавая накладка</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Кругавая накладка (мінімальная)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Вертыкальная накладка</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Вертыкальная накладка (мінімальная)</string>
|
||||
<string name="revanced_swipe_change_video_title">Уключыць зьмену відэа праз правядзенне пальцам</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Правядзенне пальцам у рэжыме поўнага экрана зьменіць відэа на наступнае/папярэдняе</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Правядзенне пальцам у рэжыме поўнага экрана не зьменіць відэа на наступнае/папярэдняе</string>
|
||||
@@ -513,6 +522,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_thanks_button_title">Схаваць Дзякуй</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Кнопка падзякі схавана</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Паказана кнопка падзякі</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Схаваць кнопку «Запытацца»</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Кнопка «Запытацца» схаваная</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Кнопка «Запытацца» паказаная</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Схаваць кліп</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Кнопка кліпа схавана</string>
|
||||
@@ -780,6 +794,11 @@ Second \"item\" text"</string>
|
||||
Налады → Прайграванне → Аўтаматычнае прайграванне наступнага відэа"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Паказваць прапанаванае відэа на канчатковым экране</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Схаваць накладку звязанага відэа ў поўнаэкранным рэжыме</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Накладка звязанага відэа схавана</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Накладка звязанага відэа паказана</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Схаваць метку часу відэа</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Метка часу схавана</string>
|
||||
@@ -1298,6 +1317,9 @@ Second \"item\" text"</string>
|
||||
<string name="microg_settings_title">Налады GmsCore</string>
|
||||
<string name="microg_settings_summary">Налады для GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Калі вы нядаўна змянілі даныя для ўваходу ў свой уліковы запіс, выдаліце і пераўсталюйце MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Абыход URL-перанакіраванняў</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">Перанакіраванне URL абыходзіць</string>
|
||||
|
||||
@@ -459,21 +459,30 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Задаване на яркост чрез плъзгане</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Плъзгането надолу до най-ниската стойност на жеста за яркост, за да се активира автоматичната яркост</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Плъзгането надолу до най-ниската стойност на жеста за яркост, без дасе активира автоматичната яркост</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Авто</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Задръжка на плъзгащата контрола за показване</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Време за което плъзгащата контрола е видима.</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Плъзгане на фона на наслагването непрозрачност</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Стойност на непрозрачността между 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Непрозрачността на плъзгането трябва да е между 0-100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Цвят на лентата за напредък при плъзгане</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Цветът на лентата за напредък за контролите за сила на звука и яркост</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Невалиден цвят на лентата за напредък</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Размер на текста на наслагването при плъзгане</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Размерът на текста за наслагването при плъзгане между 1-30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Размерът на текста трябва да е между 1-30</string>
|
||||
<string name="revanced_swipe_threshold_title">Праг на величината на плъзгане</string>
|
||||
<string name="revanced_swipe_threshold_summary">Праг преди да се осъществи плъзгането</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Чувствителност при плъзгане за сила на звука</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Колко се променя силата на звука при всяко плъзгане</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Показване на кръгъл овърлей</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Показва се кръгъл овърлей</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Показва се хоризонтален овърлей</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Активиране на минимален стил</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Минималният стил на наслагване е активиран</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Минималният стил на овърлея е деактивиран</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Стил на наслагване при плъзгане</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Хоризонтално наслагване</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Хоризонтално наслагване (минимално – отгоре)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Хоризонтално наслагване (минимално – център)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Кръгово наслагване</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Кръгово наслагване (минимално)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Вертикално наслагване</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Вертикално наслагване (минимално)</string>
|
||||
<string name="revanced_swipe_change_video_title">Включване на превключване на видеото чрез плъзване</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Плъзването в режим на цял екран ще превключи към следващото/предишно видео</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Плъзването в режим на цял екран няма да превключи към следващото/предишно видео</string>
|
||||
@@ -513,6 +522,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_thanks_button_title">Бутон за благодарност</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Бутона за благодарност е скрит</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Бутона за благодарност се показва</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Скриване на \"Попитай\"</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Бутонът \"Попитай\" е скрит</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Бутонът \"Попитай\" е показан</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Бутон за създаване на клип</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Бутона за клип е скрит</string>
|
||||
@@ -780,6 +794,11 @@ Second \"item\" text"</string>
|
||||
Настройки → Възпроизвеждане → Автоматично пускане на следващото видео"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Предложеното видео в края на екрана е показано</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Скриване на наслагването на свързани видеоклипове в цял екран</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Наслагването на свързани видеоклипове е скрито</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Наслагването на свързани видеоклипове е показано</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Скриване на клеймото за време на видеоклипа</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Скрито</string>
|
||||
@@ -1297,6 +1316,9 @@ Second \"item\" text"</string>
|
||||
<string name="microg_settings_title">GmsCore Настройки</string>
|
||||
<string name="microg_settings_summary">Настройки на GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Ако наскоро сте променили данните си за вход в профила, деинсталирайте и инсталирайте отново MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Заобикаляне на URL пренасочване</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">URL пренасочванията се заобикалят</string>
|
||||
|
||||
@@ -459,21 +459,30 @@ MicroG-এর জন্য ব্যাটারি অপ্টিমাইজ
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">স্বয়ংক্রিয়-উজ্জ্বলতার অঙ্গভঙ্গি সক্রিয় করুন</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">উজ্জ্বলতার অঙ্গভঙ্গির সর্বনিম্ন মানে সোয়াইপ ডাউন করলে অটো-উজ্জ্বলতা সক্ষম হয়</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">সর্বনিম্ন মানে সোয়াইপ ডাউন করলে অটো-উজ্জ্বলতা সক্ষম হয় না</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">স্বতস্ফূর্তভাবে</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">ওভার-লে টাইম আউট</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">কত মিলিসেকেন্ডের জন্য ওভারলে দৃশ্যমান হবে</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">অস্বচ্ছতা</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 এর মধ্যে অস্বচ্ছতার মান</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">সোয়াইপের অস্বচ্ছতা অবশ্যই 0-100 এর মধ্যে হতে হবে</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">সোয়াইপ ওভারলে প্রগ্রেস বার এর রং</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">ভলিউম এবং উজ্জ্বলতা নিয়ন্ত্রণের জন্য প্রগ্রেস বার এর রং</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">অবৈধ প্রগ্রেস বার রং</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">সোয়াইপ ওভারলে টেক্সট সাইজ</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">সোয়াইপ ওভারলে-এর জন্য টেক্সট সাইজ ১-৩০ এর মধ্যে</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">টেক্সট সাইজ অবশ্যই ১-৩০ এর মধ্যে হতে হবে</string>
|
||||
<string name="revanced_swipe_threshold_title">সোয়াইপ থ্রেশহোল্ড এর মাত্রা</string>
|
||||
<string name="revanced_swipe_threshold_summary">সোয়াইপ করার থ্রেশহোল্ডের পরিমাণ</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">ভলিউম সোয়াইপ সংবেদনশীলতা</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">প্রতি সোয়াইপে ভলিউম কতটা পরিবর্তিত হয়</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">বৃত্তাকার ওভারলে দেখান</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">বৃত্তাকার ওভারলে দেখানো হয়েছে</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">অনুভূমিক ওভারলে দেখানো হয়েছে</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">ন্যূনতম শৈলী সক্ষম করুন</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimal overlay style enabled করা হয়েছে</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">ন্যূনতম ওভারলে শৈলী নিষ্ক্রিয় করা হয়েছে</string>
|
||||
<string name="revanced_swipe_overlay_style_title">সোয়াইপ ওভারলে শৈলী</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">অনুভূমিক ওভারলে</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">অনুভূমিক ওভারলে (ন্যূনতম - উপরে)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">অনুভূমিক ওভারলে (ন্যূনতম - কেন্দ্র)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">বৃত্তাকার ওভারলে</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">বৃত্তাকার ওভারলে (ন্যূনতম)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">উল্লম্ব ওভারলে</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">উল্লম্ব ওভারলে (ন্যূনতম)</string>
|
||||
<string name="revanced_swipe_change_video_title">ভিডিও পরিবর্তন করতে সোয়াইপ করে সক্ষম করুন</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">ফুলস্ক্রিন মোডে সোয়াইপ করলে পরবর্তী/পূর্ববর্তী ভিডিওতে পরিবর্তন হবে</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">ফুলস্ক্রিন মোডে সোয়াইপ করলে পরবর্তী /পূর্ববর্তী ভিডিওতে পরিবর্তন হবে না</string>
|
||||
@@ -513,6 +522,11 @@ MicroG-এর জন্য ব্যাটারি অপ্টিমাইজ
|
||||
<string name="revanced_hide_thanks_button_title">ধন্যবাদ লুকান</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">ধন্যবাদ বোতাম লুকানো আছে</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">ধন্যবাদ বোতাম দেখানো হয়</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">জিজ্ঞাসা লুকান</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">জিজ্ঞাসা বোতাম লুকানো আছে</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">জিজ্ঞাসা বোতাম দেখানো হয়েছে</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">ক্লিপ লুকান</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">ক্লিপ বোতাম লুকিয়ে রয়েছে</string>
|
||||
@@ -780,6 +794,11 @@ YouTube সেটিংসে অটো প্লে পরিবর্তন
|
||||
সেটিংস → প্লেব্যাক → অটো প্লে পরবর্তী ভিডিও"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">শেষ স্ক্রীনে প্রস্তাবিত ভিডিও দেখানো হয়েছে</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">পূর্ণ-স্ক্রীনে সম্পর্কিত ভিডিও ওভারলে লুকান</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">সম্পর্কিত ভিডিও ওভারলে লুকানো আছে</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">সম্পর্কিত ভিডিও ওভারলে দেখানো হয়েছে</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">ভিডিওর সময়স্ট্যাম্প লুকান</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">সময়স্ট্যাম্প লুকিয়ে রয়েছে</string>
|
||||
@@ -1297,6 +1316,9 @@ DeArrow সম্পর্কে আরও জানতে এখানে ট
|
||||
<string name="microg_settings_title">GmsCore সেটিং</string>
|
||||
<string name="microg_settings_summary">GmsCore এর জন্য সেটিং</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">আপনি যদি সম্প্রতি আপনার অ্যাকাউন্ট লগইন বিশদ পরিবর্তন করে থাকেন, তবে MicroG আনইনস্টল করুন এবং পুনরায় ইনস্টল করুন।</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">URL পুনঃনির্দেশ বাইপাস করুন</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">URL পুনঃনির্দেশ বাইপাস করছে</string>
|
||||
|
||||
@@ -84,6 +84,8 @@ Second \"item\" text"</string>
|
||||
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||
</patch>
|
||||
@@ -132,6 +134,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
</patch>
|
||||
<patch id="layout.panels.popup.playerPopupPanelsPatch">
|
||||
@@ -195,6 +199,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
</patch>
|
||||
<patch id="misc.links.openLinksExternallyPatch">
|
||||
|
||||
@@ -459,21 +459,30 @@ Ajusteu el volum lliscant verticalment a la part dreta de la pantalla"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Activa el gest de la brillantor automàtica</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Deslizar hacia abajo hasta el valor más bajo del gesto de brillo activa el brillo automático</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Deslizar hacia abajo hasta el valor más bajo no activa el brillo automático</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automàtic</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Temps límite de superposició de lliscament</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">La quantitat de mil·lisegons que la superposició és visible</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Opacitat del fons de la superposició de lliscament</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Valor d\'opacitat entre 0 i 100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">L\'opacitat de lliscament ha d\'estar entre 0 i 100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Color de la barra de progrés de la superposició lliscant</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">El color de la barra de progrés per als controls de volum i brillantor</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Color de barra de progrés no vàlid</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Mida del text de la superposició lliscant</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">La mida del text per a la superposició lliscant entre 1 i 30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">La mida del text ha d\'estar entre 1 i 30</string>
|
||||
<string name="revanced_swipe_threshold_title">Llindar de magnitud de lliscament</string>
|
||||
<string name="revanced_swipe_threshold_summary">La quantitat de llindar per a què es produeixi el desplaçament</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Sensibilitat del lliscament de volum</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Quant canvia el volum per lliscament</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Mostra la superposició circular</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Es mostra la superposició circular</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Es mostra la superposició horitzontal</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Activa l\'estil minimalista</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">S\'ha activat l\'estil de superposició minimalista</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">L\'estil de superposició mínima està desactivat</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Estil de superposició de lliscament</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Superposició horitzontal</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Superposició horitzontal (mínima - part superior)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Superposició horitzontal (mínima - centre)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Superposició circular</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Superposició circular (mínima)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Superposició vertical</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Superposició vertical (mínima)</string>
|
||||
<string name="revanced_swipe_change_video_title">Activa la funció de lliscament per canviar vídeos</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Lliscar en mode de pantalla completa canviarà al vídeo següent/anterior</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Lliscar en mode de pantalla completa no canviarà al vídeo següent/anterior</string>
|
||||
@@ -513,6 +522,11 @@ Ajusteu el volum lliscant verticalment a la part dreta de la pantalla"</string>
|
||||
<string name="revanced_hide_thanks_button_title">Amaga Gràcies</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">S\'ha amagat el botó de gràcies</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">El botó Gràcies es mostra</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Amaga la sol·licitud</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">El botó de sol·licitud està amagat</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">El botó de sol·licitud es mostra</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Amaga Clip</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">El botó Clip s\'amaga</string>
|
||||
@@ -780,6 +794,11 @@ La reproducció automàtica es pot canviar a la configuració de YouTube:
|
||||
Configuració → Reproducció → Reprodueix el vídeo següent automàticament"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Es mostra el vídeo suggerit de la pantalla final</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Amaga la superposició de vídeos relacionats a pantalla completa</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">La superposició de vídeos relacionats està amagada</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Es mostra la superposició de vídeos relacionats</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Amagar segell de temps del vídeo</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">El segell de temps està ocult</string>
|
||||
@@ -1296,6 +1315,9 @@ Si actives aquesta opció, es poden desbloquejar qualitats de vídeo més altes"
|
||||
<string name="microg_settings_title">Configuració de GmsCore</string>
|
||||
<string name="microg_settings_summary">Configuració de GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Si recentment heu canviat les dades d\'inici de sessió del vostre compte, desinstal·leu i torneu a instal·lar MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Omet les redireccions d\'URL</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">Les redireccions d\'URL s\'ometent</string>
|
||||
|
||||
@@ -459,21 +459,30 @@ Hlasitost se upravuje svislým přejetím po pravé straně obrazovky"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Povolit gesto pro automatický jas</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Přejetím dolů na nejnižší hodnotu gesta pro jas aktivujete automatický jas</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Přejetím dolů na nejnižší hodnotu neaktivujete automatický jas</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automaticky</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Timeout překrytí gesta</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Doba v milisekundách, po kterou je překrytí viditelné</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Průsvitnost pozadí překrytí tažením</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Hodnota průsvitnosti mezi 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Průsvitnost tažení musí být mezi 0-100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Barva ukazatele průběhu překrytí přejetím</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Barva ukazatele průběhu pro ovládání hlasitosti a jasu</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Neplatná barva ukazatele průběhu</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Velikost textu překrytí přejetím</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Velikost textu překrytí přejetím mezi 1–30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Velikost textu musí být mezi 1–30</string>
|
||||
<string name="revanced_swipe_threshold_title">Práh vynucení gesta</string>
|
||||
<string name="revanced_swipe_threshold_summary">Velikost prahu pro provedení gesta</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Citlivost přejetí hlasitosti</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">O kolik se změní hlasitost na jedno přejetí</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Zobrazit kruhovou překryvnou vrstvu</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Zobrazuje se kruhová překryvná vrstva</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Zobrazuje se vodorovná překryvná vrstva</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Povolit minimální styl</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimální styl překrytí je povolen</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Minimální styl překryvné vrstvy je zakázán</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Styl překrytí přejetím</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Vodorovné překrytí</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Vodorovné překrytí (minimalistické – nahoře)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Vodorovné překrytí (minimalistické – uprostřed)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Kruhové překrytí</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Kruhové překrytí (minimalistické)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Svislé překrytí</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Svislé překrytí (minimalistické)</string>
|
||||
<string name="revanced_swipe_change_video_title">Povolit přejetí prstem pro změnu videa</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Přejetí prstem v režimu celé obrazovky změní video na další/předchozí</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Přejetí prstem v režimu celé obrazovky nebude video měnit na další/předchozí</string>
|
||||
@@ -513,6 +522,11 @@ Hlasitost se upravuje svislým přejetím po pravé straně obrazovky"</string>
|
||||
<string name="revanced_hide_thanks_button_title">Skrýt Poděkování</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Tlačítko Poděkování je skryto</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Tlačítko Poděkování je zobrazeno</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Skrýt Zeptat se</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Tlačítko Zeptat se je skryté</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Tlačítko Zeptat se je zobrazeno</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Skrýt Klip</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Tlačítko Klip je skryto</string>
|
||||
@@ -780,6 +794,11 @@ Automatické přehrávání lze změnit v nastavení YouTube:
|
||||
Nastavení → Přehrávání → Automatické přehrávání dalšího videa"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Navrhované video na konci obrazovky se zobrazuje</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Skrýt překryv souvisejícího videa v režimu celé obrazovky</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Překryv souvisejícího videa je skrytý</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Překryv souvisejícího videa je zobrazen</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Skrýt čas videa</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Čas je skrytý</string>
|
||||
@@ -1296,6 +1315,9 @@ Povolením této funkce lze odemknout vyšší kvality videa"</string>
|
||||
<string name="microg_settings_title">Nastavení GmsCore</string>
|
||||
<string name="microg_settings_summary">Nastavení pro GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Pokud jste nedávno změnili přihlašovací údaje svého účtu, odinstalujte a znovu nainstalujte MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Obcházet přesměrování URL</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">Přesměrování URL jsou obcházena</string>
|
||||
|
||||
@@ -423,21 +423,30 @@ Juster lydstyrken ved at swipe lodret i højre side af skærmen"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Aktivér auto-lysstyrke-bevægelse</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Stryg ned til den laveste værdi af lysstyrke-bevægelsen aktiverer auto-lysstyrke</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Stryg ned til den laveste værdi aktiverer ikke auto-lysstyrke</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automatisk</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Stryg overlay timeout</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Mængden af millisekunder, overlayet er synlig</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Baggrundsgennemsigtighed for swipe-overlay</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Gennemsigtighedsværdi mellem 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Gennemsigtighed for swipe skal være mellem 0-100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Farve på statuslinje for strygeoverlejring</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Farven på statuslinjen for lydstyrke- og lysstyrkeknapper</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Ugyldig farve til statuslinjen</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Tekststørrelse for strygeoverlejring</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Tekststørrelsen for strygeoverlejring mellem 1-30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Tekststørrelsen skal være mellem 1-30</string>
|
||||
<string name="revanced_swipe_threshold_title">Stryg størrelse tærskel</string>
|
||||
<string name="revanced_swipe_threshold_summary">Beløbet for tærskelværdi for stryg der skal ske</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Volumen strygefølsomhed</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Hvor meget lydstyrken ændres pr. strygning</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Vis cirkulært overlejring</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Cirkulært overlejring vises</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Horisontalt overlejring vises</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Aktiver minimal stil</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimal overlay-stil er aktiveret</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Minimal overlejringsstil er deaktiveret</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Stil for stryge-overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Horisontalt overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Horisontalt overlay (minimal - top)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Horisontalt overlay (minimal - center)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Cirkulært overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Cirkulært overlay (minimal)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Vertikalt overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Vertikalt overlay (minimal)</string>
|
||||
<string name="revanced_swipe_change_video_title">Aktivér swipe for at skifte videoer</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Strygning i fuldskærmstilstand vil ændre til den næste/forrige video</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Strygning i fuldskærmstilstand vil ikke ændre til den næste/forrige video</string>
|
||||
@@ -476,6 +485,11 @@ Juster lydstyrken ved at swipe lodret i højre side af skærmen"</string>
|
||||
<string name="revanced_hide_thanks_button_title">Skjul Tak</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Tak knappen er skjult</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Tak knappen er vist</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Skjul Spørg</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Spørg-knappen er skjult</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Spørg-knappen vises</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Skjul klip</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Klip knappen er skjult</string>
|
||||
@@ -743,6 +757,11 @@ Automatisk afspilning kan ændres i YouTube-indstillinger:
|
||||
Indstillinger → Afspilning → Afspil næste video automatisk"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Foreslået video på slutskærmen vises</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Skjul relateret video-overlay i fuldskærm</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Relateret video-overlay er skjult</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Relateret video-overlay vises</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Skjul tidsstempel på video</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Tidsstempel er skjult</string>
|
||||
@@ -1238,6 +1257,9 @@ Aktivering af dette kan låse op for højere videokvalitet"</string>
|
||||
<string name="microg_settings_title">GmsCore Indstillinger</string>
|
||||
<string name="microg_settings_summary">Indstillinger for GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Hvis du for nylig har ændret dine kontooplysninger, skal du afinstallere og geninstallere MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Bypass URL omdirigeringer</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">URL omdirigeringer er omgået</string>
|
||||
|
||||
@@ -452,21 +452,30 @@ Passen Sie die Helligkeit an, indem Sie auf der linken Seite des Bildschirms ver
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Auto-Helligkeit Geste aktivieren</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Wische runter auf den niedrigsten Wert der Helligkeitsgeste, um die automatische Helligkeit zu aktivieren</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Wenn man zum niedrigsten Wert wischt, aktiviert man die automatische Helligkeit nicht</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Autom</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Wischüberlagerungs-Timeout</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Die Anzahl der Millisekunden, die das Overlay sichtbar ist</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Bildschirmüberlagerung Deckkraft Swipe</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Deckkraftwert zwischen 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Die Wischdeckkraft muss zwischen 0 und 100 liegen</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Farbe des Fortschrittsbalkens für die Wischgesten-Überlagerung</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Die Farbe des Fortschrittsbalkens für Lautstärke- und Helligkeitsregler</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Ungültige Farbe für den Fortschrittsbalken</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Textgröße der Wischgesten-Überlagerung</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Die Textgröße für die Wischgesten-Überlagerung zwischen 1 und 30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Die Textgröße muss zwischen 1 und 30 liegen</string>
|
||||
<string name="revanced_swipe_threshold_title">Wischgrößenschwelle</string>
|
||||
<string name="revanced_swipe_threshold_summary">Der Schwellenwert für Wischen</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Lautstärke-Wischgestenempfindlichkeit</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Wie stark sich die Lautstärke pro Wisch ändert</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Kreisförmiges Overlay anzeigen</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Kreisförmiges Overlay wird angezeigt</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Horizontales Overlay wird angezeigt</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Minimalen Stil aktivieren</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimaler Overlay-Stil ist aktiviert</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Minimaler Overlay-Stil ist deaktiviert</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Design des Wisch-Overlays</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Horizontales Overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Horizontales Overlay (minimal - oben)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Horizontales Overlay (minimal - Mitte)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Kreisförmiges Overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Kreisförmiges Overlay (minimal)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Vertikales Overlay</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Vertikales Overlay (minimal)</string>
|
||||
<string name="revanced_swipe_change_video_title">Mit Wischen wechseln Sie zu den Videos</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Im Vollbildmodus wischen, um zum nächsten/vorherigen Video zu wechseln</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Im Vollbildmodus wischen, um nicht zum nächsten/vorherigen Video zu wechseln</string>
|
||||
@@ -506,6 +515,11 @@ Passen Sie die Helligkeit an, indem Sie auf der linken Seite des Bildschirms ver
|
||||
<string name="revanced_hide_thanks_button_title">Dank ausblenden</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Dankeschön-Taste ist ausgeblendet</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Dankeschön Button wird angezeigt</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Ask ausblenden</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Ask-Button ist ausgeblendet</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Ask-Button wird angezeigt</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Clip ausblenden</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Clip-Button ist ausgeblendet</string>
|
||||
@@ -773,6 +787,11 @@ Die automatische Wiedergabe kann in den YouTube-Einstellungen geändert werden:
|
||||
Einstellungen → Wiedergabe → Nächstes Video automatisch abspielen"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Das vorgeschlagene Video auf dem Endbildschirm wird angezeigt</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Zugehöriges Video-Overlay im Vollbildmodus ausblenden</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Zugehöriges Video-Overlay ist ausgeblendet</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Zugehöriges Video-Overlay wird angezeigt</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Verstecke Video-Zeitstempel</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Zeitstempel ist ausgeblendet</string>
|
||||
@@ -1290,6 +1309,9 @@ Durch Aktivieren dieser Option können höhere Videoqualitäten freigeschaltet w
|
||||
<string name="microg_settings_title">GmsCore Einstellungen</string>
|
||||
<string name="microg_settings_summary">Einstellungen für GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Wenn Sie kürzlich Ihre Kontoanmeldedaten geändert haben, deinstallieren Sie MicroG und installieren Sie es erneut.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">URL-Weiterleitungen umgehen</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">URL-Umleitungen werden umgangen</string>
|
||||
|
||||
@@ -86,7 +86,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_screen_03_feed_title">Ροή</string>
|
||||
<string name="revanced_settings_screen_04_general_title">Γενικά</string>
|
||||
<string name="revanced_settings_screen_05_player_title">Οθόνη αναπαραγωγής</string>
|
||||
<string name="revanced_settings_screen_07_seekbar_title">Γραμμή προόδου βίντεο</string>
|
||||
<string name="revanced_settings_screen_07_seekbar_title">Γραμμή προόδου</string>
|
||||
<string name="revanced_settings_screen_08_swipe_controls_title">Έλεγχος με σάρωση οθόνης</string>
|
||||
<string name="revanced_settings_screen_11_misc_title">Διάφορα</string>
|
||||
<string name="revanced_settings_screen_12_video_title">Βίντεο</string>
|
||||
@@ -461,21 +461,30 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Ενεργοποίηση χειρονομίας αυτόματης φωτεινότητας</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Η σάρωση προς τα κάτω στη χαμηλότερη τιμή της χειρονομίας φωτεινότητας ενεργοποιεί την αυτόματη φωτεινότητα</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Η σάρωση προς τα κάτω στη χαμηλότερη τιμή δεν ενεργοποιεί την αυτόματη φωτεινότητα</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Αυτόματη</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Χρονικό όριο εμφάνισης πλαισίου σάρωσης</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">Το χρονικό διάστημα χιλιοστών του δευτερολέπτου που είναι ορατό το πλαίσιο σάρωσης</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Αδιαφάνεια φόντου σάρωσης</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Τιμή αδιαφάνειας μεταξύ 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Η αδιαφάνεια σάρωσης πρέπει να είναι μεταξύ 0-100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Χρώμα γραμμής προόδου σάρωσης</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">Το χρώμα της γραμμής προόδου για τα στοιχεία ελέγχου έντασης ήχου και φωτεινότητας</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Μη έγκυρο χρώμα γραμμής προόδου</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Μέγεθος κειμένου σάρωσης</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">Το μέγεθος κειμένου για τα στοιχεία ελέγχου σάρωσης μεταξύ 1-30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">Το μέγεθος κειμένου πρέπει να είναι μεταξύ 1-30</string>
|
||||
<string name="revanced_swipe_threshold_title">Κατώτατο όριο μεγέθους σάρωσης</string>
|
||||
<string name="revanced_swipe_threshold_summary">Η ελάχιστη απόσταση που θα διανύσετε με το δάκτυλο σας για να είναι αναγνωρίσιμη η χειρονομία σάρωσης</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Ευαισθησία σάρωσης έντασης ήχου</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Πόσο αλλάζει η ένταση ήχου ανά σάρωση</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Εμφάνιση κυκλικής διάταξης</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Η διάταξη των ελέγχων σάρωσης είναι κυκλική</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Η διάταξη των ελέγχων σάρωσης είναι οριζόντια</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Διάταξη μινιμαλιστικού στυλ</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Το μινιμαλιστικό στυλ διάταξης των ελέγχων σάρωσης είναι ενεργοποιημένο</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Το μινιμαλιστικό στυλ διάταξης των ελέγχων σάρωσης είναι απενεργοποιημένο</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Στυλ διάταξης σάρωσης</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Οριζόντια διάταξη</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Οριζόντια διάταξη (ελάχιστη - κορυφή)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Οριζόντια διάταξη (ελάχιστη - κέντρο)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Κυκλική διάταξη</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Κυκλική διάταξη (ελάχιστη)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Κάθετη διάταξη</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Κάθετη διάταξη (ελάχιστη)</string>
|
||||
<string name="revanced_swipe_change_video_title">Χειρονομία εναλλαγής βίντεο στην πλήρη οθόνη</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Σάρωση αριστερά/δεξιά κατά τη λειτουργία πλήρους οθόνης για αλλαγή σε επόμενο/προηγούμενο βίντεο</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Η χειρονομία αλλαγής βίντεο κατά τη λειτουργία πλήρους οθόνης είναι ανενεργή</string>
|
||||
@@ -515,6 +524,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_thanks_button_title">Κουμπί «Σας ευχαριστούμε»</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Κρυμμένο</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Εμφανίζεται</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Κουμπί «Ερώτηση»</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">Κρυμμένο</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Εμφανίζεται</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Κουμπί «Κλιπ»</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Κρυμμένο</string>
|
||||
@@ -782,6 +796,11 @@ Second \"item\" text"</string>
|
||||
Ρυθμίσεις → Αναπαραγωγή→ Αυτόματη αναπαραγωγή επόμενου βίντεο"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Εμφανίζεται</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Απόκρυψη επικάλυψης σχετικού βίντεο σε πλήρη οθόνη</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Η επικάλυψη σχετικού βίντεο είναι κρυφή</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Εμφανίζεται η επικάλυψη σχετικού βίντεο</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Χρονική πρόοδος βίντεο</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Κρυμμένη</string>
|
||||
@@ -837,8 +856,8 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_ryd_estimated_like_summary_on">Τα βίντεο με απενεργοποιημένα «Μου αρέσει» εμφανίζουν έναν εκτιμώμενο αριθμό «Μου αρέσει»</string>
|
||||
<string name="revanced_ryd_estimated_like_summary_off">Τα εκτιμώμενα «Μου αρέσει» δεν εμφανίζονται</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_title">Εμφάνιση μηνύματος αν το API δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_summary_on">Να εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το Return YouTube Dislike δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_summary_off">Να μην εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το Return YouTube Dislike δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_summary_on">Εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το Return YouTube Dislike δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_ryd_toast_on_connection_error_summary_off">Δεν εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το Return YouTube Dislike δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_ryd_about">Σχετικά με</string>
|
||||
<string name="revanced_ryd_attribution_summary">Τα δεδομένα Dislike παρέχονται από το API του Return YouTube Dislike. Πατήστε εδώ για να μάθετε περισσότερα</string>
|
||||
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
|
||||
@@ -897,8 +916,8 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_sb_enable_auto_hide_skip_segment_button_sum_on">Το κουμπί παράλειψης κρύβεται μετά από μερικά δευτερόλεπτα</string>
|
||||
<string name="revanced_sb_enable_auto_hide_skip_segment_button_sum_off">Το κουμπί παράλειψης εμφανίζεται σε όλο το τμήμα</string>
|
||||
<string name="revanced_sb_general_skiptoast">Εμφάνιση μηνύματος κατά την παράλειψη</string>
|
||||
<string name="revanced_sb_general_skiptoast_sum_on">Να εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης όταν ένα τμήμα παραλείπεται αυτόματα. Πατήστε για να δείτε ένα παράδειγμα</string>
|
||||
<string name="revanced_sb_general_skiptoast_sum_off">Να μην εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης. Πατήστε για να δείτε ένα παράδειγμα</string>
|
||||
<string name="revanced_sb_general_skiptoast_sum_on">Εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης όταν ένα τμήμα παραλείπεται αυτόματα. Πατήστε για να δείτε ένα παράδειγμα</string>
|
||||
<string name="revanced_sb_general_skiptoast_sum_off">Δεν εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης. Πατήστε για να δείτε ένα παράδειγμα</string>
|
||||
<string name="revanced_sb_general_time_without">Εμφάνιση μήκους βίντεο χωρίς τα τμήματα</string>
|
||||
<string name="revanced_sb_general_time_without_sum_on">Εμφανίζεται το μήκος βίντεο μείον όλα τα τμήματα, σε παρένθεση δίπλα στο πλήρες μήκος βίντεο</string>
|
||||
<string name="revanced_sb_general_time_without_sum_off">Εμφανίζεται το πλήρες μήκος του βίντεο</string>
|
||||
@@ -917,8 +936,8 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_sb_guidelines_popup_open">Δείξτε μου</string>
|
||||
<string name="revanced_sb_general">Γενικά</string>
|
||||
<string name="revanced_sb_toast_on_connection_error_title">Εμφάνιση μηνύματος αν το API δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_sb_toast_on_connection_error_summary_on">Να εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το SponsorBlock δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_sb_toast_on_connection_error_summary_off">Να μην εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το SponsorBlock δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_sb_toast_on_connection_error_summary_on">Εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το SponsorBlock δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_sb_toast_on_connection_error_summary_off">Δεν εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το SponsorBlock δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_sb_general_skipcount">Mετρητής παραλείψεων τμημάτων</string>
|
||||
<string name="revanced_sb_general_skipcount_sum_on">Επιτρέπει στον πίνακα κατάταξης SponsorBlock να γνωρίζει πόσος χρόνος εξοικονομήθηκε. Αποστέλλεται ένα μήνυμα στον πίνακα κατάταξης κάθε φορά που παραλείπεται ένα τμήμα</string>
|
||||
<string name="revanced_sb_general_skipcount_sum_off">Ο μετρητής παραλείψεων δεν είναι ενεργός</string>
|
||||
@@ -1247,8 +1266,8 @@ Second \"item\" text"</string>
|
||||
|
||||
Πατήστε για να μάθετε περισσότερα για το DeArrow"</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">Εμφάνιση μηνύματος αν το API δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Να εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το DeArrow δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Να μην εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το DeArrow δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το DeArrow δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Δεν εμφανίζεται μήνυμα στο κάτω μέρος της οθόνης αν το DeArrow δεν είναι διαθέσιμο</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_title">Διεύθυνση API του DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">Η διεύθυνση URL του τελικού σημείου αποθήκευσης μικρογραφιών DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_title">Λήψεις ακίνητων καρέ</string>
|
||||
@@ -1296,6 +1315,9 @@ Second \"item\" text"</string>
|
||||
<string name="microg_settings_title">Ρυθμίσεις GmsCore</string>
|
||||
<string name="microg_settings_summary">Ρυθμίσεις για το MicroG GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Εάν αλλάξατε πρόσφατα τα στοιχεία σύνδεσης του λογαριασμού σας, απεγκαταστήστε και εγκαταστήστε ξανά το MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Παράκαμψη ανακατευθύνσεων συνδέσμων</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">Οι ανακατευθύνσεις συνδέσμων URL παρακάμπτονται</string>
|
||||
|
||||
@@ -456,21 +456,30 @@ Ajusta el volumen deslizando verticalmente en el lado derecho de la pantalla"</s
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Activar gesto de brillo automático</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Deslizar hacia abajo hasta el valor más bajo del gesto de brillo permite el brillo automático</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Deslizar hacia abajo hasta el valor más bajo no habilita el brillo automático</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automático</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">Tiempo de espera del deslizado</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">La cantidad de milisegundos que la superposición es visible</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_title">Opacidad del fondo de la superposición de deslizamiento</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_summary">Valor de opacidad entre 0-100</string>
|
||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">La opacidad de la superposición de deslizamiento debe estar entre 0 y 100</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_title">Color de la barra de progreso de la superposición de deslizamiento</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_summary">El color de la barra de progreso para los controles de volumen y brillo</string>
|
||||
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Color de barra de progreso no válido</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Tamaño del texto de la superposición de deslizamiento</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">El tamaño del texto para la superposición de deslizamiento entre 1 y 30</string>
|
||||
<string name="revanced_swipe_text_overlay_size_invalid_toast">El tamaño del texto debe estar entre 1 y 30</string>
|
||||
<string name="revanced_swipe_threshold_title">Umbral de magnitud del deslizamiento</string>
|
||||
<string name="revanced_swipe_threshold_summary">La cantidad de umbral para que se desliza</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_title">Sensibilidad del deslizamiento de volumen</string>
|
||||
<string name="revanced_swipe_volume_sensitivity_summary">Cuánto cambia el volumen por deslizamiento</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_title">Mostrar superposición circular</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_on">Se muestra la superposición circular</string>
|
||||
<string name="revanced_swipe_show_circular_overlay_summary_off">Se muestra la superposición horizontal</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_title">Habilitar estilo minimalista</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">El estilo de superposición mínima está habilitado</string>
|
||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">El estilo de superposición mínima está desactivado</string>
|
||||
<string name="revanced_swipe_overlay_style_title">Estilo de superposición de deslizamiento</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_1">Superposición horizontal</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_2">Superposición horizontal (mínima - superior)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_3">Superposición horizontal (mínima - centro)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_4">Superposición circular</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_5">Superposición circular (mínima)</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_6">Superposición vertical</string>
|
||||
<string name="revanced_swipe_overlay_style_entry_7">Superposición vertical (mínima)</string>
|
||||
<string name="revanced_swipe_change_video_title">Habilita el deslizamiento para cambiar videos</string>
|
||||
<string name="revanced_swipe_change_video_summary_on">Deslizar en modo de pantalla completa cambiará al video siguiente/anterior</string>
|
||||
<string name="revanced_swipe_change_video_summary_off">Deslizar en modo de pantalla completa no cambiará al video siguiente/anterior</string>
|
||||
@@ -510,6 +519,11 @@ Ajusta el volumen deslizando verticalmente en el lado derecho de la pantalla"</s
|
||||
<string name="revanced_hide_thanks_button_title">Ocultar Gracias</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">El botón de gracias está oculto</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Se muestra el botón de gracias</string>
|
||||
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||
<string name="revanced_hide_ask_button_title">Ocultar pregunta</string>
|
||||
<string name="revanced_hide_ask_button_summary_on">El botón \"Preguntar\" está oculto</string>
|
||||
<string name="revanced_hide_ask_button_summary_off">Se muestra el botón \"Preguntar\"</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Ocultar Recortar</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">El botón del recortar está oculto</string>
|
||||
@@ -777,6 +791,11 @@ La reproducción automática se puede cambiar en la configuración de YouTube:
|
||||
Configuración → Reproducción → Reproducir el siguiente vídeo automáticamente"</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_off">Se muestra el vídeo sugerido de la pantalla final</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_video_overlay_title">Ocultar la superposición de videos relacionados en pantalla completa</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_on">Se oculta la superposición de videos relacionados</string>
|
||||
<string name="revanced_hide_related_video_overlay_summary_off">Se muestra la superposición de videos relacionados</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Ocultar fecha y hora de vídeo</string>
|
||||
<string name="revanced_hide_timestamp_summary_on">Marca de tiempo oculta</string>
|
||||
@@ -1285,6 +1304,9 @@ Habilitar esto puede desbloquear calidades de vídeo más altas"</string>
|
||||
<string name="microg_settings_title">Ajustes de GmsCore</string>
|
||||
<string name="microg_settings_summary">Configuración de GmsCore</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||
<string name="microg_offline_account_login_error">Si has cambiado recientemente los datos de inicio de sesión de tu cuenta, desinstala y vuelve a instalar MicroG.</string>
|
||||
</patch>
|
||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||
<string name="revanced_bypass_url_redirects_title">Evitar redirecciones de URL</string>
|
||||
<string name="revanced_bypass_url_redirects_summary_on">Se omiten las redirecciones de URL</string>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user