mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-04-29 22:24:27 +02:00
chore: Merge branch dev
to main
(#4699)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net> Co-authored-by: Denis Kerner <11692788+dtricks@users.noreply.github.com> Co-authored-by: Denis Kerner <denis.kerner@vodafone.com> Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de> Co-authored-by: Dawid Krajcarz <80264606+drobotk@users.noreply.github.com> Co-authored-by: Jakub Blažej <trogper@gmail.com> Co-authored-by: MarcaD <152095496+MarcaDian@users.noreply.github.com> Co-authored-by: Aoife McCullough <92225779+AoifeMcCull@users.noreply.github.com> Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: user5095 <140261471+user5095@users.noreply.github.com> Co-authored-by: Brosssh <44944126+Brosssh@users.noreply.github.com> Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: oSumAtrIX <github@osumatrix.me>
This commit is contained in:
commit
47ebec532e
139
CHANGELOG.md
139
CHANGELOG.md
@ -1,3 +1,142 @@
|
||||
# [5.19.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.16...v5.19.0-dev.17) (2025-04-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
|
||||
|
||||
# [5.19.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.15...v5.19.0-dev.16) (2025-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
|
||||
|
||||
# [5.19.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.14...v5.19.0-dev.15) (2025-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
|
||||
|
||||
# [5.19.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.13...v5.19.0-dev.14) (2025-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
|
||||
|
||||
# [5.19.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.12...v5.19.0-dev.13) (2025-04-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
|
||||
|
||||
# [5.19.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.11...v5.19.0-dev.12) (2025-04-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
|
||||
|
||||
# [5.19.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.10...v5.19.0-dev.11) (2025-04-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
|
||||
|
||||
# [5.19.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.9...v5.19.0-dev.10) (2025-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
|
||||
|
||||
# [5.19.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.8...v5.19.0-dev.9) (2025-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
|
||||
|
||||
# [5.19.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.7...v5.19.0-dev.8) (2025-04-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([ea92a2e](https://github.com/ReVanced/revanced-patches/commit/ea92a2e36c7aab3bd115f7d0ec40467179485b32))
|
||||
|
||||
# [5.19.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.6...v5.19.0-dev.7) (2025-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bb3e32](https://github.com/ReVanced/revanced-patches/commit/0bb3e32244fa10809aee5c4e549f77ed4054537e))
|
||||
|
||||
# [5.19.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.5...v5.19.0-dev.6) (2025-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([0b9a5e7](https://github.com/ReVanced/revanced-patches/commit/0b9a5e7f89a89d971762b3539166d4f145111481))
|
||||
|
||||
# [5.19.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.4...v5.19.0-dev.5) (2025-04-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([d7a7a0b](https://github.com/ReVanced/revanced-patches/commit/d7a7a0b982dbafa181b04f984a5f7618fb067c2a))
|
||||
|
||||
# [5.19.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.3...v5.19.0-dev.4) (2025-04-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([ddc6e4c](https://github.com/ReVanced/revanced-patches/commit/ddc6e4c34fe35fa34bd859bf34e25645a23dbdc9))
|
||||
|
||||
# [5.19.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.2...v5.19.0-dev.3) (2025-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([34c14c9](https://github.com/ReVanced/revanced-patches/commit/34c14c9b443092824d035afd77adb678c6f89e3e))
|
||||
|
||||
# [5.19.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.1...v5.19.0-dev.2) (2025-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([d0c85f0](https://github.com/ReVanced/revanced-patches/commit/d0c85f044083d720c63a8ea4ff15d42eefeb9db7))
|
||||
|
||||
# [5.19.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.2...v5.19.0-dev.1) (2025-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([ff846b0](https://github.com/ReVanced/revanced-patches/commit/ff846b0b7ef5060caaffedb08c1f901172f5b2d1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([87c86b5](https://github.com/ReVanced/revanced-patches/commit/87c86b53a91b0054ac892a3f02bbe7bf83bbf813))
|
||||
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([8e16483](https://github.com/ReVanced/revanced-patches/commit/8e1648322948151e4565fb0d86e0f37d0a02d73f))
|
||||
|
||||
## [5.18.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.1...v5.18.1-dev.2) (2025-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([503b7eb](https://github.com/ReVanced/revanced-patches/commit/503b7eb8d413ef7f248394f128f3b2a6f3192ba6))
|
||||
|
||||
## [5.18.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.18.1-dev.1) (2025-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Remove background playback restrictions:** Do not show media controls when playing Shorts from the feed ([2ed675c](https://github.com/ReVanced/revanced-patches/commit/2ed675cdd058fb5876381a9d30dee5263f6b2e26))
|
||||
|
||||
# [5.18.0](https://github.com/ReVanced/revanced-patches/compare/v5.17.0...v5.18.0) (2025-03-28)
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@ -799,4 +800,14 @@ public class Utils {
|
||||
builder.getContext().setTheme(editTextDialogStyle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a color resource or hex code to an int representation of the color.
|
||||
*/
|
||||
public static int getColorFromString(String colorString) throws IllegalArgumentException, Resources.NotFoundException {
|
||||
if (colorString.startsWith("#")) {
|
||||
return Color.parseColor(colorString);
|
||||
}
|
||||
return getResourceColor(colorString);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,11 @@ public class BaseSettings {
|
||||
|
||||
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message");
|
||||
|
||||
/**
|
||||
* Use the icons declared in the preferences created during patching. If no icons or styles are declared then this setting does nothing.
|
||||
*/
|
||||
public static final BooleanSetting SHOW_MENU_ICONS = new BooleanSetting("revanced_show_menu_icons", TRUE, true);
|
||||
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
|
||||
public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
|
||||
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
@ -86,7 +86,6 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initialize this instance, and do any custom behavior.
|
||||
* <p>
|
||||
@ -95,7 +94,10 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
* so all app specific {@link Setting} instances are loaded before this method returns.
|
||||
*/
|
||||
protected void initialize() {
|
||||
final var identifier = Utils.getResourceIdentifier("revanced_prefs", "xml");
|
||||
String preferenceResourceName = BaseSettings.SHOW_MENU_ICONS.get()
|
||||
? "revanced_prefs_icons"
|
||||
: "revanced_prefs";
|
||||
final var identifier = Utils.getResourceIdentifier(preferenceResourceName, "xml");
|
||||
if (identifier == 0) return;
|
||||
addPreferencesFromResource(identifier);
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
package app.revanced.extension.spotify.layout.theme;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class CustomThemePatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static long getThemeColor(String colorString) {
|
||||
try {
|
||||
return Utils.getColorFromString(colorString);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Invalid custom color: " + colorString, ex);
|
||||
return Color.BLACK;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package app.revanced.extension.spotify.misc;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
import com.spotify.remoteconfig.internal.AccountAttribute;
|
||||
import com.spotify.home.evopage.homeapi.proto.Section;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -14,6 +14,25 @@ import app.revanced.extension.shared.Logger;
|
||||
@SuppressWarnings("unused")
|
||||
public final class UnlockPremiumPatch {
|
||||
|
||||
private static final String SPOTIFY_MAIN_ACTIVITY_LEGACY = "com.spotify.music.MainActivity";
|
||||
|
||||
/**
|
||||
* If the app target is 8.6.98.900.
|
||||
*/
|
||||
private static final boolean IS_SPOTIFY_LEGACY_APP_TARGET;
|
||||
|
||||
static {
|
||||
boolean legacy;
|
||||
try {
|
||||
Class.forName(SPOTIFY_MAIN_ACTIVITY_LEGACY);
|
||||
legacy = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
legacy = false;
|
||||
}
|
||||
|
||||
IS_SPOTIFY_LEGACY_APP_TARGET = legacy;
|
||||
}
|
||||
|
||||
private static class OverrideAttribute {
|
||||
/**
|
||||
* Account attribute key.
|
||||
@ -54,8 +73,8 @@ public final class UnlockPremiumPatch {
|
||||
// Make sure playing songs is not disabled remotely and playlists show up.
|
||||
new OverrideAttribute("streaming", TRUE),
|
||||
// Allows adding songs to queue and removes the smart shuffle mode restriction,
|
||||
// allowing to pick any of the other modes.
|
||||
new OverrideAttribute("pick-and-shuffle", FALSE),
|
||||
// allowing to pick any of the other modes. Flag is not present in legacy app target.
|
||||
new OverrideAttribute("pick-and-shuffle", FALSE, !IS_SPOTIFY_LEGACY_APP_TARGET),
|
||||
// Disables shuffle-mode streaming-rule, which forces songs to be played shuffled
|
||||
// and breaks the player when other patches are applied.
|
||||
new OverrideAttribute("streaming-rules", ""),
|
||||
@ -69,23 +88,51 @@ public final class UnlockPremiumPatch {
|
||||
new OverrideAttribute("tablet-free", FALSE, false)
|
||||
);
|
||||
|
||||
private static final List<Integer> REMOVED_HOME_SECTIONS = List.of(
|
||||
Section.VIDEO_BRAND_AD_FIELD_NUMBER,
|
||||
Section.IMAGE_BRAND_AD_FIELD_NUMBER
|
||||
);
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Injection point. Override account attributes.
|
||||
*/
|
||||
public static void overrideAttribute(Map<String, AccountAttribute> attributes) {
|
||||
public static void overrideAttribute(Map<String, /*AccountAttribute*/ Object> attributes) {
|
||||
try {
|
||||
for (var override : OVERRIDES) {
|
||||
var attribute = attributes.get(override.key);
|
||||
if (attribute == null) {
|
||||
if (override.isExpected) {
|
||||
Logger.printException(() -> "''" + override.key + "' expected but not found");
|
||||
Logger.printException(() -> "'" + override.key + "' expected but not found");
|
||||
}
|
||||
} else {
|
||||
attribute.value_ = override.overrideValue;
|
||||
Object overrideValue = override.overrideValue;
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
((com.spotify.useraccount.v1.AccountAttribute) attribute).value_ = overrideValue;
|
||||
} else {
|
||||
((com.spotify.remoteconfig.internal.AccountAttribute) attribute).value_ = overrideValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "overrideAttribute failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Remove station data from Google assistant URI.
|
||||
*/
|
||||
public static String removeStationString(String spotifyUriOrUrl) {
|
||||
return spotifyUriOrUrl.replace("spotify:station:", "spotify:");
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Remove ads sections from home.
|
||||
*/
|
||||
public static void removeHomeSections(List<Section> sections) {
|
||||
try {
|
||||
sections.removeIf(section -> REMOVED_HOME_SECTIONS.contains(section.featureTypeCase_));
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Remove home sections failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.spotify.home.evopage.homeapi.proto;
|
||||
|
||||
public final class Section {
|
||||
public static final int VIDEO_BRAND_AD_FIELD_NUMBER = 20;
|
||||
public static final int IMAGE_BRAND_AD_FIELD_NUMBER = 21;
|
||||
public int featureTypeCase_;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.spotify.useraccount.v1;
|
||||
|
||||
/**
|
||||
* Used for target 8.6.98.900. Class is still present in newer app targets.
|
||||
*/
|
||||
public class AccountAttribute {
|
||||
public Object value_;
|
||||
}
|
@ -9,7 +9,6 @@ import app.revanced.extension.tiktok.settings.preference.categories.DownloadsPre
|
||||
import app.revanced.extension.tiktok.settings.preference.categories.FeedFilterPreferenceCategory;
|
||||
import app.revanced.extension.tiktok.settings.preference.categories.ExtensionPreferenceCategory;
|
||||
import app.revanced.extension.tiktok.settings.preference.categories.SimSpoofPreferenceCategory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Preference fragment for ReVanced settings
|
||||
|
@ -163,7 +163,7 @@ internal object TwiFucker {
|
||||
|
||||
private fun JSONObject.entryIsWhoToFollow(): Boolean =
|
||||
optString("entryId").let {
|
||||
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-")
|
||||
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-") || it.startsWith("who-to-subscribe-")
|
||||
}
|
||||
|
||||
private fun JSONObject.itemContainsPromotedUser(): Boolean =
|
||||
|
@ -45,13 +45,24 @@ public class ThemeHelper {
|
||||
return "@color/yt_black3";
|
||||
}
|
||||
|
||||
private static int getThemeColor(String resourceName, int defaultColor) {
|
||||
try {
|
||||
return Utils.getColorFromString(resourceName);
|
||||
} catch (Exception ex) {
|
||||
// User entered an invalid custom theme color.
|
||||
// Normally this should never be reached, and no localized strings are needed.
|
||||
Utils.showToastLong("Invalid custom theme color: " + resourceName);
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The dark theme color as specified by the Theme patch (if included),
|
||||
* or the dark mode background color unpatched YT uses.
|
||||
*/
|
||||
public static int getDarkThemeColor() {
|
||||
if (darkThemeColor == null) {
|
||||
darkThemeColor = getColorInt(darkThemeResourceName());
|
||||
darkThemeColor = getThemeColor(darkThemeResourceName(), Color.BLACK);
|
||||
}
|
||||
return darkThemeColor;
|
||||
}
|
||||
@ -71,18 +82,11 @@ public class ThemeHelper {
|
||||
*/
|
||||
public static int getLightThemeColor() {
|
||||
if (lightThemeColor == null) {
|
||||
lightThemeColor = getColorInt(lightThemeResourceName());
|
||||
lightThemeColor = getThemeColor(lightThemeResourceName(), Color.WHITE);
|
||||
}
|
||||
return lightThemeColor;
|
||||
}
|
||||
|
||||
private static int getColorInt(String colorString) {
|
||||
if (colorString.startsWith("#")) {
|
||||
return Color.parseColor(colorString);
|
||||
}
|
||||
return Utils.getResourceColor(colorString);
|
||||
}
|
||||
|
||||
public static int getBackgroundColor() {
|
||||
return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor();
|
||||
}
|
||||
@ -96,6 +100,6 @@ public class ThemeHelper {
|
||||
? "yt_black3"
|
||||
: "yt_white1";
|
||||
|
||||
return getColorInt(colorName);
|
||||
return Utils.getColorFromString(colorName);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ -23,7 +24,13 @@ public class BackgroundPlaybackPatch {
|
||||
// 7. Close the Short
|
||||
// 8. Resume playing the regular video
|
||||
// 9. Minimize the app (PIP should appear)
|
||||
return !ShortsPlayerState.isOpen();
|
||||
if (ShortsPlayerState.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the video player is opened and it's not playing in the feed.
|
||||
PlayerType current = PlayerType.getCurrent();
|
||||
return !current.isNoneOrHidden() && current != PlayerType.INLINE_MINIMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,20 +2,16 @@ package app.revanced.extension.youtube.patches;
|
||||
|
||||
import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike.Vote;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
@ -60,12 +56,12 @@ public class ReturnYouTubeDislikePatch {
|
||||
private static volatile ReturnYouTubeDislike lastLithoShortsVideoData;
|
||||
|
||||
/**
|
||||
* Because the litho Shorts spans are created after {@link ReturnYouTubeDislikeFilterPatch}
|
||||
* detects the video ids, after the user votes the litho will update
|
||||
* but {@link #lastLithoShortsVideoData} is not the correct data to use.
|
||||
* If this is true, then instead use {@link #currentVideoData}.
|
||||
* Because litho Shorts spans are created offscreen after {@link ReturnYouTubeDislikeFilterPatch}
|
||||
* detects the video ids, but the current Short can arbitrarily reload the same span,
|
||||
* then use the {@link #lastLithoShortsVideoData} if this value is greater than zero.
|
||||
*/
|
||||
private static volatile boolean lithoShortsShouldUseCurrentData;
|
||||
@GuardedBy("ReturnYouTubeDislikePatch.class")
|
||||
private static int useLithoShortsVideoDataCount;
|
||||
|
||||
/**
|
||||
* Last video id prefetched. Field is to prevent prefetching the same video id multiple times in a row.
|
||||
@ -83,12 +79,28 @@ public class ReturnYouTubeDislikePatch {
|
||||
private static void clearData() {
|
||||
currentVideoData = null;
|
||||
lastLithoShortsVideoData = null;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
synchronized (ReturnYouTubeDislike.class) {
|
||||
useLithoShortsVideoDataCount = 0;
|
||||
}
|
||||
|
||||
// Rolling number text should not be cleared,
|
||||
// as it's used if incognito Short is opened/closed
|
||||
// while a regular video is on screen.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If {@link #useLithoShortsVideoDataCount} was greater than zero.
|
||||
*/
|
||||
private static boolean decrementUseLithoDataIfNeeded() {
|
||||
synchronized (ReturnYouTubeDislikePatch.class) {
|
||||
if (useLithoShortsVideoDataCount > 0) {
|
||||
useLithoShortsVideoDataCount--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Litho player for both regular videos and Shorts.
|
||||
@ -152,10 +164,13 @@ public class ReturnYouTubeDislikePatch {
|
||||
return getShortsSpan(original, true);
|
||||
}
|
||||
|
||||
if (conversionContextString.contains("|shorts_like_button.eml")
|
||||
&& !Utils.containsNumber(original)) {
|
||||
Logger.printDebug(() -> "Replacing hidden likes count");
|
||||
return getShortsSpan(original, false);
|
||||
if (conversionContextString.contains("|shorts_like_button.eml")) {
|
||||
if (!Utils.containsNumber(original)) {
|
||||
Logger.printDebug(() -> "Replacing hidden likes count");
|
||||
return getShortsSpan(original, false);
|
||||
} else {
|
||||
decrementUseLithoDataIfNeeded();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onLithoTextLoaded failure", ex);
|
||||
@ -170,7 +185,14 @@ public class ReturnYouTubeDislikePatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
ReturnYouTubeDislike videoData = lastLithoShortsVideoData;
|
||||
final ReturnYouTubeDislike videoData;
|
||||
if (decrementUseLithoDataIfNeeded()) {
|
||||
// New Short is loading off screen.
|
||||
videoData = lastLithoShortsVideoData;
|
||||
} else {
|
||||
videoData = currentVideoData;
|
||||
}
|
||||
|
||||
if (videoData == null) {
|
||||
// The Shorts litho video id filter did not detect the video id.
|
||||
// This is normal in incognito mode, but otherwise is abnormal.
|
||||
@ -178,19 +200,6 @@ public class ReturnYouTubeDislikePatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
// Use the correct dislikes data after voting.
|
||||
if (lithoShortsShouldUseCurrentData) {
|
||||
if (isDislikesSpan) {
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
}
|
||||
videoData = currentVideoData;
|
||||
if (videoData == null) {
|
||||
Logger.printException(() -> "currentVideoData is null"); // Should never happen
|
||||
return original;
|
||||
}
|
||||
Logger.printDebug(() -> "Using current video data for litho span");
|
||||
}
|
||||
|
||||
return isDislikesSpan
|
||||
? videoData.getDislikeSpanForShort((Spanned) original)
|
||||
: videoData.getLikeSpanForShort((Spanned) original);
|
||||
@ -445,7 +454,10 @@ public class ReturnYouTubeDislikePatch {
|
||||
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
videoData.setVideoIdIsShort(true);
|
||||
lastLithoShortsVideoData = videoData;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
synchronized (ReturnYouTubeDislikePatch.class) {
|
||||
// Use litho Shorts data for the next like and dislike spans.
|
||||
useLithoShortsVideoDataCount = 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean videoIdIsSame(@Nullable ReturnYouTubeDislike fetch, @Nullable String videoId) {
|
||||
@ -480,13 +492,6 @@ public class ReturnYouTubeDislikePatch {
|
||||
for (Vote v : Vote.values()) {
|
||||
if (v.value == vote) {
|
||||
videoData.sendVote(v);
|
||||
|
||||
if (isNoneHiddenOrMinimized) {
|
||||
if (lastLithoShortsVideoData != null) {
|
||||
lithoShortsShouldUseCurrentData = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -454,16 +454,20 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
}
|
||||
|
||||
private static boolean hideShelves() {
|
||||
// If the player is opened while library is selected,
|
||||
// then filter any recommendations below the player.
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()
|
||||
// Or if the search is active while library is selected, then also filter.
|
||||
|| NavigationBar.isSearchBarActive()) {
|
||||
// Horizontal shelves are used for music/game links in video descriptions,
|
||||
// such as https://youtube.com/watch?v=W8kI1na3S2M
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must check search bar after player type, since search results
|
||||
// can be in the background behind an open player.
|
||||
if (NavigationBar.isSearchBarActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not hide if the navigation back button is visible,
|
||||
// otherwise the content shelves in the YouTube Movie/Courses pages is hidden.
|
||||
// otherwise the content shelves in the explore/music/courses pages are hidde.
|
||||
if (NavigationBar.isBackButtonVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2,12 +2,25 @@ package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
|
||||
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
||||
private static final boolean AVAILABLE_ON_LAUNCH = SpoofVideoStreamsPatch.notSpoofingToAndroid();
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
// Check conditions of launch and now. Otherwise if spoofing is changed
|
||||
// without a restart the setting will show as available when it's not.
|
||||
return AVAILABLE_ON_LAUNCH && SpoofVideoStreamsPatch.notSpoofingToAndroid();
|
||||
}
|
||||
}
|
||||
|
||||
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
private final ByteArrayFilterGroup exception;
|
||||
|
@ -226,6 +226,7 @@ public final class SeekbarColorPatch {
|
||||
}
|
||||
|
||||
private static String loadRawResourceAsString(int resourceId) {
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
|
||||
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
|
||||
return scanner.next();
|
||||
@ -281,6 +282,20 @@ public final class SeekbarColorPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* 19.49+
|
||||
*/
|
||||
public static int[] getPlayerLinearGradient(int[] original, int x0, int y1) {
|
||||
// This hook is used for both the player and the feed.
|
||||
// Feed usage always has x0 and y1 value of zero, and the player is always non zero.
|
||||
if (HIDE_SEEKBAR_THUMBNAIL_ENABLED && x0 == 0 && y1 == 0) {
|
||||
return HIDDEN_SEEKBAR_GRADIENT_COLORS;
|
||||
}
|
||||
return getPlayerLinearGradient(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Pre 19.49
|
||||
*/
|
||||
public static int[] getPlayerLinearGradient(int[] original) {
|
||||
return SEEKBAR_CUSTOM_COLOR_ENABLED
|
||||
|
@ -19,6 +19,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
|
||||
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
||||
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
||||
import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
||||
@ -198,7 +199,7 @@ public class Settings extends BaseSettings {
|
||||
// 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);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE, new HideAudioFlyoutMenuAvailability());
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||
|
@ -0,0 +1,36 @@
|
||||
package app.revanced.extension.youtube.settings.preference;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class HideAudioFlyoutMenuPreference extends SwitchPreference {
|
||||
|
||||
{
|
||||
// Audio menu is not available if spoofing to Android client type.
|
||||
if (!SpoofVideoStreamsPatch.notSpoofingToAndroid()) {
|
||||
String summary = str("revanced_hide_player_flyout_audio_track_not_available");
|
||||
setSummary(summary);
|
||||
setSummaryOn(summary);
|
||||
setSummaryOff(summary);
|
||||
}
|
||||
}
|
||||
|
||||
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
public HideAudioFlyoutMenuPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.18.0
|
||||
version = 5.19.0-dev.17
|
||||
|
@ -132,6 +132,10 @@ public final class app/revanced/patches/amazon/DeepLinkingPatchKt {
|
||||
public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/angulus/ads/RemoveAdsPatchKt {
|
||||
public static final fun getAngulusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt {
|
||||
public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@ -268,6 +272,10 @@ public final class app/revanced/patches/messenger/inputfield/DisableTypingIndica
|
||||
public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/messenger/navbar/RemoveMetaAITabPatchKt {
|
||||
public static final fun getRemoveMetaAITabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
|
||||
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@ -396,6 +404,10 @@ 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/protonmail/signature/RemoveSentFromSignaturePatchKt {
|
||||
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt {
|
||||
public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@ -556,7 +568,9 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
|
||||
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
@ -1096,6 +1110,14 @@ public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideT
|
||||
public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatchKt {
|
||||
public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarPatchKt {
|
||||
public static final fun getSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt {
|
||||
public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package app.revanced.patches.angulus.ads
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
// Keywords to search for in case the method name changes:
|
||||
// dailyMeasurementCount
|
||||
// lastMeasurementDate
|
||||
// dailyAdResetCount
|
||||
// MeasurementPrefs
|
||||
|
||||
// This fingerprint targets a method that returns the daily measurement count.
|
||||
// This method is used to determine if the user has reached the daily limit of measurements.
|
||||
internal val getDailyMeasurementCountFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE)
|
||||
returns("I")
|
||||
strings("dailyMeasurementCount")
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.angulus.ads
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val angulusPatch = bytecodePatch(name = "Hide ads") {
|
||||
compatibleWith("com.drinkplusplus.angulus")
|
||||
|
||||
execute {
|
||||
// Always return 0 as the daily measurement count.
|
||||
getDailyMeasurementCountFingerprint.method.returnEarly()
|
||||
}
|
||||
}
|
@ -3,23 +3,28 @@ package app.revanced.patches.googlephotos.misc.preferences
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Deprecated("This patch no longer works and this code will soon be deleted")
|
||||
@Suppress("unused")
|
||||
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
|
||||
name = "Restore hidden 'Back up while charging' toggle",
|
||||
description = "Restores a hidden toggle to only run backups when the device is charging.",
|
||||
description = "Restores a hidden toggle to only run backups when the device is charging."
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.photos")
|
||||
compatibleWith("com.google.android.apps.photos"("7.11.0.705590205"))
|
||||
|
||||
execute {
|
||||
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
|
||||
val chargingPrefStringIndex = backupPreferencesFingerprint.stringMatches!!.first().index
|
||||
backupPreferencesFingerprint.method.apply {
|
||||
// Get the register of move-result.
|
||||
val resultRegister = getInstruction<OneRegisterInstruction>(chargingPrefStringIndex + 2).registerA
|
||||
// Insert const after move-result to override register as true.
|
||||
addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1")
|
||||
backupPreferencesFingerprint.let {
|
||||
it.method.apply {
|
||||
val index = indexOfFirstInstructionOrThrow(
|
||||
it.stringMatches!!.first().index,
|
||||
Opcode.MOVE_RESULT
|
||||
)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
addInstruction(index + 1, "const/4 v$register, 0x1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package app.revanced.patches.messenger.navbar
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val createTabConfigurationFingerprint = fingerprint {
|
||||
strings("MessengerTabConfigurationCreator.createTabConfiguration")
|
||||
opcodes(
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
)
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.messenger.navbar
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val removeMetaAITabPatch = bytecodePatch(
|
||||
name = "Remove Meta AI tab",
|
||||
description = "Removes the 'Meta AI' tab from the navbar.",
|
||||
) {
|
||||
compatibleWith("com.facebook.orca")
|
||||
|
||||
execute {
|
||||
createTabConfigurationFingerprint.let {
|
||||
val moveResultIndex = it.patternMatch!!.startIndex + 1
|
||||
val enabledRegister = it.method.getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||
it.method.replaceInstruction(
|
||||
moveResultIndex,
|
||||
"const/4 v$enabledRegister, 0x0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ val getDeviceIdPatch = bytecodePatch(
|
||||
) {
|
||||
dependsOn(signatureDetectionPatch)
|
||||
|
||||
compatibleWith("com.microblink.photomath"("8.37.0"))
|
||||
compatibleWith("com.microblink.photomath")
|
||||
|
||||
execute {
|
||||
getDeviceIdFingerprint.method.replaceInstructions(
|
||||
|
@ -14,5 +14,5 @@ internal val checkSignatureFingerprint = fingerprint {
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT,
|
||||
)
|
||||
strings("signatures")
|
||||
}
|
||||
strings("SHA")
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ val hideUpdatePopupPatch = bytecodePatch(
|
||||
) {
|
||||
dependsOn(signatureDetectionPatch)
|
||||
|
||||
compatibleWith("com.microblink.photomath"("8.32.0"))
|
||||
compatibleWith("com.microblink.photomath")
|
||||
|
||||
execute {
|
||||
hideUpdatePopupFingerprint.method.addInstructions(
|
||||
|
@ -11,7 +11,7 @@ val unlockPlusPatch = bytecodePatch(
|
||||
) {
|
||||
dependsOn(signatureDetectionPatch, enableBookpointPatch)
|
||||
|
||||
compatibleWith("com.microblink.photomath"("8.37.0"))
|
||||
compatibleWith("com.microblink.photomath")
|
||||
|
||||
execute {
|
||||
isPlusUnlockedFingerprint.method.addInstructions(
|
||||
|
@ -0,0 +1,42 @@
|
||||
package app.revanced.patches.protonmail.signature
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.util.findElementByAttributeValue
|
||||
import java.io.File
|
||||
|
||||
@Suppress("unused")
|
||||
val removeSentFromSignaturePatch = resourcePatch(
|
||||
name = "Remove 'Sent from' signature",
|
||||
description = "Removes the 'Sent from Proton Mail mobile' signature from emails.",
|
||||
) {
|
||||
compatibleWith("ch.protonmail.android")
|
||||
|
||||
execute {
|
||||
val stringResourceFiles = mutableListOf<File>()
|
||||
|
||||
get("res").walk().forEach { file ->
|
||||
if (file.isFile && file.name.equals("strings.xml", ignoreCase = true)) {
|
||||
stringResourceFiles.add(file)
|
||||
}
|
||||
}
|
||||
|
||||
var foundString = false
|
||||
stringResourceFiles.forEach { filePath ->
|
||||
document(filePath.absolutePath).use { document ->
|
||||
var node = document.documentElement.childNodes.findElementByAttributeValue(
|
||||
"name",
|
||||
"mail_settings_identity_mobile_footer_default_free"
|
||||
)
|
||||
|
||||
// String is not localized in all languages.
|
||||
if (node != null) {
|
||||
node.textContent = ""
|
||||
foundString = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundString) throw PatchException("Could not find 'sent from' string in resources")
|
||||
}
|
||||
}
|
@ -41,11 +41,12 @@ fun sharedExtensionPatch(
|
||||
|
||||
execute {
|
||||
if (classes.none { EXTENSION_CLASS_DESCRIPTOR == it.type }) {
|
||||
throw PatchException(
|
||||
"Shared extension has not been merged yet. This patch can not succeed without merging it.",
|
||||
)
|
||||
throw PatchException("Shared extension is not available. This patch can not succeed without it.")
|
||||
}
|
||||
}
|
||||
|
||||
finalize {
|
||||
// The hooks are made in finalize to ensure that the context is hooked before any other patches.
|
||||
hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) }
|
||||
|
||||
// Modify Utils method to include the patches release version.
|
||||
@ -92,7 +93,7 @@ fun sharedExtensionPatch(
|
||||
}
|
||||
|
||||
class ExtensionHook internal constructor(
|
||||
private val fingerprint: Fingerprint,
|
||||
internal val fingerprint: Fingerprint,
|
||||
private val insertIndexResolver: ((Method) -> Int),
|
||||
private val contextRegisterResolver: (Method) -> String,
|
||||
) {
|
||||
@ -109,8 +110,14 @@ class ExtensionHook internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun extensionHook(
|
||||
insertIndexResolver: ((Method) -> Int) = { 0 },
|
||||
contextRegisterResolver: (Method) -> String = { "p0" },
|
||||
fingerprint: Fingerprint,
|
||||
) = ExtensionHook(fingerprint, insertIndexResolver, contextRegisterResolver)
|
||||
|
||||
fun extensionHook(
|
||||
insertIndexResolver: ((Method) -> Int) = { 0 },
|
||||
contextRegisterResolver: (Method) -> String = { "p0" },
|
||||
fingerprintBuilderBlock: FingerprintBuilder.() -> Unit,
|
||||
) = ExtensionHook(fingerprint(block = fingerprintBuilderBlock), insertIndexResolver, contextRegisterResolver)
|
||||
) = extensionHook(insertIndexResolver, contextRegisterResolver, fingerprint(block = fingerprintBuilderBlock))
|
||||
|
@ -17,7 +17,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
@ -110,19 +109,18 @@ fun gmsCoreSupportPatch(
|
||||
|
||||
// region Collection of transformations that are applied to all strings.
|
||||
|
||||
fun commonTransform(referencedString: String): String? =
|
||||
when (referencedString) {
|
||||
"com.google",
|
||||
"com.google.android.gms",
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES,
|
||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
||||
fun commonTransform(referencedString: String): String? = when (referencedString) {
|
||||
"com.google",
|
||||
"com.google.android.gms",
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES,
|
||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
||||
|
||||
// No vendor prefix for whatever reason...
|
||||
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
||||
else -> null
|
||||
}
|
||||
// No vendor prefix for whatever reason...
|
||||
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun contentUrisTransform(str: String): String? {
|
||||
// only when content:// uri
|
||||
@ -205,16 +203,8 @@ fun gmsCoreSupportPatch(
|
||||
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.method.apply {
|
||||
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
|
||||
val setContextIndex = indexOfFirstInstruction {
|
||||
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
||||
|
||||
reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V"
|
||||
}
|
||||
|
||||
// Add after setContext call, because this patch needs the context.
|
||||
addInstructions(
|
||||
if (setContextIndex < 0) 0 else setContextIndex + 1,
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" +
|
||||
"checkGmsCore(Landroid/app/Activity;)V",
|
||||
)
|
||||
|
@ -7,6 +7,8 @@ import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.getNode
|
||||
@ -36,14 +38,14 @@ fun settingsPatch (
|
||||
execute {
|
||||
copyResources(
|
||||
"settings",
|
||||
ResourceGroup("xml", "revanced_prefs.xml"),
|
||||
ResourceGroup("xml", "revanced_prefs.xml", "revanced_prefs_icons.xml"),
|
||||
)
|
||||
|
||||
addResources("shared", "misc.settings.settingsResourcePatch")
|
||||
}
|
||||
|
||||
finalize {
|
||||
fun Node.addPreference(preference: BasePreference, prepend: Boolean = false) {
|
||||
fun Node.addPreference(preference: BasePreference) {
|
||||
preference.serialize(ownerDocument) { resource ->
|
||||
// TODO: Currently, resources can only be added to "values", which may not be the correct place.
|
||||
// It may be necessary to ask for the desired resourceValue in the future.
|
||||
@ -61,7 +63,7 @@ fun settingsPatch (
|
||||
val preferenceFileName = "res/xml/$fileName.xml"
|
||||
if (get(preferenceFileName).exists()) {
|
||||
document(preferenceFileName).use { document ->
|
||||
document.getNode("PreferenceScreen").addPreference(intent, true)
|
||||
document.getNode("PreferenceScreen").addPreference(intent)
|
||||
}
|
||||
modified = true
|
||||
}
|
||||
@ -71,6 +73,30 @@ fun settingsPatch (
|
||||
}
|
||||
|
||||
// Add all preferences to the ReVanced fragment.
|
||||
document("res/xml/revanced_prefs_icons.xml").use { document ->
|
||||
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
|
||||
preferences.forEach { revancedPreferenceScreenNode.addPreference(it) }
|
||||
}
|
||||
|
||||
// Because the icon preferences require declaring a layout resource,
|
||||
// there is no easy way to change to the Android default preference layout
|
||||
// after the preference is inflated.
|
||||
// Using two different preference files is the simplest and most robust solution.
|
||||
fun removeIconsAndLayout(preferences: Collection<BasePreference>) {
|
||||
preferences.forEach { preference ->
|
||||
preference.icon = null
|
||||
preference.layout = null
|
||||
|
||||
if (preference is PreferenceCategory) {
|
||||
removeIconsAndLayout(preference.preferences)
|
||||
}
|
||||
if (preference is PreferenceScreenPreference) {
|
||||
removeIconsAndLayout(preference.preferences)
|
||||
}
|
||||
}
|
||||
}
|
||||
removeIconsAndLayout(preferences)
|
||||
|
||||
document("res/xml/revanced_prefs.xml").use { document ->
|
||||
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
|
||||
preferences.forEach { revancedPreferenceScreenNode.addPreference(it) }
|
||||
|
@ -19,10 +19,17 @@ abstract class BasePreference(
|
||||
val key: String? = null,
|
||||
val titleKey: String? = "${key}_title",
|
||||
val summaryKey: String? = "${key}_summary",
|
||||
val icon: String? = null,
|
||||
val layout: String? = null,
|
||||
icon: String? = null,
|
||||
layout: String? = null,
|
||||
val tag: String
|
||||
) {
|
||||
|
||||
var icon: String? = icon
|
||||
internal set
|
||||
|
||||
var layout: String? = layout
|
||||
internal set
|
||||
|
||||
/**
|
||||
* Serialize preference element to XML.
|
||||
* Overriding methods should invoke super and operate on its return value.
|
||||
|
@ -10,6 +10,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
@ -94,14 +95,14 @@ fun spoofVideoStreamsPatch(
|
||||
getReference<MethodReference>()?.name == "newUrlRequestBuilder"
|
||||
}
|
||||
val urlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
|
||||
val freeRegister = getInstruction<OneRegisterInstruction>(newRequestBuilderIndex + 1).registerA
|
||||
val freeRegister = findFreeRegister(newRequestBuilderIndex, urlRegister)
|
||||
|
||||
addInstructions(
|
||||
newRequestBuilderIndex,
|
||||
"""
|
||||
move-object v$freeRegister, p1
|
||||
invoke-static { v$urlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
||||
""",
|
||||
move-object v$freeRegister, p1
|
||||
invoke-static { v$urlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,82 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.*
|
||||
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.reference.FieldReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
|
||||
|
||||
internal val customThemeByteCodePatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
val backgroundColor by spotifyBackgroundColor
|
||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary
|
||||
|
||||
execute {
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
// Bytecode changes are not needed for legacy app target.
|
||||
// Player background color is changed with existing resource patch.
|
||||
return@execute
|
||||
}
|
||||
|
||||
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
|
||||
val index = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
const-string v$register, "$colorString"
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
|
||||
move-result-wide v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
val encoreColorsClassName = with(encoreThemeFingerprint) {
|
||||
// Find index of the first static get found after the string constant.
|
||||
val encoreColorsFieldReferenceIndex = originalMethod.indexOfFirstInstructionOrThrow(
|
||||
stringMatches!!.first().index,
|
||||
Opcode.SGET_OBJECT
|
||||
)
|
||||
|
||||
originalMethod.getInstruction(encoreColorsFieldReferenceIndex)
|
||||
.getReference<FieldReference>()!!.definingClass
|
||||
}
|
||||
|
||||
val encoreColorsConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
custom { method, classDef ->
|
||||
classDef.type == encoreColorsClassName &&
|
||||
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
|
||||
}
|
||||
}
|
||||
|
||||
encoreColorsConstructorFingerprint.method.apply {
|
||||
// Playlist song list background color.
|
||||
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
|
||||
|
||||
// Share menu background color.
|
||||
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
|
||||
homeCategoryPillColorsFingerprint.method.apply {
|
||||
// Home category pills background color.
|
||||
addColorChangeInstructions(HOME_CATEGORY_PILL_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
|
||||
settingsHeaderColorFingerprint.method.apply {
|
||||
// Settings header background color.
|
||||
addColorChangeInstructions(SETTINGS_HEADER_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +1,24 @@
|
||||
@file:Suppress("NAME_SHADOWING")
|
||||
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Suppress("unused")
|
||||
val customThemePatch = resourcePatch(
|
||||
name = "Custom theme",
|
||||
description = "Applies a custom theme.",
|
||||
description = "Applies a custom theme (defaults to amoled black)",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
val backgroundColor by stringOption(
|
||||
key = "backgroundColor",
|
||||
default = "@android:color/black",
|
||||
title = "Primary background color",
|
||||
description = "The background color. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
dependsOn(customThemeByteCodePatch)
|
||||
|
||||
val backgroundColorSecondary by stringOption(
|
||||
key = "backgroundColorSecondary",
|
||||
default = "#ff282828",
|
||||
title = "Secondary background color",
|
||||
description = "The secondary background color. (e.g. search box, artist & podcast). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
val accentColor by stringOption(
|
||||
key = "accentColor",
|
||||
default = "#ff1ed760",
|
||||
title = "Accent color",
|
||||
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
val accentColorPressed by stringOption(
|
||||
key = "accentColorPressed",
|
||||
default = "#ff169c46",
|
||||
title = "Pressed dark theme accent color",
|
||||
description =
|
||||
"The color when accented buttons are pressed, by default slightly darker than accent. " +
|
||||
"Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
val backgroundColor by spotifyBackgroundColor()
|
||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary()
|
||||
val accentColor by spotifyAccentColor()
|
||||
val accentColorPressed by spotifyAccentColorPressed()
|
||||
|
||||
execute {
|
||||
val backgroundColor = backgroundColor!!
|
||||
val backgroundColorSecondary = backgroundColorSecondary!!
|
||||
val accentColor = accentColor!!
|
||||
val accentColorPressed = accentColorPressed!!
|
||||
|
||||
document("res/values/colors.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
|
||||
@ -61,20 +26,37 @@ val customThemePatch = resourcePatch(
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i) as? Element ?: continue
|
||||
|
||||
node.textContent =
|
||||
when (node.getAttribute("name")) {
|
||||
"dark_base_background_elevated_base", "design_dark_default_color_background",
|
||||
"design_dark_default_color_surface", "gray_7", "gray_background", "gray_layer",
|
||||
"sthlm_blk",
|
||||
node.textContent = when (node.getAttribute("name")) {
|
||||
// Gradient next to user photo and "All" in home page
|
||||
"dark_base_background_base",
|
||||
// Main background
|
||||
"gray_7",
|
||||
// Left sidebar background in tablet mode
|
||||
"gray_10",
|
||||
// Add account, Settings and privacy, View Profile left sidebar background
|
||||
"dark_base_background_elevated_base",
|
||||
// Song/player background
|
||||
"bg_gradient_start_color", "bg_gradient_end_color",
|
||||
// Login screen
|
||||
"sthlm_blk", "sthlm_blk_grad_start", "stockholm_black",
|
||||
// Misc
|
||||
"image_placeholder_color",
|
||||
-> backgroundColor
|
||||
|
||||
"gray_15" -> backgroundColorSecondary
|
||||
// Track credits, merch in song player
|
||||
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
|
||||
// Playlist list background in home page
|
||||
"opacity_white_10",
|
||||
// About artist background in song player
|
||||
"gray_15",
|
||||
// What's New pills background
|
||||
"dark_base_background_tinted_highlight"
|
||||
-> backgroundColorSecondary
|
||||
|
||||
"dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor
|
||||
|
||||
"dark_brightaccent_background_press" -> accentColorPressed
|
||||
else -> continue
|
||||
}
|
||||
"dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor
|
||||
"dark_brightaccent_background_press" -> accentColorPressed
|
||||
else -> continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val encoreThemeFingerprint = fingerprint {
|
||||
strings("Encore theme was not provided.") // Partial string match.
|
||||
}
|
||||
|
||||
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
|
||||
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
|
||||
internal const val PLAYLIST_BACKGROUND_COLOR_LITERAL = 0xFF121212
|
||||
internal const val SHARE_MENU_BACKGROUND_COLOR_LITERAL = 0xFF1F1F1F
|
||||
|
||||
internal val homeCategoryPillColorsFingerprint = fingerprint{
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
custom { method, _ ->
|
||||
method.containsLiteralInstruction(HOME_CATEGORY_PILL_COLOR_LITERAL) &&
|
||||
method.containsLiteralInstruction(0x33000000)
|
||||
}
|
||||
}
|
||||
|
||||
internal val settingsHeaderColorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
custom { method, _ ->
|
||||
method.containsLiteralInstruction(SETTINGS_HEADER_COLOR_LITERAL) &&
|
||||
method.containsLiteralInstruction(0)
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
|
||||
internal val spotifyBackgroundColor = stringOption(
|
||||
key = "backgroundColor",
|
||||
default = "@android:color/black",
|
||||
title = "Primary background color",
|
||||
description = "The background color. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyBackgroundColorSecondary = stringOption(
|
||||
key = "backgroundColorSecondary",
|
||||
default = "#FF121212",
|
||||
title = "Secondary background color",
|
||||
description = "The secondary background color. (e.g. playlist list, player arist, credits). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyAccentColor = stringOption(
|
||||
key = "accentColor",
|
||||
default = "#FF1ED760",
|
||||
title = "Accent color",
|
||||
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyAccentColorPressed = stringOption(
|
||||
key = "accentColorPressed",
|
||||
default = "#FF169C46",
|
||||
title = "Pressed dark theme accent color",
|
||||
description =
|
||||
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
@ -1,15 +1,28 @@
|
||||
package app.revanced.patches.spotify.misc
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val accountAttributeFingerprint = fingerprint {
|
||||
custom { _, c -> c.endsWith("internal/AccountAttribute;") }
|
||||
custom { _, classDef ->
|
||||
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
"Lcom/spotify/useraccount/v1/AccountAttribute;"
|
||||
} else {
|
||||
"Lcom/spotify/remoteconfig/internal/AccountAttribute;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val productStateProtoFingerprint = fingerprint {
|
||||
returns("Ljava/util/Map;")
|
||||
custom { _, classDef ->
|
||||
classDef.endsWith("ProductStateProto;")
|
||||
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
"Lcom/spotify/ucs/proto/v0/UcsResponseWrapper${'$'}AccountAttributesResponse;"
|
||||
} else {
|
||||
"Lcom/spotify/remoteconfig/internal/ProductStateProto;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,3 +34,38 @@ internal val contextMenuExperimentsFingerprint = fingerprint {
|
||||
parameters("L")
|
||||
strings("remove_ads_upsell_enabled")
|
||||
}
|
||||
|
||||
internal val contextFromJsonFingerprint = fingerprint {
|
||||
opcodes(
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_STATIC
|
||||
)
|
||||
custom { methodDef, classDef ->
|
||||
methodDef.name == "fromJson" &&
|
||||
classDef.endsWith("voiceassistants/playermodels/ContextJsonAdapter;")
|
||||
}
|
||||
}
|
||||
|
||||
internal val readPlayerOptionOverridesFingerprint = fingerprint {
|
||||
custom { methodDef, classDef ->
|
||||
methodDef.name == "readPlayerOptionOverrides" &&
|
||||
classDef.endsWith("voiceassistants/playermodels/PreparePlayOptionsJsonAdapter;")
|
||||
}
|
||||
}
|
||||
|
||||
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;") }
|
||||
}
|
||||
|
@ -1,12 +1,26 @@
|
||||
package app.revanced.patches.spotify.misc
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.spotify.misc.check.checkEnvironmentPatch
|
||||
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 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.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import java.util.logging.Logger
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
|
||||
|
||||
@ -17,7 +31,12 @@ val unlockPremiumPatch = bytecodePatch(
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
dependsOn(sharedExtensionPatch)
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
// Currently there is no easy way to make a mandatory patch,
|
||||
// so for now this is a dependent of this patch.
|
||||
checkEnvironmentPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
// Make _value accessible so that it can be overridden in the extension.
|
||||
@ -27,23 +46,121 @@ val unlockPremiumPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// Override the attributes map in the getter method.
|
||||
val attributesMapRegister = 0
|
||||
val instantiateUnmodifiableMapIndex = 1
|
||||
productStateProtoFingerprint.method.addInstruction(
|
||||
instantiateUnmodifiableMapIndex,
|
||||
"invoke-static { v$attributesMapRegister }," +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->overrideAttribute(Ljava/util/Map;)V",
|
||||
)
|
||||
with(productStateProtoFingerprint.method) {
|
||||
val getAttributesMapIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
||||
val attributesMapRegister = getInstruction<TwoRegisterInstruction>(getAttributesMapIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
getAttributesMapIndex + 1,
|
||||
"invoke-static { v$attributesMapRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->overrideAttribute(Ljava/util/Map;)V"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Add the query parameter trackRows to show popular tracks in the artist page.
|
||||
val addQueryParameterIndex = buildQueryParametersFingerprint.stringMatches!!.first().index - 1
|
||||
buildQueryParametersFingerprint.method.replaceInstruction(addQueryParameterIndex, "nop")
|
||||
with(buildQueryParametersFingerprint) {
|
||||
val addQueryParameterConditionIndex = method.indexOfFirstInstructionReversedOrThrow(
|
||||
stringMatches!!.first().index, Opcode.IF_EQZ
|
||||
)
|
||||
method.replaceInstruction(addQueryParameterConditionIndex, "nop")
|
||||
}
|
||||
|
||||
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
return@execute Logger.getLogger(this::class.java.name).warning(
|
||||
"Patching a legacy Spotify version. Patch functionality may be limited."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Enable choosing a specific song/artist via Google Assistant.
|
||||
contextFromJsonFingerprint.method.apply {
|
||||
val insertIndex = contextFromJsonFingerprint.patternMatch!!.startIndex
|
||||
// Both the URI and URL need to be modified.
|
||||
val registerUrl = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
|
||||
val registerUri = getInstruction<FiveRegisterInstruction>(insertIndex + 2).registerD
|
||||
|
||||
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"removeStationString(Ljava/lang/String;)Ljava/lang/String;"
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$registerUrl }, $extensionMethodDescriptor
|
||||
move-result-object v$registerUrl
|
||||
invoke-static { v$registerUri }, $extensionMethodDescriptor
|
||||
move-result-object v$registerUri
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Disable forced shuffle when asking for an album/playlist via Google Assistant.
|
||||
readPlayerOptionOverridesFingerprint.method.apply {
|
||||
val shufflingContextCallIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.name == "shufflingContext"
|
||||
}
|
||||
|
||||
val registerBool = getInstruction<FiveRegisterInstruction>(shufflingContextCallIndex).registerD
|
||||
addInstruction(
|
||||
shufflingContextCallIndex,
|
||||
"sget-object v$registerBool, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Disable the "Spotify Premium" upsell experiment in context menus.
|
||||
with(contextMenuExperimentsFingerprint) {
|
||||
val moveIsEnabledIndex = stringMatches!!.first().index + 2
|
||||
val moveIsEnabledIndex = method.indexOfFirstInstructionOrThrow(
|
||||
stringMatches!!.first().index, Opcode.MOVE_RESULT
|
||||
)
|
||||
val isUpsellEnabledRegister = method.getInstruction<OneRegisterInstruction>(moveIsEnabledIndex).registerA
|
||||
method.replaceInstruction(moveIsEnabledIndex, "const/4 v$isUpsellEnabledRegister, 0")
|
||||
}
|
||||
|
||||
|
||||
// Make featureTypeCase_ accessible so we can check the home section type in the extension.
|
||||
homeSectionFingerprint.classDef.fields.first { it.name == "featureTypeCase_" }.apply {
|
||||
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
|
||||
}
|
||||
|
||||
val protobufListClassName = with(protobufListsFingerprint.originalMethod) {
|
||||
val emptyProtobufListGetIndex = indexOfFirstInstructionOrThrow(Opcode.SGET_OBJECT)
|
||||
getInstruction(emptyProtobufListGetIndex).getReference<FieldReference>()!!.definingClass
|
||||
}
|
||||
|
||||
val protobufListRemoveFingerprint = fingerprint {
|
||||
custom { method, classDef ->
|
||||
method.name == "remove" && classDef.type == protobufListClassName
|
||||
}
|
||||
}
|
||||
|
||||
// Need to allow mutation of the list so the home ads sections can be removed.
|
||||
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability.
|
||||
// Forcing that always on breaks unrelated code in strange ways.
|
||||
// Instead, remove the method call that checks if the list is unmodifiable.
|
||||
with(protobufListRemoveFingerprint.method) {
|
||||
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
reference?.returnType == "V" && reference.parameterTypes.isEmpty()
|
||||
}
|
||||
|
||||
// Remove the method call that throws an exception if the list is not mutable.
|
||||
removeInstruction(invokeThrowUnmodifiableIndex)
|
||||
}
|
||||
|
||||
// Remove ads sections from home.
|
||||
with(homeStructureFingerprint.method) {
|
||||
val getSectionsIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
||||
val sectionsRegister = getInstruction<TwoRegisterInstruction>(getSectionsIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
getSectionsIndex + 1,
|
||||
"invoke-static { v$sectionsRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->removeHomeSections(Ljava/util/List;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package app.revanced.patches.spotify.misc.check
|
||||
|
||||
import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch
|
||||
import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
|
||||
internal val checkEnvironmentPatch = checkEnvironmentPatch(
|
||||
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
|
||||
extensionPatch = sharedExtensionPatch,
|
||||
"com.spotify.music",
|
||||
)
|
@ -1,5 +1,21 @@
|
||||
package app.revanced.patches.spotify.misc.extension
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.spotify.shared.SPOTIFY_MAIN_ACTIVITY_LEGACY
|
||||
|
||||
val sharedExtensionPatch = sharedExtensionPatch("spotify", spotifyMainActivityOnCreate)
|
||||
/**
|
||||
* If patching a legacy 8.x target. This may also be set if patching slightly older/newer app targets,
|
||||
* but the only legacy target of interest is 8.6.98.900 as it's the last version that
|
||||
* supports Spotify integration on Kenwood/Pioneer car stereos.
|
||||
*/
|
||||
internal var IS_SPOTIFY_LEGACY_APP_TARGET = false
|
||||
|
||||
val sharedExtensionPatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch("spotify", mainActivityOnCreateHook))
|
||||
|
||||
execute {
|
||||
IS_SPOTIFY_LEGACY_APP_TARGET = mainActivityOnCreateHook.fingerprint
|
||||
.originalClassDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
package app.revanced.patches.spotify.misc.extension
|
||||
|
||||
import app.revanced.patches.shared.misc.extension.extensionHook
|
||||
import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint
|
||||
|
||||
internal val spotifyMainActivityOnCreate = extensionHook {
|
||||
custom { method, classDef ->
|
||||
classDef.type == "Lcom/spotify/music/SpotifyMainActivity;" &&
|
||||
method.name == "onCreate"
|
||||
}
|
||||
}
|
||||
internal val mainActivityOnCreateHook = extensionHook(fingerprint = mainActivityOnCreateFingerprint)
|
||||
|
@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.spotify.shared
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
private const val SPOTIFY_MAIN_ACTIVITY = "Lcom/spotify/music/SpotifyMainActivity;"
|
||||
|
||||
/**
|
||||
* Main activity of target 8.6.98.900.
|
||||
*/
|
||||
internal const val SPOTIFY_MAIN_ACTIVITY_LEGACY = "Lcom/spotify/music/MainActivity;"
|
||||
|
||||
internal val mainActivityOnCreateFingerprint = fingerprint {
|
||||
custom { method, classDef ->
|
||||
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|
||||
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
val disableSubscriptionSuggestionsPatch = bytecodePatch(
|
||||
name = "Disable subscription suggestions",
|
||||
) {
|
||||
compatibleWith("com.strava"("320.12"))
|
||||
compatibleWith("com.strava")
|
||||
|
||||
execute {
|
||||
val helperMethodName = "getModulesIfNotUpselling"
|
||||
|
@ -11,8 +11,10 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/DisablePreciseSeekingGesturePatch;"
|
||||
|
||||
val disablePreciseSeekingGesturePatch = bytecodePatch(
|
||||
name = "Disable precise seeking gesture",
|
||||
description = "Adds an option to disable precise seeking when swiping up on the seekbar.",
|
||||
) {
|
||||
dependsOn(
|
||||
@ -21,25 +23,12 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "interaction.seekbar.disablePreciseSeekingGesturePatch")
|
||||
|
||||
PreferenceScreen.SEEKBAR.addPreferences(
|
||||
SwitchPreference("revanced_disable_precise_seeking_gesture"),
|
||||
)
|
||||
val extensionMethodDescriptor =
|
||||
"Lapp/revanced/extension/youtube/patches/DisablePreciseSeekingGesturePatch;"
|
||||
|
||||
allowSwipingUpGestureFingerprint.match(
|
||||
swipingUpGestureParentFingerprint.originalClassDef,
|
||||
@ -47,7 +36,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $extensionMethodDescriptor->isGestureDisabled()Z
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isGestureDisabled()Z
|
||||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
return-void
|
||||
@ -62,7 +51,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $extensionMethodDescriptor->isGestureDisabled()Z
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isGestureDisabled()Z
|
||||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
const/4 v0, 0x0
|
||||
|
@ -15,7 +15,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
val enableSeekbarTappingPatch = bytecodePatch(
|
||||
name = "Enable tap to seek",
|
||||
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
|
||||
) {
|
||||
dependsOn(
|
||||
@ -24,17 +23,6 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "interaction.seekbar.enableSeekbarTappingPatch")
|
||||
|
||||
|
@ -18,11 +18,9 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal const val EXTENSION_METHOD_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/SlideToSeekPatch;->isSlideToSeekDisabled(Z)Z"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SlideToSeekPatch;"
|
||||
|
||||
val enableSlideToSeekPatch = bytecodePatch(
|
||||
name = "Enable slide to seek",
|
||||
description = "Adds an option to enable slide to seek " +
|
||||
"instead of playing at 2x speed when pressing and holding in the video player."
|
||||
) {
|
||||
@ -33,17 +31,6 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "interaction.seekbar.enableSlideToSeekPatch")
|
||||
|
||||
@ -59,6 +46,8 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
val checkReference = slideToSeekFingerprint.method.getInstruction(checkIndex)
|
||||
.getReference<MethodReference>()!!
|
||||
|
||||
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->isSlideToSeekDisabled(Z)Z"
|
||||
|
||||
// A/B check method was only called on this class.
|
||||
slideToSeekFingerprint.classDef.methods.forEach { method ->
|
||||
method.findInstructionIndicesReversed {
|
||||
@ -70,7 +59,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
addInstructions(
|
||||
index + 2,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_METHOD_DESCRIPTOR
|
||||
invoke-static { v$register }, $extensionMethodDescriptor
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
@ -95,7 +84,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static { v$targetRegister }, $EXTENSION_METHOD_DESCRIPTOR
|
||||
invoke-static { v$targetRegister }, $extensionMethodDescriptor
|
||||
move-result v$targetRegister
|
||||
""",
|
||||
)
|
||||
@ -109,7 +98,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$targetRegister }, $EXTENSION_METHOD_DESCRIPTOR
|
||||
invoke-static { v$targetRegister }, $extensionMethodDescriptor
|
||||
move-result v$targetRegister
|
||||
""",
|
||||
)
|
||||
|
@ -0,0 +1,46 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
|
||||
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.patches.youtube.shared.seekbarFingerprint
|
||||
import app.revanced.patches.youtube.shared.seekbarOnDrawFingerprint
|
||||
|
||||
val hideSeekbarPatch = bytecodePatch(
|
||||
description = "Adds an option to hide the seekbar.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
seekbarColorPatch,
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "layout.hide.seekbar.hideSeekbarPatch")
|
||||
|
||||
PreferenceScreen.SEEKBAR.addPreferences(
|
||||
SwitchPreference("revanced_hide_seekbar"),
|
||||
SwitchPreference("revanced_hide_seekbar_thumbnail"),
|
||||
)
|
||||
|
||||
seekbarOnDrawFingerprint.match(seekbarFingerprint.originalClassDef).method.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x0
|
||||
invoke-static { }, Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;->hideSeekbar()Z
|
||||
move-result v0
|
||||
if-eqz v0, :hide_seekbar
|
||||
return-void
|
||||
:hide_seekbar
|
||||
nop
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
|
||||
@Suppress("unused")
|
||||
val seekbarPatch = bytecodePatch(
|
||||
name = "Seekbar",
|
||||
description = "Adds options to disable precise seeking when swiping up on the seekbar, " +
|
||||
"slide to seek instead of playing at 2x speed when pressing and holding, " +
|
||||
"tapping the player seekbar to seek, " +
|
||||
"and hiding the video player seekbar."
|
||||
) {
|
||||
dependsOn(
|
||||
disablePreciseSeekingGesturePatch,
|
||||
enableSlideToSeekPatch,
|
||||
enableSeekbarTappingPatch,
|
||||
hideSeekbarPatch,
|
||||
seekbarThumbnailsPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
)
|
||||
)
|
||||
}
|
@ -10,6 +10,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.layout.seekbar.fullscreenSeekbarThumbnailsFingerprint
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_17_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
|
||||
|
||||
@ -17,7 +18,6 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/SeekbarThumbnailsPatch;"
|
||||
|
||||
val seekbarThumbnailsPatch = bytecodePatch(
|
||||
name = "Seekbar thumbnails",
|
||||
description = "Adds an option to use high quality fullscreen seekbar thumbnails. " +
|
||||
"Patching 19.16.39 adds an option to restore old seekbar thumbnails.",
|
||||
) {
|
||||
@ -27,18 +27,13 @@ val seekbarThumbnailsPatch = bytecodePatch(
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
if (is_20_09_or_greater) {
|
||||
// High quality seekbar thumbnails is partially broken in 20.09
|
||||
// and the code is completely removed in 20.10+
|
||||
return@execute
|
||||
}
|
||||
|
||||
addResources("youtube", "layout.seekbar.seekbarThumbnailsPatch")
|
||||
|
||||
if (is_19_17_or_greater) {
|
||||
|
@ -20,11 +20,10 @@ import app.revanced.patches.shared.misc.settings.preference.*
|
||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_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.findFreeRegister
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
@ -123,7 +122,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
addResourcesPatch,
|
||||
hideLayoutComponentsResourcePatch,
|
||||
navigationBarHookPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
@ -132,8 +130,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
@ -254,17 +250,16 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
(if (is_20_07_or_greater) parseElementFromBufferFingerprint
|
||||
else parseElementFromBufferLegacyFingerprint).let {
|
||||
it.method.apply {
|
||||
// Target code is a mess with a lot of register moves.
|
||||
// There is no simple way to find a free register for all versions so this is hard coded.
|
||||
val freeRegister = if (is_19_47_or_greater) 6 else 0
|
||||
val byteArrayParameter = "p3"
|
||||
val startIndex = it.patternMatch!!.startIndex
|
||||
val conversionContextRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
val returnEmptyComponentRegister = (returnEmptyComponentInstruction as FiveRegisterInstruction).registerC
|
||||
val insertIndex = startIndex + 1
|
||||
val freeRegister = findFreeRegister(insertIndex, conversionContextRegister, returnEmptyComponentRegister)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
startIndex + 1,
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
|
||||
move-result v$freeRegister
|
||||
@ -272,7 +267,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
move-object v$returnEmptyComponentRegister, p1 # Required for 19.47
|
||||
goto :return_empty_component
|
||||
:show
|
||||
const/4 v$freeRegister, 0x0 # Restore register, required for 19.16
|
||||
nop
|
||||
""",
|
||||
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
|
||||
)
|
||||
|
@ -51,7 +51,10 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
|
||||
SwitchPreference("revanced_hide_player_flyout_speed"),
|
||||
SwitchPreference("revanced_hide_player_flyout_lock_screen"),
|
||||
SwitchPreference("revanced_hide_player_flyout_more_info"),
|
||||
SwitchPreference("revanced_hide_player_flyout_audio_track"),
|
||||
SwitchPreference(
|
||||
key = "revanced_hide_player_flyout_audio_track",
|
||||
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference"
|
||||
),
|
||||
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
|
||||
SwitchPreference("revanced_hide_player_flyout_sleep_timer"),
|
||||
SwitchPreference("revanced_hide_player_flyout_video_quality_footer"),
|
||||
|
@ -1,58 +1,11 @@
|
||||
package app.revanced.patches.youtube.layout.hide.seekbar
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
|
||||
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.patches.youtube.shared.seekbarFingerprint
|
||||
import app.revanced.patches.youtube.shared.seekbarOnDrawFingerprint
|
||||
import app.revanced.patches.youtube.interaction.seekbar.hideSeekbarPatch
|
||||
|
||||
val hideSeekbarPatch = bytecodePatch(
|
||||
name = "Hide seekbar",
|
||||
description = "Adds an option to hide the seekbar.",
|
||||
) {
|
||||
@Deprecated("Patch was moved to app.revanced.patches.youtube.interaction.seekbar")
|
||||
val hideSeekbarPatch = bytecodePatch {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
seekbarColorPatch,
|
||||
addResourcesPatch,
|
||||
hideSeekbarPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "layout.hide.seekbar.hideSeekbarPatch")
|
||||
|
||||
PreferenceScreen.SEEKBAR.addPreferences(
|
||||
SwitchPreference("revanced_hide_seekbar"),
|
||||
SwitchPreference("revanced_hide_seekbar_thumbnail"),
|
||||
)
|
||||
|
||||
seekbarOnDrawFingerprint.match(seekbarFingerprint.originalClassDef).method.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x0
|
||||
invoke-static { }, Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;->hideSeekbar()Z
|
||||
move-result v0
|
||||
if-eqz v0, :hide_seekbar
|
||||
return-void
|
||||
:hide_seekbar
|
||||
nop
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -168,11 +168,8 @@ val miniplayerPatch = bytecodePatch(
|
||||
// 19.30.39 // Modern 3 is less broken when double tap expand is enabled, but cannot swipe to expand when double tap is off.
|
||||
// 19.31.36 // All Modern 1 buttons are missing. Unusable.
|
||||
// 19.32.36 // 19.32+ and beyond all work without issues.
|
||||
// 19.33.35
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
|
@ -25,7 +25,6 @@ val openVideosFullscreenPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
)
|
||||
|
@ -70,6 +70,8 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
key = "revanced_settings_screen_09",
|
||||
titleKey = "revanced_ryd_settings_title",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_09_ryd",
|
||||
layout = "@layout/preference_with_icon",
|
||||
intent = newIntent("revanced_ryd_settings_intent"),
|
||||
),
|
||||
)
|
||||
@ -179,9 +181,6 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
|
||||
// region Hook rolling numbers.
|
||||
|
||||
// Do this last to allow patching old unsupported versions (if the user really wants),
|
||||
// On older unsupported version this will fail to match and throw an exception,
|
||||
// but everything will still work correctly anyway.
|
||||
val dislikesIndex = rollingNumberSetterFingerprint.patternMatch!!.endIndex
|
||||
|
||||
rollingNumberSetterFingerprint.method.apply {
|
||||
|
@ -84,31 +84,14 @@ internal val playerLinearGradientFingerprint = fingerprint {
|
||||
}
|
||||
|
||||
/**
|
||||
* 19.46 - 19.47
|
||||
* 19.25 - 19.47
|
||||
*/
|
||||
internal val playerLinearGradientLegacy1946Fingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameters("I", "I", "I", "I")
|
||||
internal val playerLinearGradientLegacyFingerprint = fingerprint {
|
||||
returns("V")
|
||||
opcodes(
|
||||
Opcode.FILLED_NEW_ARRAY,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
)
|
||||
custom { method, _ ->
|
||||
method.name == "setBounds" && method.containsLiteralInstruction(ytYoutubeMagentaColorId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 19.25 - 19.45
|
||||
*/
|
||||
internal val playerLinearGradientLegacy1925Fingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters("Landroid/content/Context;")
|
||||
opcodes(
|
||||
Opcode.FILLED_NEW_ARRAY,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
)
|
||||
literal { ytYoutubeMagentaColorId }
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.youtube.layout.seekbar
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
@ -310,14 +311,15 @@ val seekbarColorPatch = bytecodePatch(
|
||||
"""
|
||||
)
|
||||
|
||||
val playerFingerprint =
|
||||
if (is_19_49_or_greater) {
|
||||
playerLinearGradientFingerprint
|
||||
} else if (is_19_46_or_greater) {
|
||||
playerLinearGradientLegacy1946Fingerprint
|
||||
} else {
|
||||
playerLinearGradientLegacy1925Fingerprint
|
||||
}
|
||||
val playerFingerprint: Fingerprint
|
||||
val checkGradientCoordinates: Boolean
|
||||
if (is_19_49_or_greater) {
|
||||
playerFingerprint = playerLinearGradientFingerprint
|
||||
checkGradientCoordinates = true
|
||||
} else {
|
||||
playerFingerprint = playerLinearGradientLegacyFingerprint
|
||||
checkGradientCoordinates = false
|
||||
}
|
||||
|
||||
playerFingerprint.let {
|
||||
it.method.apply {
|
||||
@ -326,10 +328,17 @@ val seekbarColorPatch = bytecodePatch(
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerLinearGradient([I)[I
|
||||
move-result-object v$register
|
||||
"""
|
||||
if (checkGradientCoordinates) {
|
||||
"""
|
||||
invoke-static { v$register, p0, p1 }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerLinearGradient([III)[I
|
||||
move-result-object v$register
|
||||
"""
|
||||
} else {
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerLinearGradient([I)[I
|
||||
move-result-object v$register
|
||||
"""
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,9 @@ 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.findFreeRegister
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@ -106,11 +106,12 @@ val openShortsInRegularPlayerPatch = bytecodePatch(
|
||||
getReference<MethodReference>()?.returnType ==
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
|
||||
}
|
||||
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
|
||||
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
val insertIndex = index + 2
|
||||
val freeRegister = findFreeRegister(insertIndex, playbackStartRegister)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
index + 2,
|
||||
insertIndex,
|
||||
extensionInstructions(playbackStartRegister, freeRegister)
|
||||
)
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ private val sponsorBlockResourcePatch = resourcePatch {
|
||||
key = "revanced_settings_screen_10",
|
||||
titleKey = "revanced_sb_settings_title",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_10_sb",
|
||||
layout = "@layout/preference_with_icon",
|
||||
intent = newIntent("revanced_sb_settings_intent"),
|
||||
),
|
||||
)
|
||||
|
@ -1,9 +1,7 @@
|
||||
package app.revanced.patches.youtube.layout.startupshortsreset
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
@ -12,11 +10,12 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_02_or_greater
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@ -39,8 +38,6 @@ val disableResumingShortsOnStartupPatch = bytecodePatch(
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
@ -80,23 +77,19 @@ val disableResumingShortsOnStartupPatch = bytecodePatch(
|
||||
reference?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" &&
|
||||
reference.name == "isDone"
|
||||
}
|
||||
val originalInstructionRegister =
|
||||
getInstruction<FiveRegisterInstruction>(listenableInstructionIndex).registerC
|
||||
val freeRegister =
|
||||
getInstruction<OneRegisterInstruction>(listenableInstructionIndex + 1).registerA
|
||||
val freeRegister = findFreeRegister(listenableInstructionIndex)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
listenableInstructionIndex + 1,
|
||||
addInstructionsAtControlFlowLabel(
|
||||
listenableInstructionIndex,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :show
|
||||
return-void
|
||||
:show
|
||||
invoke-interface {v$originalInstructionRegister}, Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z
|
||||
nop
|
||||
"""
|
||||
)
|
||||
removeInstruction(listenableInstructionIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
@ -42,13 +43,14 @@ val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{
|
||||
}
|
||||
|
||||
playbackSpeedInFeedsFingerprint.method.apply {
|
||||
val freeRegister = implementation!!.registerCount - parameters.size - 2
|
||||
val playbackSpeedIndex = indexOfGetPlaybackSpeedInstruction(this)
|
||||
val playbackSpeedRegister = getInstruction<TwoRegisterInstruction>(playbackSpeedIndex).registerA
|
||||
val returnIndex = indexOfFirstInstructionOrThrow(playbackSpeedIndex, Opcode.RETURN_VOID)
|
||||
val insertIndex = playbackSpeedIndex + 1
|
||||
val freeRegister = findFreeRegister(insertIndex, playbackSpeedRegister)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
playbackSpeedIndex + 1,
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$playbackSpeedRegister }, $EXTENSION_CLASS_DESCRIPTOR->playbackSpeedChanged(F)Z
|
||||
move-result v$freeRegister
|
||||
|
@ -36,8 +36,6 @@ val bypassURLRedirectsPatch = bytecodePatch(
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
|
@ -8,7 +8,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
|
||||
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.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
@ -16,12 +15,14 @@ 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.util.findFreeRegister
|
||||
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.*
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@ -191,17 +192,7 @@ val lithoFilterPatch = bytecodePatch(
|
||||
},
|
||||
).registerA
|
||||
|
||||
// Find a free temporary register.
|
||||
val freeRegister = getInstruction<OneRegisterInstruction>(
|
||||
// Immediately before is a StringBuilder append constant character.
|
||||
indexOfFirstInstructionReversedOrThrow(insertHookIndex, Opcode.CONST_16),
|
||||
).registerA
|
||||
|
||||
// Verify the temp register will not clobber the method result register.
|
||||
if (stringBuilderRegister == freeRegister) {
|
||||
throw PatchException("Free register will clobber StringBuilder register")
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -73,7 +73,22 @@ private val settingsResourcePatch = resourcePatch {
|
||||
appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"]
|
||||
|
||||
arrayOf(
|
||||
ResourceGroup("drawable", "revanced_settings_icon.xml"),
|
||||
ResourceGroup("drawable",
|
||||
"revanced_settings_icon.xml",
|
||||
"revanced_settings_screen_00_about.xml",
|
||||
"revanced_settings_screen_01_ads.xml",
|
||||
"revanced_settings_screen_02_alt_thumbnails.xml",
|
||||
"revanced_settings_screen_03_feed.xml",
|
||||
"revanced_settings_screen_04_general.xml",
|
||||
"revanced_settings_screen_05_player.xml",
|
||||
"revanced_settings_screen_06_shorts.xml",
|
||||
"revanced_settings_screen_07_seekbar.xml",
|
||||
"revanced_settings_screen_08_swipe_controls.xml",
|
||||
"revanced_settings_screen_09_ryd.xml",
|
||||
"revanced_settings_screen_10_sb.xml",
|
||||
"revanced_settings_screen_11_misc.xml",
|
||||
"revanced_settings_screen_12_video.xml",
|
||||
),
|
||||
ResourceGroup("layout", "revanced_settings_with_toolbar.xml"),
|
||||
).forEach { resourceGroup ->
|
||||
copyResources("settings", resourceGroup)
|
||||
@ -159,6 +174,8 @@ val settingsPatch = bytecodePatch(
|
||||
// Add an "about" preference to the top.
|
||||
preferences += NonInteractivePreference(
|
||||
key = "revanced_settings_screen_00_about",
|
||||
icon = "@drawable/revanced_settings_screen_00_about",
|
||||
layout = "@layout/preference_with_icon",
|
||||
summaryKey = null,
|
||||
tag = "app.revanced.extension.youtube.settings.preference.ReVancedYouTubeAboutPreference",
|
||||
selectable = true,
|
||||
@ -170,6 +187,10 @@ val settingsPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
SwitchPreference("revanced_show_menu_icons")
|
||||
)
|
||||
|
||||
PreferenceScreen.MISC.addPreferences(
|
||||
TextPreference(
|
||||
key = null,
|
||||
@ -277,37 +298,53 @@ object PreferenceScreen : BasePreferenceScreen() {
|
||||
val ADS = Screen(
|
||||
key = "revanced_settings_screen_01_ads",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_01_ads",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
val ALTERNATIVE_THUMBNAILS = Screen(
|
||||
key = "revanced_settings_screen_02_alt_thumbnails",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_02_alt_thumbnails",
|
||||
layout = "@layout/preference_with_icon",
|
||||
sorting = Sorting.UNSORTED,
|
||||
)
|
||||
val FEED = Screen(
|
||||
key = "revanced_settings_screen_03_feed",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_03_feed",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
val GENERAL_LAYOUT = Screen(
|
||||
key = "revanced_settings_screen_04_general",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_04_general",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
val PLAYER = Screen(
|
||||
key = "revanced_settings_screen_05_player",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_05_player",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
|
||||
val SHORTS = Screen(
|
||||
key = "revanced_settings_screen_06_shorts",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_06_shorts",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
|
||||
val SEEKBAR = Screen(
|
||||
key = "revanced_settings_screen_07_seekbar",
|
||||
summaryKey = null,
|
||||
)
|
||||
icon = "@drawable/revanced_settings_screen_07_seekbar",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
val SWIPE_CONTROLS = Screen(
|
||||
key = "revanced_settings_screen_08_swipe_controls",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_08_swipe_controls",
|
||||
layout = "@layout/preference_with_icon",
|
||||
sorting = Sorting.UNSORTED,
|
||||
)
|
||||
|
||||
@ -317,10 +354,14 @@ object PreferenceScreen : BasePreferenceScreen() {
|
||||
val MISC = Screen(
|
||||
key = "revanced_settings_screen_11_misc",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_11_misc",
|
||||
layout = "@layout/preference_with_icon",
|
||||
)
|
||||
val VIDEO = Screen(
|
||||
key = "revanced_settings_screen_12_video",
|
||||
summaryKey = null,
|
||||
icon = "@drawable/revanced_settings_screen_12_video",
|
||||
layout = "@layout/preference_with_icon",
|
||||
sorting = Sorting.BY_KEY,
|
||||
)
|
||||
|
||||
|
@ -15,13 +15,185 @@ 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 com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.Opcode.*
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import java.util.EnumSet
|
||||
|
||||
/**
|
||||
* Starting from and including the instruction at index [startIndex],
|
||||
* finds the next register that is wrote to and not read from. If a return instruction
|
||||
* is encountered, then the lowest unused register is returned.
|
||||
*
|
||||
* This method can return a non 4-bit register, and the calling code may need to temporarily
|
||||
* swap register contents if a 4-bit register is required.
|
||||
*
|
||||
* @param startIndex Inclusive starting index.
|
||||
* @param registersToExclude Registers to exclude, and consider as used. For most use cases,
|
||||
* all registers used in injected code should be specified.
|
||||
* @throws IllegalArgumentException If a branch or conditional statement is encountered
|
||||
* before a suitable register is found.
|
||||
*/
|
||||
internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): Int {
|
||||
if (implementation == null) {
|
||||
throw IllegalArgumentException("Method has no implementation: $this")
|
||||
}
|
||||
if (startIndex < 0 || startIndex >= instructions.count()) {
|
||||
throw IllegalArgumentException("startIndex out of bounds: $startIndex")
|
||||
}
|
||||
|
||||
// All registers used by an instruction.
|
||||
fun Instruction.getRegistersUsed() = when (this) {
|
||||
is FiveRegisterInstruction -> {
|
||||
when (registerCount) {
|
||||
1 -> listOf(registerC)
|
||||
2 -> listOf(registerC, registerD)
|
||||
3 -> listOf(registerC, registerD, registerE)
|
||||
4 -> listOf(registerC, registerD, registerE, registerF)
|
||||
else -> listOf(registerC, registerD, registerE, registerF, registerG)
|
||||
}
|
||||
}
|
||||
is ThreeRegisterInstruction -> listOf(registerA, registerB, registerC)
|
||||
is TwoRegisterInstruction -> listOf(registerA, registerB)
|
||||
is OneRegisterInstruction -> listOf(registerA)
|
||||
is RegisterRangeInstruction -> (startRegister until (startRegister + registerCount)).toList()
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
// Register that is written to by an instruction.
|
||||
fun Instruction.getWriteRegister() : Int {
|
||||
// Two and three register instructions extend OneRegisterInstruction.
|
||||
if (this is OneRegisterInstruction) return registerA
|
||||
throw IllegalStateException("Not a write instruction: $this")
|
||||
}
|
||||
|
||||
val writeOpcodes = EnumSet.of(
|
||||
ARRAY_LENGTH,
|
||||
INSTANCE_OF,
|
||||
NEW_INSTANCE, NEW_ARRAY,
|
||||
MOVE, MOVE_FROM16, MOVE_16, MOVE_WIDE, MOVE_WIDE_FROM16, MOVE_WIDE_16, MOVE_OBJECT,
|
||||
MOVE_OBJECT_FROM16, MOVE_OBJECT_16, MOVE_RESULT, MOVE_RESULT_WIDE, MOVE_RESULT_OBJECT, MOVE_EXCEPTION,
|
||||
CONST, CONST_4, CONST_16, CONST_HIGH16, CONST_WIDE_16, CONST_WIDE_32,
|
||||
CONST_WIDE, CONST_WIDE_HIGH16, CONST_STRING, CONST_STRING_JUMBO,
|
||||
IGET, IGET_WIDE, IGET_OBJECT, IGET_BOOLEAN, IGET_BYTE, IGET_CHAR, IGET_SHORT,
|
||||
IGET_VOLATILE, IGET_WIDE_VOLATILE, IGET_OBJECT_VOLATILE,
|
||||
SGET, SGET_WIDE, SGET_OBJECT, SGET_BOOLEAN, SGET_BYTE, SGET_CHAR, SGET_SHORT,
|
||||
SGET_VOLATILE, SGET_WIDE_VOLATILE, SGET_OBJECT_VOLATILE,
|
||||
AGET, AGET_WIDE, AGET_OBJECT, AGET_BOOLEAN, AGET_BYTE, AGET_CHAR, AGET_SHORT,
|
||||
// Arithmetic and logical operations.
|
||||
ADD_DOUBLE_2ADDR, ADD_DOUBLE, ADD_FLOAT_2ADDR, ADD_FLOAT, ADD_INT_2ADDR,
|
||||
ADD_INT_LIT8, ADD_INT, ADD_LONG_2ADDR, ADD_LONG, ADD_INT_LIT16,
|
||||
AND_INT_2ADDR, AND_INT_LIT8, AND_INT_LIT16, AND_INT, AND_LONG_2ADDR, AND_LONG,
|
||||
DIV_DOUBLE_2ADDR, DIV_DOUBLE, DIV_FLOAT_2ADDR, DIV_FLOAT, DIV_INT_2ADDR,
|
||||
DIV_INT_LIT16, DIV_INT_LIT8, DIV_INT, DIV_LONG_2ADDR, DIV_LONG,
|
||||
DOUBLE_TO_FLOAT, DOUBLE_TO_INT, DOUBLE_TO_LONG,
|
||||
FLOAT_TO_DOUBLE, FLOAT_TO_INT, FLOAT_TO_LONG,
|
||||
INT_TO_BYTE, INT_TO_CHAR, INT_TO_DOUBLE, INT_TO_FLOAT, INT_TO_LONG, INT_TO_SHORT,
|
||||
LONG_TO_DOUBLE, LONG_TO_FLOAT, LONG_TO_INT,
|
||||
MUL_DOUBLE_2ADDR, MUL_DOUBLE, MUL_FLOAT_2ADDR, MUL_FLOAT, MUL_INT_2ADDR,
|
||||
MUL_INT_LIT16, MUL_INT_LIT8, MUL_INT, MUL_LONG_2ADDR, MUL_LONG,
|
||||
NEG_DOUBLE, NEG_FLOAT, NEG_INT, NEG_LONG,
|
||||
NOT_INT, NOT_LONG,
|
||||
OR_INT_2ADDR, OR_INT_LIT16, OR_INT_LIT8, OR_INT, OR_LONG_2ADDR, OR_LONG,
|
||||
REM_DOUBLE_2ADDR, REM_DOUBLE, REM_FLOAT_2ADDR, REM_FLOAT, REM_INT_2ADDR,
|
||||
REM_INT_LIT16, REM_INT_LIT8, REM_INT, REM_LONG_2ADDR, REM_LONG,
|
||||
RSUB_INT_LIT8, RSUB_INT,
|
||||
SHL_INT_2ADDR, SHL_INT_LIT8, SHL_INT, SHL_LONG_2ADDR, SHL_LONG,
|
||||
SHR_INT_2ADDR, SHR_INT_LIT8, SHR_INT, SHR_LONG_2ADDR, SHR_LONG,
|
||||
SUB_DOUBLE_2ADDR, SUB_DOUBLE, SUB_FLOAT_2ADDR, SUB_FLOAT, SUB_INT_2ADDR,
|
||||
SUB_INT, SUB_LONG_2ADDR, SUB_LONG,
|
||||
USHR_INT_2ADDR, USHR_INT_LIT8, USHR_INT, USHR_LONG_2ADDR, USHR_LONG,
|
||||
XOR_INT_2ADDR, XOR_INT_LIT16, XOR_INT_LIT8, XOR_INT, XOR_LONG_2ADDR, XOR_LONG,
|
||||
)
|
||||
|
||||
val branchOpcodes = EnumSet.of(
|
||||
GOTO, GOTO_16, GOTO_32,
|
||||
IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE,
|
||||
IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ,
|
||||
PACKED_SWITCH_PAYLOAD, SPARSE_SWITCH_PAYLOAD
|
||||
)
|
||||
|
||||
val returnOpcodes = EnumSet.of(
|
||||
RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, RETURN_VOID_NO_BARRIER,
|
||||
THROW
|
||||
)
|
||||
|
||||
// Highest 4-bit register available, exclusive. Ideally return a free register less than this.
|
||||
val maxRegister4Bits = 16
|
||||
var bestFreeRegisterFound: Int? = null
|
||||
val usedRegisters = registersToExclude.toMutableSet()
|
||||
|
||||
for (i in startIndex until instructions.count()) {
|
||||
val instruction = getInstruction(i)
|
||||
val instructionRegisters = instruction.getRegistersUsed()
|
||||
|
||||
if (instruction.opcode in returnOpcodes) {
|
||||
// Method returns.
|
||||
usedRegisters.addAll(instructionRegisters)
|
||||
|
||||
// Use lowest register that hasn't been encountered.
|
||||
val freeRegister = (0 until implementation!!.registerCount).find {
|
||||
it !in usedRegisters
|
||||
}
|
||||
if (freeRegister != null) {
|
||||
return freeRegister
|
||||
}
|
||||
if (bestFreeRegisterFound != null) {
|
||||
return bestFreeRegisterFound
|
||||
}
|
||||
|
||||
// Somehow every method register was read from before any register was wrote to.
|
||||
// In practice this never occurs.
|
||||
throw IllegalArgumentException("Could not find a free register from startIndex: " +
|
||||
"$startIndex excluding: $registersToExclude")
|
||||
}
|
||||
|
||||
if (instruction.opcode in branchOpcodes) {
|
||||
if (bestFreeRegisterFound != null) {
|
||||
return bestFreeRegisterFound
|
||||
}
|
||||
// This method is simple and does not follow branching.
|
||||
throw IllegalArgumentException("Encountered a branch statement before a free register could be found")
|
||||
}
|
||||
|
||||
if (instruction.opcode in writeOpcodes) {
|
||||
val writeRegister = instruction.getWriteRegister()
|
||||
|
||||
if (writeRegister !in usedRegisters) {
|
||||
// Verify the register is only used for write and not also as a parameter.
|
||||
// If the instruction uses the write register once then it's not also a read register.
|
||||
if (instructionRegisters.count { register -> register == writeRegister } == 1) {
|
||||
if (writeRegister < maxRegister4Bits) {
|
||||
// Found an ideal register.
|
||||
return writeRegister
|
||||
}
|
||||
|
||||
// Continue searching for a 4-bit register if available.
|
||||
if (bestFreeRegisterFound == null || writeRegister < bestFreeRegisterFound) {
|
||||
bestFreeRegisterFound = writeRegister
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usedRegisters.addAll(instructionRegisters)
|
||||
}
|
||||
|
||||
// Some methods can have array payloads at the end of the method after a return statement.
|
||||
// But in normal usage this cannot be reached since a branch or return statement
|
||||
// will be encountered before the end of the method.
|
||||
throw IllegalArgumentException("Start index is outside the range of normal control flow: $startIndex")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
|
||||
@ -395,7 +567,7 @@ fun Method.findInstructionIndicesReversedOrThrow(opcode: Opcode): List<Int> {
|
||||
|
||||
internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, extensionsMethod: String) {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
||||
val index = indexOfFirstInstructionOrThrow(literalIndex, MOVE_RESULT)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
val operation = if (register < 16) {
|
||||
@ -423,7 +595,7 @@ fun BytecodePatchContext.forEachLiteralValueInstruction(
|
||||
classes.forEach { classDef ->
|
||||
classDef.methods.forEach { method ->
|
||||
method.implementation?.instructions?.forEachIndexed { index, instruction ->
|
||||
if (instruction.opcode == Opcode.CONST &&
|
||||
if (instruction.opcode == CONST &&
|
||||
(instruction as WideLiteralInstruction).wideLiteral == literal
|
||||
) {
|
||||
val mutableMethod = proxy(classDef).mutableClass.findMutableMethodOf(method)
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -22,7 +21,7 @@ Second \"item\" text"</string>
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">فشلت عمليات التحقق</string>
|
||||
<string name="revanced_check_environment_failed_title">فشلت الفحوصات</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">فتح الموقع الرسمي</string>
|
||||
<string name="revanced_check_environment_dialog_ignore_button">تجاهل</string>
|
||||
<string name="revanced_check_environment_failed_message"><h5>لا يبدو أن هذا التطبيق قد تم تعديله من قبلك.</h5><br>قد لا يعمل هذا التطبيق بشكل صحيح، <b>قد يكون ضارًا أو حتى خطيرًا للاستخدام</b>.<br><br>تشير هذه الفحوصات إلى أن هذا التطبيق تم تعديله مسبقًا أو تم الحصول عليه من شخص آخر:<br><br><small>%1$s</small><br>يوصى بشدة بـ <b>إلغاء تثبيت هذا التطبيق وتعديله بنفسك</b> للتأكد من أنك تستخدم تطبيقًا معتمدًا وآمنًا.<p><br>في حالة تجاهل هذا التحذير، سيتم عرضه مرتين فقط.</string>
|
||||
@ -30,19 +29,22 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">لم يتم تثبيته بواسطة ReVanced Manager</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time">تم تعديله قبل أكثر من 10 دقائق</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_days">تم التعديل منذ %s يوم</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">تاريخ إنشاء APK تالف</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">تاريخ بناء APK تالف</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsResourcePatch">
|
||||
<string name="revanced_settings_submenu_title">الإعدادات</string>
|
||||
<string name="revanced_settings_confirm_user_dialog_title">هل ترغب في المتابعة؟</string>
|
||||
<string name="revanced_settings_reset">إعادة التعيين</string>
|
||||
<string name="revanced_settings_restart_title">تحديث وإعادة تشغيل</string>
|
||||
<string name="revanced_settings_restart_title">تحديث وإعادة التشغيل</string>
|
||||
<string name="revanced_settings_restart">إعادة التشغيل</string>
|
||||
<string name="revanced_settings_import">استيراد</string>
|
||||
<string name="revanced_settings_import_copy">نسخ</string>
|
||||
<string name="revanced_settings_import_reset">إعادة تعيين إعدادات ReVanced إلى الوضع الافتراضي</string>
|
||||
<string name="revanced_settings_import_success">تم استيراد %d إعدادات</string>
|
||||
<string name="revanced_settings_import_failure_parse">فشل الاستيراد: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">عرض أيقونات إعدادات ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">يتم عرض أيقونات الإعدادات</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">لا يتم عرض أيقونات الإعدادات</string>
|
||||
<string name="revanced_language_title">لغة ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"قد تكون الترجمات لبعض اللغات مفقودة أو غير مكتملة.
|
||||
|
||||
@ -106,9 +108,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_debug_protobuffer_title">سجل بروتوكول التخزين المؤقت</string>
|
||||
<string name="revanced_debug_protobuffer_summary_on">تتضمن سجلات التصحيح التخزين المؤقت</string>
|
||||
<string name="revanced_debug_protobuffer_summary_off">لا تتضمن سجلات التصحيح التخزين المؤقت</string>
|
||||
<string name="revanced_debug_stacktrace_title">سجل عمليات التطبيق</string>
|
||||
<string name="revanced_debug_stacktrace_summary_on">تتضمن سجلات التصحيح سِجِل عمليات التطبيق</string>
|
||||
<string name="revanced_debug_stacktrace_summary_off">لا تتضمن سجلات التصحيح سِجِل عمليات التطبيق</string>
|
||||
<string name="revanced_debug_stacktrace_title">سجل تتبع المكدس</string>
|
||||
<string name="revanced_debug_stacktrace_summary_on">تتضمن سجلات التصحيح سجل تتبع المكدس</string>
|
||||
<string name="revanced_debug_stacktrace_summary_off">لا تتضمن سجلات التصحيح سجل تتبع المكدس</string>
|
||||
<string name="revanced_debug_toast_on_error_title">عرض ملاحظة عند وجود خطأ في ReVanced</string>
|
||||
<string name="revanced_debug_toast_on_error_summary_on">يتم عرض ملاحظة في حالة حدوث خطأ</string>
|
||||
<string name="revanced_debug_toast_on_error_summary_off">لا يتم عرض ملاحظة في حالة حدوث خطأ</string>
|
||||
@ -214,9 +216,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_related_videos_title">إخفاء الفيديوهات ذات الصلة في الإجراءات السريعة</string>
|
||||
<string name="revanced_hide_related_videos_summary_on">تم إخفاء الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_related_videos_summary_off">يتم عرض الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_image_shelf_title">إخفاء رفوف الصور في نتائج البحث</string>
|
||||
<string name="revanced_hide_image_shelf_summary_on">تم إخفاء رفوف الصورة</string>
|
||||
<string name="revanced_hide_image_shelf_summary_off">يتم عرض رفوف الصورة</string>
|
||||
<string name="revanced_hide_image_shelf_title">إخفاء رف الصورة في نتائج البحث</string>
|
||||
<string name="revanced_hide_image_shelf_summary_on">تم إخفاء رف الصورة</string>
|
||||
<string name="revanced_hide_image_shelf_summary_off">يتم عرض رف الصورة</string>
|
||||
<string name="revanced_hide_latest_posts_ads_title">إخفاء آخر المشاركات</string>
|
||||
<string name="revanced_hide_latest_posts_ads_summary_on">تم إخفاء أحدث المشاركات</string>
|
||||
<string name="revanced_hide_latest_posts_ads_summary_off">يتم عرض أحدث المشاركات</string>
|
||||
@ -226,7 +228,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_artist_cards_title">إخفاء بطاقات الفنان</string>
|
||||
<string name="revanced_hide_artist_cards_summary_on">تم إخفاء بطاقات الفنان</string>
|
||||
<string name="revanced_hide_artist_cards_summary_off">يتم عرض بطاقات الفنان</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">إخفاء \"ملخص الفيديو الذي تم إنشاؤه بواسطة الذكاء الاصطناعي\"</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">إخفاء \'ملخص الفيديو الذي تم إنشاؤه بواسطة الذكاء الاصطناعي\'</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">تم إخفاء قسم ملخص الفيديو</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">يتم عرض قسم ملخص الفيديو</string>
|
||||
<string name="revanced_hide_attributes_section_title">إخفاء الصفات</string>
|
||||
@ -253,31 +255,31 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_description_components_screen_title">وصف الفيديو</string>
|
||||
<string name="revanced_hide_description_components_screen_summary">إخفاء أو عرض مكونات وصف الفيديو</string>
|
||||
<string name="revanced_hide_filter_bar_screen_title">شريط التصفية</string>
|
||||
<string name="revanced_hide_filter_bar_screen_summary">إخفاء أو إظهار شريط الفلتر في الخلاصة ونتائج البحث ومقاطع الفيديو ذات الصلة</string>
|
||||
<string name="revanced_hide_filter_bar_screen_summary">إخفاء أو عرض شريط الفلتر في الموجز ونتائج البحث والفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_feed_title">إخفاء في الموجز</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_feed_summary_on">مخفي في الموجز</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">يعرض في الموجز</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">يُعرض في الموجز</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_search_title">إخفاء في نتائج البحث</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">مخفي في نتائج البحث</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">يظهر في نتائج البحث</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">يُعرض في نتائج البحث</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">إخفاء في الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">مخفي في الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">يعرض في الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">يُعرض في الفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_comments_screen_title">التعليقات</string>
|
||||
<string name="revanced_comments_screen_summary">إخفاء أو عرض مكونات قسم التعليقات</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_title">إخفاء ملخص دردشة الذكاء الاصطناعي</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_title">إخفاء ملخص محادثات الذكاء الاصطناعي</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_summary_on">تم إخفاء ملخص المحادثات</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_summary_off">يتم عرض ملخص المحادثات</string>
|
||||
<string name="revanced_hide_comments_ai_summary_title">إخفاء ملخص تعليقات الذكاء الاصطناعي</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_on">ملخص التعليقات مخفي</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_off">ملخص التعليقات معروض</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_on">تم إخفاء ملخص التعليقات</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_off">يتم عرض ملخص التعليقات</string>
|
||||
<string name="revanced_hide_comments_by_members_header_title">إخفاء رأس \'تعليقات الأعضاء\'</string>
|
||||
<string name="revanced_hide_comments_by_members_header_summary_on">تم إخفاء علامة تعليقات من الأعضاء</string>
|
||||
<string name="revanced_hide_comments_by_members_header_summary_off">يتم عرض علامة تعليقات من الأعضاء</string>
|
||||
<string name="revanced_hide_comments_section_title">إخفاء قسم التعليقات</string>
|
||||
<string name="revanced_hide_comments_section_summary_on">تم إخفاء قسم التعليقات</string>
|
||||
<string name="revanced_hide_comments_section_summary_off">يتم عرض قسم التعليقات</string>
|
||||
<string name="revanced_hide_comments_create_a_short_button_title">إخفاء زر \'إنشاء مقطع Short\'</string>
|
||||
<string name="revanced_hide_comments_create_a_short_button_title">إخفاء زر \'إنشاء Short\'</string>
|
||||
<string name="revanced_hide_comments_create_a_short_button_summary_on">تم إخفاء زر إنشاء Short</string>
|
||||
<string name="revanced_hide_comments_create_a_short_button_summary_off">يتم عرض زر إنشاء Short</string>
|
||||
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_title">إخفاء أزرار الرموز التعبيرية والطوابع الزمنية</string>
|
||||
@ -305,7 +307,7 @@ Second \"item\" text"</string>
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<string name="revanced_custom_filter_strings_summary">قائمة سلاسل منشئ مسار المكونات المراد تصفيتها مفصولة بسطر جديد</string>
|
||||
<string name="revanced_custom_filter_toast_invalid_syntax">فلتر مخصص غير صالح: %s</string>
|
||||
<string name="revanced_hide_keyword_content_screen_title">إخفاء محتوى الكلمات الرئيسية</string>
|
||||
<string name="revanced_hide_keyword_content_screen_title">إخفاء محتوى الكلمات المفتاحية</string>
|
||||
<string name="revanced_hide_keyword_content_screen_summary">إخفاء فيديوهات البحث والموجز باستخدام فلاتر الكلمات المفتاحية</string>
|
||||
<string name="revanced_hide_keyword_content_home_title">إخفاء فيديوهات الصفحة الرئيسية بواسطة الكلمات المفتاحية</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_on">تتم تصفية الفيديوهات في علامة التبويب \"الصفحة الرئيسية\" حسب الكلمات المفتاحية</string>
|
||||
@ -335,11 +337,11 @@ Second \"item\" text"</string>
|
||||
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc.) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_summary">سيؤدي وضع علامة اقتباس مزدوجة حول كلمة رئيسية/عبارة إلى منع التطابقات الجزئية لعناوين الفيديو وأسماء القنوات.<br><br>على سبيل المثال،<br><b>\"ai\"</b> سيخفي الفيديو: <b>How does AI work?</b><br>ولكن لن يخفي: <b>What does fair use mean?</b></string>
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<string name="revanced_hide_keyword_toast_invalid_common">لا يمكن استخدام الكلمة الرئيسية: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common_whole_word_required">إضافة اقتباسات لاستخدام الكلمة الرئيسية: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_conflicting">الكلمة الرئيسية لها بيانات متضاربة: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_length">الكلمة الرئيسية قصيرة جدًا وتتطلب اقتباسات: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_broad">الكلمة الرئيسية سوف تخفي جميع الفيديوهات: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common">لا يمكن استخدام الكلمة المفتاحية: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common_whole_word_required">إضافة اقتباسات لاستخدام الكلمة المفتاحية: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_conflicting">الكلمة المفتاحية لها بيانات متضاربة: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_length">الكلمة المفتاحية قصيرة جدًا وتتطلب اقتباسات: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_broad">الكلمة المفتاحية سوف تخفي جميع الفيديوهات: %s</string>
|
||||
</patch>
|
||||
<patch id="ad.general.hideAdsResourcePatch">
|
||||
<string name="revanced_hide_general_ads_title">إخفاء الإعلانات العامة</string>
|
||||
@ -359,20 +361,20 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_self_sponsor_ads_title">إخفاء بطاقات الرعاية الذاتية</string>
|
||||
<string name="revanced_hide_self_sponsor_ads_summary_on">تم إخفاء بطاقات الرعاية الذاتية</string>
|
||||
<string name="revanced_hide_self_sponsor_ads_summary_off">يتم عرض بطاقات الرعاية الذاتية</string>
|
||||
<string name="revanced_hide_products_banner_title">إخفاء لافتة \"عرض المنتجات\"</string>
|
||||
<string name="revanced_hide_products_banner_title">إخفاء لافتة \'عرض المنتجات\'</string>
|
||||
<string name="revanced_hide_products_banner_summary_on">تم إخفاء البانر</string>
|
||||
<string name="revanced_hide_products_banner_summary_off">يتم عرض البانر</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_title">إخفاء لافتة شاشة المتجر النهائية</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_on">تم إخفاء لافتة المتجر</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_off">يتم عرض لافتة المتجر</string>
|
||||
<string name="revanced_hide_player_store_shelf_title">إخفاء رف مشغل التسوق</string>
|
||||
<string name="revanced_hide_player_store_shelf_summary_on">تم إخفاء رفوف التسوق</string>
|
||||
<string name="revanced_hide_player_store_shelf_summary_off">يتم عرض رفوف التسوق</string>
|
||||
<string name="revanced_hide_player_store_shelf_summary_on">تم إخفاء رف التسوق</string>
|
||||
<string name="revanced_hide_player_store_shelf_summary_off">يتم عرض رف التسوق</string>
|
||||
<string name="revanced_hide_shopping_links_title">إخفاء روابط التسوق في وصف الفيديو</string>
|
||||
<string name="revanced_hide_shopping_links_summary_on">تم إخفاء روابط التسوق في وصف الفيديو</string>
|
||||
<string name="revanced_hide_shopping_links_summary_off">يتم عرض روابط التسوق في وصف الفيديو</string>
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_visit_store_button_title">إخفاء زر \"زيارة المتجر\" على صفحات القناة</string>
|
||||
<string name="revanced_hide_visit_store_button_title">إخفاء زر \'زيارة المتجر\' على صفحات القناة</string>
|
||||
<string name="revanced_hide_visit_store_button_summary_on">تم إخفاء الزر في صفحة القناة</string>
|
||||
<string name="revanced_hide_visit_store_button_summary_off">يتم عرض الزر في صفحة القناة</string>
|
||||
<string name="revanced_hide_web_search_results_title">إخفاء نتائج بحث الويب</string>
|
||||
@ -387,7 +389,7 @@ Second \"item\" text"</string>
|
||||
<patch id="ad.getpremium.hideGetPremiumPatch">
|
||||
<string name="revanced_hide_get_premium_title">إخفاء ترقية YouTube Premium</string>
|
||||
<string name="revanced_hide_get_premium_summary_on">تم إخفاء عروض YouTube Premium الترويجية تحت مشغل الفيديو</string>
|
||||
<string name="revanced_hide_get_premium_summary_off">يتم عرض عروض YouTube Premium الترويجية تحت مشغل الفيديو</string>
|
||||
<string name="revanced_hide_get_premium_summary_off">يتم إظهار عروض YouTube Premium الترويجية تحت مشغل الفيديو</string>
|
||||
</patch>
|
||||
<patch id="ad.video.videoAdsPatch">
|
||||
<string name="revanced_hide_video_ads_title">إخفاء إعلانات الفيديو</string>
|
||||
@ -430,9 +432,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_disable_precise_seeking_gesture_summary_off">تم تمكين الإيماءة</string>
|
||||
</patch>
|
||||
<patch id="interaction.seekbar.enableSeekbarTappingPatch">
|
||||
<string name="revanced_seekbar_tapping_title">تمكين النقر للبحث</string>
|
||||
<string name="revanced_seekbar_tapping_summary_on">تم تمكين النقر للتنقل</string>
|
||||
<string name="revanced_seekbar_tapping_summary_off">تم تعطيل النقر للبحث</string>
|
||||
<string name="revanced_seekbar_tapping_title">تمكين النقر للتمرير</string>
|
||||
<string name="revanced_seekbar_tapping_summary_on">تم تمكين النقر للتمرير</string>
|
||||
<string name="revanced_seekbar_tapping_summary_off">تم تعطيل النقر للتمرير</string>
|
||||
</patch>
|
||||
<patch id="interaction.swipecontrols.swipeControlsResourcePatch">
|
||||
<string name="revanced_swipe_brightness_title">التحكم بالسطوع عن طريق ايماءة التمرير</string>
|
||||
@ -610,6 +612,10 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">إخفاء المقطع الصوتي</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">تم إخفاء قائمة المقطع الصوتي</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">يتم عرض قائمة المقطع الصوتي</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"تم إخفاء قائمة المقطع الصوتي
|
||||
|
||||
لعرض قائمة المقطع الصوتي، غيّر 'Spoof Video Streams' إلى iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">إخفاء المشاهدة في VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">تم إخفاء قائمة المشاهدة في الوضع الافتراضي</string>
|
||||
@ -667,17 +673,17 @@ Second \"item\" text"</string>
|
||||
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
|
||||
<string name="revanced_hide_shorts_home_title">إخفاء Shorts في موجز الصفحة الرئيسية</string>
|
||||
<string name="revanced_hide_shorts_home_summary_on">مخفية في الصفحة الرئيسية والفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_shorts_home_summary_off">تعرض في الصفحة الرئيسية والفيديوهات ذات الصلة</string>
|
||||
<string name="revanced_hide_shorts_home_summary_off">تُعرض في الصفحة الرئيسية والفيديوهات ذات الصلة</string>
|
||||
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
|
||||
<string name="revanced_hide_shorts_subscriptions_title">إخفاء Shorts في موجز الاشتراكات</string>
|
||||
<string name="revanced_hide_shorts_subscriptions_summary_on">مخفية في موجز الاشتراكات</string>
|
||||
<string name="revanced_hide_shorts_subscriptions_summary_off">تعرض في موجز الاشتراكات</string>
|
||||
<string name="revanced_hide_shorts_subscriptions_summary_off">تُعرض في موجز الاشتراكات</string>
|
||||
<string name="revanced_hide_shorts_search_title">إخفاء Shorts في نتائج البحث</string>
|
||||
<string name="revanced_hide_shorts_search_summary_on">مخفية في نتائج البحث</string>
|
||||
<string name="revanced_hide_shorts_search_summary_off">تعرض في نتائج البحث</string>
|
||||
<string name="revanced_hide_shorts_search_summary_off">تُعرض في نتائج البحث</string>
|
||||
<string name="revanced_hide_shorts_history_title">إخفاء Shorts في سجل المشاهدة</string>
|
||||
<string name="revanced_hide_shorts_history_summary_on">مخفية في سجل المشاهدة</string>
|
||||
<string name="revanced_hide_shorts_history_summary_off">تعرض في سجل المشاهدة</string>
|
||||
<string name="revanced_hide_shorts_history_summary_off">تُعرض في سجل المشاهدة</string>
|
||||
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
|
||||
<string name="revanced_hide_shorts_join_button_title">إخفاء زر الانضمام</string>
|
||||
<string name="revanced_hide_shorts_join_button_summary_on">تم إخفاء زر الانضمام</string>
|
||||
@ -766,7 +772,7 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
|
||||
<string name="revanced_end_screen_suggested_video_title">إخفاء الفيديو المقترح في شاشة النهاية</string>
|
||||
<string name="revanced_end_screen_suggested_video_summary_on">"يتم إخفاء الفيديو المقترح في شاشة النهاية عند إيقاف التشغيل التلقائي
|
||||
<string name="revanced_end_screen_suggested_video_summary_on">"تم إخفاء الفيديو المقترح في شاشة النهاية عند إيقاف التشغيل التلقائي
|
||||
|
||||
يمكن تغيير التشغيل التلقائي في إعدادات YouTube:
|
||||
الإعدادات ← التشغيل ← تشغيل الفيديو التالي تلقائيًا"</string>
|
||||
@ -864,7 +870,7 @@ Second \"item\" text"</string>
|
||||
|
||||
مصغرات شريط التقدم سوف تستخدم نفس جودة الفيديو الحالي.
|
||||
|
||||
تعمل هذه الميزة بشكل أفضل مع جودة فيديو 720p أو أقل وعند استخدام اتصال إنترنت سريع جداً."</string>
|
||||
تعمل هذه الميزة بشكل أفضل مع جودة فيديو 720p أو أقل وعند استخدام اتصال إنترنت سريع جدًا."</string>
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_title">استعادة مصغرات شريط التقدم القديمة</string>
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">مصغرات شريط التقدم ستظهر فوق شريط تقدم الفيديو</string>
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">مصغرات شريط التقدم ستظهر في ملء الشاشة</string>
|
||||
@ -937,15 +943,15 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">لا تعرض مرة أخرى</string>
|
||||
<string name="revanced_sb_diff_segments">تغيير سلوك المقطع</string>
|
||||
<string name="revanced_sb_segments_sponsor">الراعي</string>
|
||||
<string name="revanced_sb_segments_sponsor_sum">الترويج المدفوع الأجر، والإحالات المدفوعة الأجر والإعلانات المباشرة. ليس للترويج الذاتي أو لصراعات مجانية للقضايا/المبدعين/المواقع الإلكترونية/المنتجات التي يحبون الحصول عليها</string>
|
||||
<string name="revanced_sb_segments_sponsor_sum">الترويج المدفوع، والإحالات المدفوعة، والإعلانات المباشرة. ليس للترويج الذاتي أو الترويج المجاني للقضايا/المبدعين/المواقع الإلكترونية/المنتجات التي يفضلونها</string>
|
||||
<string name="revanced_sb_segments_selfpromo">ترويج شخصي/غير مدفوع الأجر</string>
|
||||
<string name="revanced_sb_segments_selfpromo_sum">مشابهة لـ \"الراعي\" باستثناء ما يتعلق بالإعلانات غير المدفوعة الأجر أو الذاتية. ويشمل ذلك أقسام عن السلع أو التبرعات أو المعلومات المتعلقة بمن تعاونوا مع ناشر المحتوى</string>
|
||||
<string name="revanced_sb_segments_selfpromo_sum">مشابهة لـ \'الراعي\' باستثناء ما يتعلق بالإعلانات غير المدفوعة الأجر أو الذاتية. ويشمل ذلك أقسام عن السلع أو التبرعات أو المعلومات المتعلقة بمن تعاونوا مع ناشر المحتوى</string>
|
||||
<string name="revanced_sb_segments_interaction">تذكير بالتفاعل (اشتراك)</string>
|
||||
<string name="revanced_sb_segments_interaction_sum">تذكير قصير للإعجاب أو الاشتراك أو المتابعة في منتصف المحتوى. إذا كانت طويلة أو تتعلق بشيء محدد، فيجب أن تكون خاضعة للترويج الشخصي بدلاً من ذلك</string>
|
||||
<string name="revanced_sb_segments_highlight">الأبرز</string>
|
||||
<string name="revanced_sb_segments_highlight_sum">الجزء من الفيديو الذي يبحث عنه معظم الناس</string>
|
||||
<string name="revanced_sb_segments_intro">المقدمة/فاصل</string>
|
||||
<string name="revanced_sb_segments_intro_sum">فاصل زمني بدون محتوى فعلي. يمكن أن يكون وقفاً مؤقتاً، أو إطارًا ثابتاً، أو تكرارًا. لا يتضمن التحولات التي تحتوي على معلومات</string>
|
||||
<string name="revanced_sb_segments_intro_sum">فاصل زمني بدون محتوى فعلي. قد يكون توقفًا مؤقتًا، أو إطارًا ثابتًا، أو رسومًا متحركة متكررة. لا يتضمن انتقالات تحتوي على معلومات</string>
|
||||
<string name="revanced_sb_segments_outro">الخاتمة/تترات النهاية</string>
|
||||
<string name="revanced_sb_segments_outro_sum">تتر النهاية أو عندما تظهر بطاقات نهاية YouTube، نهايات غير منطوقة. ليس للاستنتاجات مع المعلومات</string>
|
||||
<string name="revanced_sb_segments_preview">معاينة/موجز/ربط</string>
|
||||
@ -1079,9 +1085,9 @@ Second \"item\" text"</string>
|
||||
تصميم الجهاز اللوحي
|
||||
• مشاركات المجتمع مخفية
|
||||
|
||||
تصميم السيارة
|
||||
تصميم Automotive
|
||||
• يتم فتح Shorts في المشغل العادي
|
||||
• يتم تنظيم الخلاصة حسب المواضيع والقنوات"</string>
|
||||
• يتم تنظيم الموجز حسب المواضيع والقنوات"</string>
|
||||
</patch>
|
||||
<patch id="layout.spoofappversion.spoofAppVersionPatch">
|
||||
<string name="revanced_spoof_app_version_title">خِداع إصدار التطبيق</string>
|
||||
@ -1155,7 +1161,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_miniplayer_type_entry_4">حديث 1</string>
|
||||
<string name="revanced_miniplayer_type_entry_5">حديث 2</string>
|
||||
<string name="revanced_miniplayer_type_entry_6">حديث 3</string>
|
||||
<string name="revanced_miniplayer_type_entry_7">الوضع الحديث 4</string>
|
||||
<string name="revanced_miniplayer_type_entry_7">حديث 4</string>
|
||||
<string name="revanced_miniplayer_rounded_corners_title">تمكين الزوايا المستديرة</string>
|
||||
<string name="revanced_miniplayer_rounded_corners_summary_on">الزوايا مستديرة</string>
|
||||
<string name="revanced_miniplayer_rounded_corners_summary_off">الزوايا مربعة</string>
|
||||
@ -1175,14 +1181,14 @@ Second \"item\" text"</string>
|
||||
|
||||
يمكن سحب المشغل المصغر خارج الشاشة إلى اليسار أو اليمين"</string>
|
||||
<string name="revanced_miniplayer_horizontal_drag_summary_off">تم تعطيل إيماءة السحب الأفقية</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">إخفاء أزرار التراكب</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">أزرار التراكب مخفية</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">أزرار التراكب ظاهرة</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">إخفاء أزرار الواجهة</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">تم إخفاء أزرار الواجهة</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">يتم عرض أزرار الواجهة</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">إخفاء زري التوسيع والإغلاق</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"الأزرار مخفية
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"تم إخفاء الأزرار
|
||||
|
||||
اسحب للتوسيع أو الإغلاق"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">زرا التوسيع والإغلاق ظاهران</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">يتم عرض زري التوسيع والإغلاق</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_title">إخفاء النصوص الفرعية</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_on">تم إخفاء النصوص الفرعية</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_off">يتم عرض النصوص الفرعية</string>
|
||||
@ -1218,7 +1224,7 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
|
||||
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
|
||||
<string name="revanced_alt_thumbnail_home_title">علامة تبويب الصفحة الرئيسية</string>
|
||||
<string name="revanced_alt_thumbnail_home_title">علامة التبويب الصفحة الرئيسية</string>
|
||||
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
|
||||
<string name="revanced_alt_thumbnail_subscription_title">علامة التبويب الاشتراكات</string>
|
||||
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
|
||||
@ -1229,11 +1235,11 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & المصّغرات الأصلية</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & اللقطات الثابتة</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">اللقطات الثابتة</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"يوفر DeArrow مصغرات فيديو من مصادر جماعية لفيديوهات YouTube. هذه المصغرات غالبا ما تكون أكثر صلة من تلك المقدمة من YouTube
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">"يوفر DeArrow مصغرات فيديو من مصادر جماعية لفيديوهات YouTube. هذه المصغرات غالبًا ما تكون أكثر صلة من تلك المقدمة من YouTube
|
||||
|
||||
إذا تم تفعيلها، سيتم إرسال روابط الفيديو إلى خادم API ولن يتم إرسال أي بيانات أخرى. إذا كان الفيديو لا يحتوي على مصغرات DArrow، سيتم عرض المقاطع الأصلية أو اللقطات الثابتة
|
||||
إذا تم تفعيلها، سيتم إرسال روابط الفيديو إلى خادم API ولن يتم إرسال أي بيانات أخرى. إذا كان الفيديو لا يحتوي على مصغرات DeArrow، سيتم عرض المقاطع الأصلية أو اللقطات الثابتة
|
||||
|
||||
اضغط هنا لمعرفة المزيد عن DArrow"</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>
|
||||
@ -1309,7 +1315,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_force_original_audio_summary_on">استخدام لغة الصوت الأصلية</string>
|
||||
<string name="revanced_force_original_audio_summary_off">استخدام الصوت الافتراضي</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_force_original_audio_not_available">لاستخدام هذه الميزة، غيّر \"انتحال دفقات الفيديو\" إلى iOS TV</string>
|
||||
<string name="revanced_force_original_audio_not_available">لاستخدام هذه الميزة، غيّر \'Spoof Video Streams\' إلى iOS TV</string>
|
||||
</patch>
|
||||
<patch id="video.quality.rememberVideoQualityPatch">
|
||||
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
|
||||
@ -1323,7 +1329,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_remember_shorts_quality_last_selected_summary_on">تنطبق تغييرات الجودة على جميع فيديوهات Shorts</string>
|
||||
<string name="revanced_remember_shorts_quality_last_selected_summary_off">تنطبق تغييرات الجودة فقط على فيديو Short الحالي</string>
|
||||
<string name="revanced_shorts_quality_default_wifi_title">جودة Shorts الافتراضية على شبكة Wi-Fi</string>
|
||||
<string name="revanced_shorts_quality_default_mobile_title">جودة Shorts الافتراضية على شبكة الجوال</string>
|
||||
<string name="revanced_shorts_quality_default_mobile_title">جودة Shorts الافتراضية على شبكة الجوّال</string>
|
||||
<string name="revanced_remember_video_quality_mobile">الجوّال</string>
|
||||
<string name="revanced_remember_video_quality_wifi">Wi-Fi</string>
|
||||
<string name="revanced_remember_video_quality_toast">تم تغيير جودة %1$s الافتراضية إلى: %2$s</string>
|
||||
@ -1359,7 +1365,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_disable_hdr_video_summary_off">تم تمكين فيديو HDR</string>
|
||||
</patch>
|
||||
<patch id="video.quality.advancedVideoQualityMenuPatch">
|
||||
<string name="revanced_advanced_video_quality_menu_title">إظهار قائمة جودة الفيديو المتقدمة</string>
|
||||
<string name="revanced_advanced_video_quality_menu_title">عرض قائمة جودة الفيديو المتقدمة</string>
|
||||
<string name="revanced_advanced_video_quality_menu_summary_on">يتم عرض قائمة جودة الفيديو المتقدمة</string>
|
||||
<string name="revanced_advanced_video_quality_menu_summary_off">لا يتم عرض قائمة جودة الفيديو المتقدمة</string>
|
||||
</patch>
|
||||
@ -1393,7 +1399,7 @@ AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الص
|
||||
• مستوى الصوت الثابت غير متاح
|
||||
• فرض الصوت الأصلي غير متوفر"</string>
|
||||
<string name="revanced_spoof_video_streams_about_no_av1">• لا يوجد ترميز الفيديو AV1</string>
|
||||
<string name="revanced_spoof_video_streams_about_kids_videos">• الفيديوات العربية لا يمكن تشغيل أو في وضع التشغيل أو في حالة الخفية</string>
|
||||
<string name="revanced_spoof_video_streams_about_kids_videos">• قد لا يتم تشغيل الفيديوهات المخصصة للأطفال عند تسجيل الخروج أو عند استخدام وضع التصفح المتخفي</string>
|
||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">عرض في إحصاءات تقنية</string>
|
||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">يتم عرض نوع العميل في إحصاءات تقنية</string>
|
||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">تم إخفاء نوع العميل في إحصاءات تقنية</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">ReVanced tənzimləmələr standarta təyin edildi</string>
|
||||
<string name="revanced_settings_import_success">%d tənzimləmə idxal edildi</string>
|
||||
<string name="revanced_settings_import_failure_parse">Uğursuz idxal prosesi: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">ReVanced tənzimləmə nişanların göstər</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Tənzimləmə nişanları göstərilir</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Tənzimləmə nişanları göstərilmir</string>
|
||||
<string name="revanced_language_title">ReVanced dili</string>
|
||||
<string name="revanced_language_user_dialog_message">"Bəzi dillər üçün tərcümələr əskik və ya səhv ola bilər.
|
||||
|
||||
@ -226,6 +228,9 @@ Gözlənilməz hallardan xəbərdar olmayacaqsınız."</string>
|
||||
<string name="revanced_hide_artist_cards_title">Sənətçi kartlarını gizlət</string>
|
||||
<string name="revanced_hide_artist_cards_summary_on">Sənətçi kartları gizlidir</string>
|
||||
<string name="revanced_hide_artist_cards_summary_off">Sənətçi kartları göstərilir</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">\"AI ilə yaradılan video xülasəsini\" gizlət</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Video xülasə bölməsi gizlədilib</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Video xülasə bölməsi göstərilir</string>
|
||||
<string name="revanced_hide_attributes_section_title">Atributları Gizlət</string>
|
||||
<string name="revanced_hide_attributes_section_summary_on">Seçilən yerlər, Oyunlar, Musiqi və qeyd edilən insanlar bölmələri gizlədilir</string>
|
||||
<string name="revanced_hide_attributes_section_summary_off">Seçilən yerlər, Oyunlar, Musiqi və qeyd edilən insanlar bölmələri görünür</string>
|
||||
@ -262,8 +267,12 @@ Gözlənilməz hallardan xəbərdar olmayacaqsınız."</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Əlaqəli videolarda görünür</string>
|
||||
<string name="revanced_comments_screen_title">Şərhlər</string>
|
||||
<string name="revanced_comments_screen_summary">Şərhlər bölməsi elementlərin gizlət və ya göstər</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_title">AI Söhbət Xülasəsini Gizlət</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_summary_on">Söhbət yekunu gizlidir </string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_summary_off">Söhbət yekunu görünür</string>
|
||||
<string name="revanced_hide_comments_ai_summary_title">AI Ṣərhlər Xülasəsini Gizlət</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_on">Ṣərh yekunu gizlidir </string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_off">Şərh yekunu görünür</string>
|
||||
<string name="revanced_hide_comments_by_members_header_title">\'Üzvlərin şərhləri\' başlığını gizlət</string>
|
||||
<string name="revanced_hide_comments_by_members_header_summary_on">Üzvlərin şərhləri başlığı gizlidir</string>
|
||||
<string name="revanced_hide_comments_by_members_header_summary_off">Üzvlərin şərhləri başlığı görünür</string>
|
||||
@ -423,6 +432,9 @@ Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
|
||||
<string name="revanced_disable_precise_seeking_gesture_summary_off">Jest aktivləşdirilib</string>
|
||||
</patch>
|
||||
<patch id="interaction.seekbar.enableSeekbarTappingPatch">
|
||||
<string name="revanced_seekbar_tapping_title">Axtarmaq üçün toxun\'u aktivləşdir</string>
|
||||
<string name="revanced_seekbar_tapping_summary_on">Axtarmaq üçün toxun, aktivdir</string>
|
||||
<string name="revanced_seekbar_tapping_summary_off">Axtarmaq üçün toxun qapalıdır</string>
|
||||
</patch>
|
||||
<patch id="interaction.swipecontrols.swipeControlsResourcePatch">
|
||||
<string name="revanced_swipe_brightness_title">Parlaqlıq jestini aktivləşdir</string>
|
||||
@ -435,6 +447,9 @@ Ekranın sol tərəfində dikinə sürüşdürərək parlaqlığı tənzimləyin
|
||||
|
||||
Ekranın sağ tərəfində düzünə sürüşdürərək səs səviyyəsini tənzimlə"</string>
|
||||
<string name="revanced_swipe_volume_summary_off">Tam ekran səs sürüşdürməsi qapalıdır</string>
|
||||
<string name="revanced_swipe_press_to_engage_title">Sürüşdürmə jesti üçün sıxmanı aktiv et</string>
|
||||
<string name="revanced_swipe_press_to_engage_summary_on">Sürüşdürmə üçün sıxma aktivdir</string>
|
||||
<string name="revanced_swipe_press_to_engage_summary_off">Sürüşdürmə üçün sıxma qapalıdır</string>
|
||||
<string name="revanced_swipe_haptic_feedback_title">Əks-əlaqə reaksiyasını aktivləşdir</string>
|
||||
<string name="revanced_swipe_haptic_feedback_summary_on">Əks-əlaqə reaksiyası aktivləşdirilib</string>
|
||||
<string name="revanced_swipe_haptic_feedback_summary_off">Əks-əlaqə reaksiyası qeyri-aktivdir</string>
|
||||
@ -542,6 +557,7 @@ Bu seçimi dəyişdirmə işə düşmürsə, Gizli rejimə keçməyə çalışı
|
||||
<string name="revanced_disable_translucent_status_bar_title">Yarımşəffaf mövqe cizgisin qapat</string>
|
||||
<string name="revanced_disable_translucent_status_bar_summary_on">Mövqe cizgisi qeyri-şəffafdır</string>
|
||||
<string name="revanced_disable_translucent_status_bar_summary_off">Mövqe cizgisi qeyri-şəffaf və ya şəffafdır</string>
|
||||
<string name="revanced_disable_translucent_status_bar_user_dialog_message">Bəzi cihazlarda bu xüsusiyyət aktivləşməsi sistem fəaliyyət cərgəsini şəffaf-a dəyişə bilər.</string>
|
||||
<string name="revanced_disable_translucent_navigation_bar_light_title">Açıq temada şəffaf cizgini qapat</string>
|
||||
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">İşıqlı rejim fəaliyyət cərgəsi qeyri-şəffafdır</string>
|
||||
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">İşıqlı rejim fəaliyyət cərgəsi qeyri-şəffaf və ya şəffafdır</string>
|
||||
@ -596,6 +612,7 @@ Bu seçimi dəyişdirmə işə düşmürsə, Gizli rejimə keçməyə çalışı
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Səs trekini gizlət</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Səs axını menyusu gizlidir</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Səs axını menyusu göstərilir</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">\"VR-da İzləni\" gizlət</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">VR menyusunda izləmə gizlidir</string>
|
||||
@ -1140,6 +1157,7 @@ Sonradan qapadılarsa, UI səhvlərin önləmək üçün tətbiq məlumatların
|
||||
<string name="revanced_miniplayer_type_entry_4">Müasir 1</string>
|
||||
<string name="revanced_miniplayer_type_entry_5">Müasir 2</string>
|
||||
<string name="revanced_miniplayer_type_entry_6">Müasir 3</string>
|
||||
<string name="revanced_miniplayer_type_entry_7">Müasir 4</string>
|
||||
<string name="revanced_miniplayer_rounded_corners_title">Dairəvi küncləri aktivləşdir</string>
|
||||
<string name="revanced_miniplayer_rounded_corners_summary_on">Künclər dairəvidir</string>
|
||||
<string name="revanced_miniplayer_rounded_corners_summary_off">Künclər kvadratdır</string>
|
||||
@ -1159,6 +1177,14 @@ Kiçik oynadıcı ekranın istənilən küncünə sürüklənə bilər"</string>
|
||||
|
||||
Kiçik oynadıcı ekrandan sola və ya sağa sürüklənə bilər"</string>
|
||||
<string name="revanced_miniplayer_horizontal_drag_summary_off">Üfüqi sürükləmə jesti qapatıldı</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Örtük düymələrini gizlət</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Örtük düymələri gizlidir</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Örtük düymələri görünür</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Genişləndir və bağla düymələrini gizlət</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Düymələr gizlidir
|
||||
|
||||
Genişləndirmək və ya bağlamaq üçün sürüşdür"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">Genişləndir və bağla düymələri göstərilir</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_title">Alt mətnləri gizlət</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_on">Alt mətnlər gizlədilir</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_off">Alt mətnlər göstərilir</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Налады ReVanced скінуты да стандартных</string>
|
||||
<string name="revanced_settings_import_success">Імпартавана %d налад</string>
|
||||
<string name="revanced_settings_import_failure_parse">Памылка імпарту: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Паказваць значкі налад ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Значкі налад паказваюцца</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Значкі налад не паказваюцца</string>
|
||||
<string name="revanced_language_title">Мова ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Пераклады для некаторых моў могуць быць адсутнымі або няпоўнымі.
|
||||
|
||||
@ -610,6 +612,10 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Схаваць гукавую дарожку</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Меню гукавой дарожкі схавана</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Адлюструецца меню гукавой дарожкі</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Меню аўдыядарожкі схавана
|
||||
|
||||
Каб паказаць меню аўдыядарожкі, змяніце \"Падробка відэаструменяў\" на iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Схаваць гадзіннік у VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Меню прагляду ў VR схавана</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Настройките на ReVanced бяха нулирани</string>
|
||||
<string name="revanced_settings_import_success">Следните настройки бяха импортирани успешно: %d</string>
|
||||
<string name="revanced_settings_import_failure_parse">Импортирането беше неуспешно: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Показване на иконите на настройките на ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Иконите на настройките се показват</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Иконите на настройките не се показват</string>
|
||||
<string name="revanced_language_title">Език на ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Преводите на някои езици може да липсват или да са непълни.
|
||||
|
||||
@ -610,6 +612,10 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Избор на Аудио</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Менюто за избор на Аудио е скрито</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Менюто за избор на Аудио се показва</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Менюто за аудио тракове е скрито
|
||||
|
||||
За да покажете менюто за аудио тракове, променете \"Подмяна на видео потоци\" на iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Гледайте във VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Менюто за гледане в VR е скрито</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">ReVanced সেটিং ডিফল্ট সেট করা হয়েছে</string>
|
||||
<string name="revanced_settings_import_success">%d সেটিং আমদানি হয়েছে</string>
|
||||
<string name="revanced_settings_import_failure_parse">আমদানি করা যায়নি: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">ReVanced সেটিং আইকন দেখান</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">সেটিং আইকন দেখানো হয়েছে</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">সেটিং আইকন দেখানো হচ্ছে না</string>
|
||||
<string name="revanced_language_title">ReVanced ভাষা</string>
|
||||
<string name="revanced_language_user_dialog_message">"কিছু ভাষার জন্য অনুবাদ অনুপস্থিত বা অসম্পূর্ণ হতে পারে।
|
||||
|
||||
@ -610,6 +612,10 @@ MicroG-এর জন্য ব্যাটারি অপ্টিমাইজ
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">অডিও ট্র্যাক লুকান</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">অডিও ট্র্যাক মেনু লুকানো আছে</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">অডিও ট্র্যাক মেনু দেখানো হয়</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"অডিও ট্র্যাক মেনু লুকানো আছে
|
||||
|
||||
অডিও ট্র্যাক মেনু দেখাতে, 'স্পুফ ভিডিও স্ট্রিম' পরিবর্তন করে iOS TV করুন"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">ভিআর-এ ঘড়ি লুকান</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">ভিআর মেনুতে দেখুন লুকানো আছে</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">La configuració de ReVanced s\'ha restablert als valors predeterminats</string>
|
||||
<string name="revanced_settings_import_success">S\'han importat %d configuracions</string>
|
||||
<string name="revanced_settings_import_failure_parse">No s\'ha pogut importar: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Mostra les icones de configuració de ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Es mostren les icones de configuració</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">No es mostren les icones de configuració</string>
|
||||
<string name="revanced_language_title">Llenguatge de ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Les traduccions per a algunes llengües poden faltar o ser incompletes.
|
||||
|
||||
@ -610,6 +612,10 @@ Si canviar aquesta opció no té cap efecte, prova a canviar al mode d'incògnit
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Amaga la pista d\'àudio</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">El menú de la pista d\'àudio s\'amaga</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Es mostra el menú de la pista d\'àudio</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"El menú de la pista d'àudio està amagat
|
||||
|
||||
Per mostrar el menú de la pista d'àudio, canvieu \"Suplanta els fluxos de vídeo\" a iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Amaga Mira en VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">El menú Mira en VR s\'amaga</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Nastavení ReVanced obnoveno do výchozího stavu</string>
|
||||
<string name="revanced_settings_import_success">Importováno %d nastavení</string>
|
||||
<string name="revanced_settings_import_failure_parse">Importováni selhalo: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Zobrazit ikony nastavení ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Ikony nastavení se zobrazují</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Ikony nastavení se nezobrazují</string>
|
||||
<string name="revanced_language_title">Jazyk ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Překlady pro některé jazyky mohou chybět nebo být neúplné.
|
||||
|
||||
@ -610,6 +612,10 @@ Pokud změna tohoto nastavení nemá žádný účinek, zkuste přepnout do rež
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Skrýt Zvuková stopa</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Menu Zvuková stopa je skryto</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Menu Zvuková stopa je zobrazeno</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Nabídka zvukové stopy je skrytá.
|
||||
|
||||
Chcete-li zobrazit nabídku zvukové stopy, změňte možnost „Zfalšovat streamy videa“ na iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Skrýt Sledovat ve VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Menu Sledovat ve VR je skryto</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">ReVanced-indstillinger nulstillet til standard</string>
|
||||
<string name="revanced_settings_import_success">%d indstillinger importeret</string>
|
||||
<string name="revanced_settings_import_failure_parse">Import mislykkedes: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Vis ReVanced-indstillingsikoner</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Indstillingsikoner vises</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Indstillingsikoner vises ikke</string>
|
||||
<string name="revanced_language_title">ReVanced-sprog</string>
|
||||
<string name="revanced_language_user_dialog_message">"Oversættelser for nogle sprog mangler muligvis eller er ufuldstændige.
|
||||
|
||||
@ -573,6 +575,10 @@ Hvis ændring af denne indstilling ikke træder i kraft, kan du prøve at skifte
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Skjul lydspor</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Menuen for lydspor er skjult</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Menuen Lydspor vises</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Lydspormenuen er skjult
|
||||
|
||||
For at vise lydspormenuen skal du ændre \"Spoof videostream\" til iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Skjul vagt i VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Se i VR-menuen er skjult</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">ReVanced-Einstellungen auf Standardwerte zurückgesetzt</string>
|
||||
<string name="revanced_settings_import_success">%d Einstellungen importiert</string>
|
||||
<string name="revanced_settings_import_failure_parse">Import fehlgeschlagen: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">ReVanced-Einstellungssymbole anzeigen</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Einstellungssymbole werden angezeigt</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Einstellungssymbole werden nicht angezeigt</string>
|
||||
<string name="revanced_language_title">ReVanced-Sprache</string>
|
||||
<string name="revanced_language_user_dialog_message">"Übersetzungen für einige Sprachen fehlen möglicherweise oder sind unvollständig.
|
||||
|
||||
@ -603,6 +605,10 @@ Wenn diese Änderung nicht wirksam wird, versuchen Sie, in den Inkognito-Modus z
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Audiospur ausblenden</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Audiospur-Menü ist ausgeblendet</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Audiospurmenü wird angezeigt</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Das Audiotrack-Menü ist ausgeblendet.
|
||||
|
||||
Um das Audiotrack-Menü anzuzeigen, ändere \"Video-Streams fälschen\" zu iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Überwachung in VR ausblenden</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Im VR-Menü beobachten ist ausgeblendet</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Επαναφέρθηκαν οι προεπιλεγμένες ρυθμίσεις ReVanced</string>
|
||||
<string name="revanced_settings_import_success">Έγινε εισαγωγή %d ρυθμίσεων</string>
|
||||
<string name="revanced_settings_import_failure_parse">Η εισαγωγή απέτυχε: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Εμφάνιση εικονιδίων στις ρυθμίσεις ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Τα εικονίδια ρυθμίσεων εμφανίζονται</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Τα εικονίδια ρυθμίσεων δεν εμφανίζονται</string>
|
||||
<string name="revanced_language_title">Γλώσσα ρυθμίσεων ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Οι μεταφράσεις για κάποιες γλώσσες ενδέχεται να λείπουν ή να είναι ελλιπείς.
|
||||
|
||||
@ -612,6 +614,10 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Μενού «Κομμάτι ήχου»</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Κρυμμένο</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Εμφανίζεται</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Το μενού «Κομμάτι ήχου» είναι κρυμμένο
|
||||
|
||||
Για να εμφανίζεται το μενού κομματιού ήχου, αλλάξτε την «Παραποίηση ροών βίντεο» σε iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Μενού «Προβολή σε VR»</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Κρυμμένο</string>
|
||||
@ -1180,10 +1186,10 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Κρυμμένα</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Εμφανίζονται</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Κουμπιά επέκτασης και κλεισίματος</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Τα κουμπιά δεν εμφανίζονται
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Κρυμμένα
|
||||
|
||||
Σύρετε για να αναπτύξετε ή να κλείσετε"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">Τα κουμπιά εμφανίζονται</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">Εμφανίζονται</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_title">Κείμενα οθόνης αναπαραγωγής</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_on">Κρυμμένα</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_off">Εμφανίζονται</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Configuración ReVanced restablecida por defecto</string>
|
||||
<string name="revanced_settings_import_success">Configuración importada de %d</string>
|
||||
<string name="revanced_settings_import_failure_parse">Error de importación: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Mostrar iconos de configuración de ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Se muestran los iconos de configuración</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">No se muestran los iconos de configuración</string>
|
||||
<string name="revanced_language_title">Idioma de ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Las traducciones para algunos idiomas pueden faltar o estar incompletas.
|
||||
|
||||
@ -607,6 +609,10 @@ Si cambiar este ajuste no tiene efecto, intenta cambiar al modo incógnito."</st
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Ocultar pista de audio</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">El menú de pista de audio está oculto</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">El menú de pista de audio se muestra</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"El menú de la pista de audio está oculto.
|
||||
|
||||
Para mostrar el menú de la pista de audio, cambia \"Suplantar transmisiones de video\" a iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Ocultar reloj en VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Ver en el menú VR está oculto</string>
|
||||
@ -699,7 +705,7 @@ Si cambiar este ajuste no tiene efecto, intenta cambiar al modo incógnito."</st
|
||||
<string name="revanced_hide_shorts_location_label_summary_on">Etiqueta de ubicación oculta</string>
|
||||
<string name="revanced_hide_shorts_location_label_summary_off">Etiqueta de ubicación mostrada</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_title">Ocultar el botón Guardar música</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_on">Guardar botón de música está oculto</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_on">El botón Guardar música está oculto</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_off">Mostrar el botón de guardar música</string>
|
||||
<string name="revanced_hide_shorts_use_template_button_title">Ocultar el botón Usar plantilla</string>
|
||||
<string name="revanced_hide_shorts_use_template_button_summary_on">Botón de plantilla de uso está oculto</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">ReVancedi seaded on lähtestatud</string>
|
||||
<string name="revanced_settings_import_success">Imporditi %d seadet</string>
|
||||
<string name="revanced_settings_import_failure_parse">Importimine ebaõnnestus: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Näita ReVancedi seadete ikoone</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Seadete ikoonid on näidatud</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Seadete ikoonid ei ole nähtavad</string>
|
||||
<string name="revanced_language_title">Revancedi keel</string>
|
||||
<string name="revanced_language_user_dialog_message">"Mõnede keelte tõlked võivad olla puudulikud või ebatäielikud.
|
||||
|
||||
@ -610,6 +612,10 @@ Kui selle sätte muutmine ei avalda mõju, proovige lülituda Inkognito režiimi
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Peida Helitraek</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Helitraekide menüü on peidetud</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Helitraekide menüü on nähtav</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Heliriba menüü on peidetud
|
||||
|
||||
Heliriba menüü kuvamiseks muutke valikut „Võltsitud videovoogedastus“ väärtuseks iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Peida Vaata VR-is</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Vaata VR-is menüü on peidetud</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -127,6 +126,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Revanced-asetukset nollattiin</string>
|
||||
<string name="revanced_settings_import_success">%d asetusta tuotiin</string>
|
||||
<string name="revanced_settings_import_failure_parse">Tuonti epäonnistui: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Näytä ReVanced-asetuskuvakkeet</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Asetuskuvakkeet näytetään</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Asetuskuvakkeita ei näytetä</string>
|
||||
<string name="revanced_language_title">ReVancedin kieli</string>
|
||||
<string name="revanced_language_user_dialog_message">"Joidenkin kielten käännökset saattavat puuttua tai olla puutteellisia.
|
||||
|
||||
@ -226,6 +228,9 @@ Et saa ilmoituksia odottamattomista tapahtumista."</string>
|
||||
<string name="revanced_hide_artist_cards_title">Piilota artistikortit</string>
|
||||
<string name="revanced_hide_artist_cards_summary_on">Artistikortit on piilotettu</string>
|
||||
<string name="revanced_hide_artist_cards_summary_off">Artistikortit näytetään</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_title">Piilota \"tekoälyn luoma videoyhteenveto\"</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Videon yhteenveto-osio on piilotettu</string>
|
||||
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Videon yhteenveto-osio näytetään</string>
|
||||
<string name="revanced_hide_attributes_section_title">Piilota Määritteet</string>
|
||||
<string name="revanced_hide_attributes_section_summary_on">Esitellyt paikat, Pelit, Musiikki ja Mainitut ihmiset -osiot on piilotettu</string>
|
||||
<string name="revanced_hide_attributes_section_summary_off">Esitellyt paikat, Pelit, Musiikki ja Mainitut ihmiset -osiot näytetään</string>
|
||||
@ -262,8 +267,12 @@ Et saa ilmoituksia odottamattomista tapahtumista."</string>
|
||||
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Näytetään liittyvissä videoissa</string>
|
||||
<string name="revanced_comments_screen_title">Kommentit</string>
|
||||
<string name="revanced_comments_screen_summary">Piilota tai näytä kommenttiosion osia</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_title">Piilota tekoälyn luoma chat-yhteenveto</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_summary_on">Chat-yhteenveto on piilotettu</string>
|
||||
<string name="revanced_hide_comments_ai_chat_summary_summary_off">Chat-yhteenveto näytetään</string>
|
||||
<string name="revanced_hide_comments_ai_summary_title">Piilota tekoälyn luoma kommenttiyhteenveto</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_on">Kommenttien yhteenveto on piilotettu</string>
|
||||
<string name="revanced_hide_comments_ai_summary_summary_off">Kommenttien yhteenveto näytetään</string>
|
||||
<string name="revanced_hide_comments_by_members_header_title">Piilota \"Jäsenten kommentit\" -ylätunniste</string>
|
||||
<string name="revanced_hide_comments_by_members_header_summary_on">Jäsenten kommentit -ylätunniste on piilotettu</string>
|
||||
<string name="revanced_hide_comments_by_members_header_summary_off">Jäsenten kommentit -ylätunniste näytetään</string>
|
||||
@ -423,6 +432,9 @@ Tämä ominaisuus on käytettävissä vain vanhemmilla laitteilla"</string>
|
||||
<string name="revanced_disable_precise_seeking_gesture_summary_off">Ele on käytössä</string>
|
||||
</patch>
|
||||
<patch id="interaction.seekbar.enableSeekbarTappingPatch">
|
||||
<string name="revanced_seekbar_tapping_title">Ota kelaus napauttamalla käyttöön</string>
|
||||
<string name="revanced_seekbar_tapping_summary_on">Kelaus napauttamalla on käytössä</string>
|
||||
<string name="revanced_seekbar_tapping_summary_off">Kelaus napauttamalla ei ole käytössä</string>
|
||||
</patch>
|
||||
<patch id="interaction.swipecontrols.swipeControlsResourcePatch">
|
||||
<string name="revanced_swipe_brightness_title">Ota kirkkauden ele käyttöön</string>
|
||||
@ -435,6 +447,9 @@ Säädä kirkkautta pyyhkäisemällä pystysuoraan näytön vasemmalla puolella"
|
||||
|
||||
Säädä äänenvoimakkuutta pyyhkäisemällä pystysuoraan näytön oikealta puolella"</string>
|
||||
<string name="revanced_swipe_volume_summary_off">Koko näytön äänenvoimakkuuden pyyhkäisy ei ole käytössä</string>
|
||||
<string name="revanced_swipe_press_to_engage_title">Ota pyyhkäise painamalla -ele käyttöön</string>
|
||||
<string name="revanced_swipe_press_to_engage_summary_on">Pyyhkäise painamalla -ele on käytössä</string>
|
||||
<string name="revanced_swipe_press_to_engage_summary_off">Pyyhkäise painamalla -ele ei ole käytössä</string>
|
||||
<string name="revanced_swipe_haptic_feedback_title">Ota haptinen palaute käyttöön</string>
|
||||
<string name="revanced_swipe_haptic_feedback_summary_on">Haptinen palaute on käytössä</string>
|
||||
<string name="revanced_swipe_haptic_feedback_summary_off">Haptinen palaute ei ole käytössä</string>
|
||||
@ -542,6 +557,7 @@ Jos tämän asetuksen muuttaminen ei tule voimaan, kokeile vaihtaa Incognito-til
|
||||
<string name="revanced_disable_translucent_status_bar_title">Poista läpikuultava tilapalkki käytöstä</string>
|
||||
<string name="revanced_disable_translucent_status_bar_summary_on">Tilapalkki on läpinäkymätön</string>
|
||||
<string name="revanced_disable_translucent_status_bar_summary_off">Tilapalkki on läpinäkymätön tai läpikuultava</string>
|
||||
<string name="revanced_disable_translucent_status_bar_user_dialog_message">Joillakin laitteilla tämän ominaisuuden käyttöönotto voi muuttaa järjestelmän navigointipalkin läpinäkyväksi.</string>
|
||||
<string name="revanced_disable_translucent_navigation_bar_light_title">Poista vaalea läpikuultava palkki käytöstä</string>
|
||||
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">Vaalean tilan navigointipalkki on läpinäkymätön</string>
|
||||
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">Vaalean tilan navigointipalkki on läpinäkymätön tai läpikuultava</string>
|
||||
@ -596,6 +612,7 @@ Jos tämän asetuksen muuttaminen ei tule voimaan, kokeile vaihtaa Incognito-til
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Piilota Ääniraita</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Ääniraitavalikko on piilotettu</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Ääniraitavalikko näytetään</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Piilota Katso VR-tilassa</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Katso VR-tilassa -valinta on piilotettu</string>
|
||||
@ -1161,6 +1178,9 @@ Minisoitin voidaan vetää mihin tahansa näytön kulmaan"</string>
|
||||
|
||||
Minisoitin voidaan vetää pois näytöltä vasemmalle tai oikealle"</string>
|
||||
<string name="revanced_miniplayer_horizontal_drag_summary_off">Vaakasuuntainen vetoele ei ole käytössä</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Piilota peittokuvan painikkeet</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Peittokuvan painikkeet on piilotettu</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Peittokuvan painikkeet näytetään</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Piilota laajenna- ja sulje-painikkeet</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Painikkeet piilotetaan
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">I-reset ang mga ReVanced na setting sa default</string>
|
||||
<string name="revanced_settings_import_success">Na-import ang %d na mga setting</string>
|
||||
<string name="revanced_settings_import_failure_parse">Nabigo ang pag-import: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Ipakita ang mga icon ng setting ng ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Ipinapakita ang mga icon ng setting</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Hindi ipinapakita ang mga icon ng setting</string>
|
||||
<string name="revanced_language_title">Wika ng ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Ang mga pagsasalin para sa ilang mga wika ay maaaring nawawala o hindi kumpleto.
|
||||
|
||||
@ -608,6 +610,10 @@ Tandaan: Ang pagpapagana nito ay nagtatago rin ng mga ad ng video"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Itago ang Audio track</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Nakatago ang menu ng audio track</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Ipinapakita ang menu ng audio track</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Nakatago ang menu ng audio track
|
||||
|
||||
Upang ipakita ang menu ng Audio track, baguhin ang 'Spoof video streams' sa iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Itago ang Panoorin sa VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Nakatago ang panonood sa VR menu</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Paramètres ReVanced réinitialisés aux valeurs par défaut</string>
|
||||
<string name="revanced_settings_import_success">%d paramètres importés</string>
|
||||
<string name="revanced_settings_import_failure_parse">Importation échouée : %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Afficher les icônes des paramètres ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Les icônes des paramètres sont affichées</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Les icônes des paramètres ne sont pas affichées</string>
|
||||
<string name="revanced_language_title">Langue de ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Il se peut que les traductions dans certaines langues soient manquantes ou incomplètes.
|
||||
|
||||
@ -610,6 +612,10 @@ Si la modification de ce paramètre ne prend pas effet, essayez de passer en mod
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Masquer \"Piste audio\"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Le menu Piste audio est masqué</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Le menu Piste audio est affiché</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Le menu Piste audio est masqué
|
||||
|
||||
Pour afficher le menu Piste audio, définissez \"Falsifier les flux vidéo\" sur iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Masquer \"Regarder en RV\"</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Le menu Regarder en RV est masqué</string>
|
||||
@ -1174,13 +1180,13 @@ Le lecteur réduit peut être déplacé vers n'importe quel coin de l'écran"</s
|
||||
|
||||
Le lecteur réduit peut être déplacé hors de l'écran, à gauche comme à droite"</string>
|
||||
<string name="revanced_miniplayer_horizontal_drag_summary_off">Geste de déplacement horizontal désactivé</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Masquer les boutons de superposition</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Les boutons de superposition sont masqués</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Les boutons de superposition sont affichés</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Masquer les boutons en superposition</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Les boutons en superposition sont masqués</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Les boutons en superposition sont affichés</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Masquer les boutons Agrandir et Fermer</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Les boutons sont masqués
|
||||
|
||||
Balayez pour développer ou fermer"</string>
|
||||
Balayez pour agrandir ou fermer"</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_off">Les boutons Agrandir et Fermer sont affichés</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_title">Masquer les sous-textes</string>
|
||||
<string name="revanced_miniplayer_hide_subtext_summary_on">Les sous-textes sont masqués</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Athshocraigh socruithe ReVanced go réamhshocrú</string>
|
||||
<string name="revanced_settings_import_success">Iompórtáladh %d socruithe</string>
|
||||
<string name="revanced_settings_import_failure_parse">Theip ar allmhairiú: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Taispeáin deilbhíní socruithe ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Taispeántar deilbhíní socruithe</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Ní thaispeántar deilbhíní socraithe</string>
|
||||
<string name="revanced_language_title">Teanga ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"D'fhéadfadh aistriúcháin do roinnt teangacha a bheith ar iarraidh nó mí-iomlán.
|
||||
|
||||
@ -610,6 +612,10 @@ Mura dtagann aon athrú ar an socrú seo, bain triail as mód Incognito a chur a
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Folaigh Rian Fuaime</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Tá roghchlár rian fuaime i bhfolach</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Taispeántar roghchlár rian fuaime</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Tá roghchlár na rian fuaime i bhfolach
|
||||
|
||||
Chun roghchlár na rian fuaime a thaispeáint, athraigh 'Srutháin físeáin bhréige' go iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Folaigh Watch i VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Tá faire i roghchlár VR i bhfolach</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">A ReVanced beállítások visszaállnak az alapértelmezettre</string>
|
||||
<string name="revanced_settings_import_success">%d beállítás importálva</string>
|
||||
<string name="revanced_settings_import_failure_parse">Sikertelen importálás: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">ReVanced beállításikonok megjelenítése</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">A beállításikonok láthatók</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">A beállítások ikonjai nem jelennek meg</string>
|
||||
<string name="revanced_language_title">ReVanced nyelve</string>
|
||||
<string name="revanced_language_user_dialog_message">"A fordítások hiányozhatnak vagy hiányosak lehetnek néhány nyelven.
|
||||
|
||||
@ -610,6 +612,10 @@ Ha a beállítás módosítása nem lép életbe, próbáljon meg Inkognitó mó
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Hangsáv elrejtése</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">A hangsáv menü el van rejtve</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">A hangsáv menü megjelenik</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Az audiosáv menü rejtett
|
||||
|
||||
Az audiosáv menü megjelenítéséhez módosítsa a \"Videófolyamok hamisítása\" beállítást iOS TV-re"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">\"Megtekintés VR-módban\" elrejtése</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">A megtekintés VR-módban menü el van rejtve</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">ReVanced կարգավորումները վերադրվել են դեֆոլտային</string>
|
||||
<string name="revanced_settings_import_success">Import %d կարգավորում</string>
|
||||
<string name="revanced_settings_import_failure_parse">Import-ը ձախողվել է։ %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Ցուցադրել ReVanced-ի կարգավորումների պատկերակները</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Կարգավորումների պատկերակները ցուցադրվում են</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Կարգավորումների պատկերակները ցուցադրված չեն</string>
|
||||
<string name="revanced_language_title">ReVanced լեզվի</string>
|
||||
<string name="revanced_language_user_dialog_message">"Որոշ լեզուների թարգմանությունները կարող են լինել բացակայուն կամ անավարտ:
|
||||
|
||||
@ -610,6 +612,10 @@ MicroG-ի համար մարտկոցի օպտիմալացումը անջատել
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Աուդիո ձայնագրությունը թաքցնել</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Աուդիո ձայնագրման մենյուը թաքցված է</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Աուդիո ձայնագրման մենյուը երևում է</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Աուդիո ուղու ընտրացանկը թաքնված է:
|
||||
|
||||
Աուդիո ուղու ընտրացանկը ցուցադրելու համար փոխեք «Կեղծել տեսահոսքերը»-ը iOS TV-ի"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Դիտել VR-ով թաքցնել</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">VR-ով դիտել մենյուը թաքցված է</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Pengaturan ReVanced diatur ke setelan awal</string>
|
||||
<string name="revanced_settings_import_success">Mengimpor setelan %d</string>
|
||||
<string name="revanced_settings_import_failure_parse">Impor gagal: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Tampilkan ikon pengaturan ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Ikon pengaturan ditampilkan</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Ikon pengaturan tidak ditampilkan</string>
|
||||
<string name="revanced_language_title">Bahasa ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Terjemahan untuk beberapa bahasa mungkin hilang atau tidak lengkap.
|
||||
|
||||
@ -610,6 +612,10 @@ Jika mengubah setelan ini tidak berpengaruh, coba beralih ke mode Penyamaran."</
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Sembunyikan trek Audio</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Menu trek audio disembunyikan</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Menu trek audio ditampilkan</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Menu trek audio disembunyikan
|
||||
|
||||
Untuk menampilkan menu trek Audio, ubah 'Spoof aliran video' ke iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Sembunyikan Tonton di VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Menu tonton di VR disembunyikan</string>
|
||||
@ -1174,9 +1180,9 @@ Miniplayer dapat diseret ke sudut layar mana pun"</string>
|
||||
|
||||
Miniplayer dapat diseret keluar layar ke kiri atau kanan"</string>
|
||||
<string name="revanced_miniplayer_horizontal_drag_summary_off">Gerakan seret horizontal dinonaktifkan</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Sembunyikan tombol overlay</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Tombol overlay disembunyikan</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Tombol overlay ditampilkan</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_title">Sembunyikan tombol hamparan</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_on">Tombol hamparan disembunyikan</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_summary_off">Tombol hamparan ditampilkan</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_title">Sembunyikan tombol perluas dan tutup</string>
|
||||
<string name="revanced_miniplayer_hide_overlay_buttons_legacy_summary_on">"Tombol disembunyikan
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -107,6 +106,7 @@ Second \"item\" text"</string>
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
</patch>
|
||||
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -43,6 +42,9 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_settings_import_reset">Reimposta le impostazioni di ReVanced a quelle predefinite</string>
|
||||
<string name="revanced_settings_import_success">Importate %d impostazioni</string>
|
||||
<string name="revanced_settings_import_failure_parse">Importazione non riuscita: %s</string>
|
||||
<string name="revanced_show_menu_icons_title">Mostra le icone delle impostazioni di ReVanced</string>
|
||||
<string name="revanced_show_menu_icons_summary_on">Le icone delle impostazioni vengono mostrate</string>
|
||||
<string name="revanced_show_menu_icons_summary_off">Le icone delle impostazioni non vengono mostrate</string>
|
||||
<string name="revanced_language_title">Lingua di ReVanced</string>
|
||||
<string name="revanced_language_user_dialog_message">"Le traduzioni per alcune lingue potrebbero essere mancanti o incomplete.
|
||||
|
||||
@ -610,6 +612,10 @@ Se la modifica di questa impostazione non ha effetto, prova a passare alla modal
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">Nascondi Traccia audio</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">Il menu Traccia audio è nascosto</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">Il menu Traccia audio è visibile</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<string name="revanced_hide_player_flyout_audio_track_not_available">"Il menu della traccia audio è nascosto
|
||||
|
||||
Per mostrare il menu della traccia audio, cambia \"Spoof video streams\" in iOS TV"</string>
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">Nascondi Guarda in VR</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Il menu Guarda in VR è nascosto</string>
|
||||
|
@ -4,7 +4,6 @@
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
|
||||
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
|
||||
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
|
||||
|
||||
@ -596,6 +595,7 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_title">הסתר טראק אודיו</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_on">תפריט טראק אודיו מוסתר</string>
|
||||
<string name="revanced_hide_player_flyout_audio_track_summary_off">תפריט טראק אודיו מוצג</string>
|
||||
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
|
||||
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_title">הסתר \'צפה ב-VR\'</string>
|
||||
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">תפריט \'צפה ב-VR\' מוסתר</string>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user