mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-24 11:11:03 +00:00
Compare commits
7 Commits
v5.48.1-de
...
v5.50.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97e74157fa | ||
|
|
5edd9dccae | ||
|
|
9c18e1e649 | ||
|
|
71ce8230a9 | ||
|
|
156441d3cf | ||
|
|
634f47ef84 | ||
|
|
eeb133325e |
7
.github/workflows/push_strings.yml
vendored
7
.github/workflows/push_strings.yml
vendored
@@ -16,10 +16,11 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Preprocess strings
|
- name: Process strings
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}
|
||||||
run: ./gradlew clean preprocessCrowdinStrings
|
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: ./gradlew processStringsForCrowdin
|
||||||
|
|
||||||
- name: Push strings
|
- name: Push strings
|
||||||
uses: crowdin/github-action@v2
|
uses: crowdin/github-action@v2
|
||||||
|
|||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
|||||||
|
# [5.50.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.0...v5.50.0-dev.1) (2026-01-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube Music:** Add `Unlock Android Auto Media Browser` patch ([#6477](https://github.com/ReVanced/revanced-patches/issues/6477)) ([89645dc](https://github.com/ReVanced/revanced-patches/commit/89645dcc2e13603b8f2fedb5e16231cb396e5965))
|
||||||
|
|
||||||
|
# [5.49.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.1-dev.1...v5.49.0-dev.1) (2026-01-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube Music:** Add `Hide layout components` patch ([#6365](https://github.com/ReVanced/revanced-patches/issues/6365)) ([71ce823](https://github.com/ReVanced/revanced-patches/commit/71ce8230a959dcaf2d8cd5dad1a4f21b88819aa0))
|
||||||
|
|
||||||
## [5.48.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.0...v5.48.1-dev.1) (2026-01-21)
|
## [5.48.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.48.0...v5.48.1-dev.1) (2026-01-21)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import static java.lang.Boolean.FALSE;
|
|||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
import app.revanced.extension.shared.settings.EnumSetting;
|
import app.revanced.extension.shared.settings.EnumSetting;
|
||||||
import app.revanced.extension.shared.spoof.ClientType;
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
public class Settings extends BaseSettings {
|
public class Settings extends YouTubeAndMusicSettings {
|
||||||
|
|
||||||
// Ads
|
// Ads
|
||||||
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true);
|
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.shared.patches.components;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
@@ -15,13 +15,15 @@ import java.util.regex.Pattern;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
|
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows custom filtering using a path and optionally a proto buffer string.
|
* Allows custom filtering using a path and optionally a proto buffer string.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final class CustomFilter extends Filter {
|
public final class CustomFilter extends Filter {
|
||||||
|
|
||||||
private static void showInvalidSyntaxToast(@NonNull String expression) {
|
private static void showInvalidSyntaxToast(@NonNull String expression) {
|
||||||
Utils.showToastLong(str("revanced_custom_filter_toast_invalid_syntax", expression));
|
Utils.showToastLong(str("revanced_custom_filter_toast_invalid_syntax", expression));
|
||||||
@@ -45,7 +47,7 @@ final class CustomFilter extends Filter {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
static Collection<CustomFilterGroup> parseCustomFilterGroups() {
|
static Collection<CustomFilterGroup> parseCustomFilterGroups() {
|
||||||
String rawCustomFilterText = Settings.CUSTOM_FILTER_STRINGS.get();
|
String rawCustomFilterText = YouTubeAndMusicSettings.CUSTOM_FILTER_STRINGS.get();
|
||||||
if (rawCustomFilterText.isBlank()) {
|
if (rawCustomFilterText.isBlank()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,7 @@ final class CustomFilter extends Filter {
|
|||||||
ByteTrieSearch bufferSearch;
|
ByteTrieSearch bufferSearch;
|
||||||
|
|
||||||
CustomFilterGroup(boolean startsWith, @NonNull String path) {
|
CustomFilterGroup(boolean startsWith, @NonNull String path) {
|
||||||
super(Settings.CUSTOM_FILTER, path);
|
super(YouTubeAndMusicSettings.CUSTOM_FILTER, path);
|
||||||
this.startsWith = startsWith;
|
this.startsWith = startsWith;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +147,7 @@ final class CustomFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
// All callbacks are custom filter groups.
|
// All callbacks are custom filter groups.
|
||||||
CustomFilterGroup custom = (CustomFilterGroup) matchedGroup;
|
CustomFilterGroup custom = (CustomFilterGroup) matchedGroup;
|
||||||
@@ -159,4 +161,4 @@ final class CustomFilter extends Filter {
|
|||||||
|
|
||||||
return custom.bufferSearch.matches(buffer);
|
return custom.bufferSearch.matches(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.shared.patches.litho;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters litho based components.
|
* Filters litho based components.
|
||||||
*
|
*
|
||||||
@@ -14,11 +17,11 @@ import java.util.List;
|
|||||||
* either an identifier or a path.
|
* either an identifier or a path.
|
||||||
* Then inside {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
* Then inside {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
||||||
* search for the buffer content using either a {@link ByteArrayFilterGroup} (if searching for 1 pattern)
|
* search for the buffer content using either a {@link ByteArrayFilterGroup} (if searching for 1 pattern)
|
||||||
* or a {@link ByteArrayFilterGroupList} (if searching for more than 1 pattern).
|
* or a {@link FilterGroupList.ByteArrayFilterGroupList} (if searching for more than 1 pattern).
|
||||||
*
|
*
|
||||||
* All callbacks must be registered before the constructor completes.
|
* All callbacks must be registered before the constructor completes.
|
||||||
*/
|
*/
|
||||||
abstract class Filter {
|
public abstract class Filter {
|
||||||
|
|
||||||
public enum FilterContentType {
|
public enum FilterContentType {
|
||||||
IDENTIFIER,
|
IDENTIFIER,
|
||||||
@@ -65,7 +68,7 @@ abstract class Filter {
|
|||||||
* @param contentIndex Matched index of the identifier or path.
|
* @param contentIndex Matched index of the identifier or path.
|
||||||
* @return True if the litho component should be filtered out.
|
* @return True if the litho component should be filtered out.
|
||||||
*/
|
*/
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
package app.revanced.extension.shared.patches.litho;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
|
|
||||||
|
public abstract class FilterGroup<T> {
|
||||||
|
public final static class FilterGroupResult {
|
||||||
|
private BooleanSetting setting;
|
||||||
|
private int matchedIndex;
|
||||||
|
private int matchedLength;
|
||||||
|
// In the future it might be useful to include which pattern matched,
|
||||||
|
// but for now that is not needed.
|
||||||
|
|
||||||
|
FilterGroupResult() {
|
||||||
|
this(null, -1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterGroupResult(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
||||||
|
setValues(setting, matchedIndex, matchedLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValues(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
||||||
|
this.setting = setting;
|
||||||
|
this.matchedIndex = matchedIndex;
|
||||||
|
this.matchedLength = matchedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A null value if the group has no setting,
|
||||||
|
* or if no match is returned from {@link FilterGroupList#check(Object)}.
|
||||||
|
*/
|
||||||
|
public BooleanSetting getSetting() {
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFiltered() {
|
||||||
|
return matchedIndex >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matched index of first pattern that matched, or -1 if nothing matched.
|
||||||
|
*/
|
||||||
|
public int getMatchedIndex() {
|
||||||
|
return matchedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the matched filter pattern.
|
||||||
|
*/
|
||||||
|
public int getMatchedLength() {
|
||||||
|
return matchedLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final BooleanSetting setting;
|
||||||
|
protected final T[] filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new filter group.
|
||||||
|
*
|
||||||
|
* @param setting The associated setting.
|
||||||
|
* @param filters The filters.
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public FilterGroup(final BooleanSetting setting, final T... filters) {
|
||||||
|
this.setting = setting;
|
||||||
|
this.filters = filters;
|
||||||
|
if (filters.length == 0) {
|
||||||
|
throw new IllegalArgumentException("Must use one or more filter patterns (zero specified)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return setting == null || setting.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return If {@link FilterGroupList} should include this group when searching.
|
||||||
|
* By default, all filters are included except non enabled settings that require reboot.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
public boolean includeInSearch() {
|
||||||
|
return isEnabled() || !setting.rebootApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + ": " + (setting == null ? "(null setting)" : setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract FilterGroupResult check(final T stack);
|
||||||
|
|
||||||
|
|
||||||
|
public static class StringFilterGroup extends FilterGroup<String> {
|
||||||
|
|
||||||
|
public StringFilterGroup(final BooleanSetting setting, final String... filters) {
|
||||||
|
super(setting, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterGroupResult check(final String string) {
|
||||||
|
int matchedIndex = -1;
|
||||||
|
int matchedLength = 0;
|
||||||
|
if (isEnabled()) {
|
||||||
|
for (String pattern : filters) {
|
||||||
|
if (!string.isEmpty()) {
|
||||||
|
final int indexOf = string.indexOf(pattern);
|
||||||
|
if (indexOf >= 0) {
|
||||||
|
matchedIndex = indexOf;
|
||||||
|
matchedLength = pattern.length();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you have more than 1 filter patterns, then all instances of
|
||||||
|
* this class should filtered using {@link FilterGroupList.ByteArrayFilterGroupList#check(byte[])},
|
||||||
|
* which uses a prefix tree to give better performance.
|
||||||
|
*/
|
||||||
|
public static class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||||
|
|
||||||
|
private volatile int[][] failurePatterns;
|
||||||
|
|
||||||
|
// Modified implementation from https://stackoverflow.com/a/1507813
|
||||||
|
private static int indexOf(final byte[] data, final byte[] pattern, final int[] failure) {
|
||||||
|
// Finds the first occurrence of the pattern in the byte array using
|
||||||
|
// KMP matching algorithm.
|
||||||
|
int patternLength = pattern.length;
|
||||||
|
for (int i = 0, j = 0, dataLength = data.length; i < dataLength; i++) {
|
||||||
|
while (j > 0 && pattern[j] != data[i]) {
|
||||||
|
j = failure[j - 1];
|
||||||
|
}
|
||||||
|
if (pattern[j] == data[i]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j == patternLength) {
|
||||||
|
return i - patternLength + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] createFailurePattern(byte[] pattern) {
|
||||||
|
// Computes the failure function using a boot-strapping process,
|
||||||
|
// where the pattern is matched against itself.
|
||||||
|
final int patternLength = pattern.length;
|
||||||
|
final int[] failure = new int[patternLength];
|
||||||
|
|
||||||
|
for (int i = 1, j = 0; i < patternLength; i++) {
|
||||||
|
while (j > 0 && pattern[j] != pattern[i]) {
|
||||||
|
j = failure[j - 1];
|
||||||
|
}
|
||||||
|
if (pattern[j] == pattern[i]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
failure[i] = j;
|
||||||
|
}
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayFilterGroup(BooleanSetting setting, byte[]... filters) {
|
||||||
|
super(setting, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the Strings into byte arrays. Used to search for text in binary data.
|
||||||
|
*/
|
||||||
|
public ByteArrayFilterGroup(BooleanSetting setting, String... filters) {
|
||||||
|
super(setting, ByteTrieSearch.convertStringsToBytes(filters));
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void buildFailurePatterns() {
|
||||||
|
if (failurePatterns != null) return; // Thread race and another thread already initialized the search.
|
||||||
|
Logger.printDebug(() -> "Building failure array for: " + this);
|
||||||
|
int[][] failurePatterns = new int[filters.length][];
|
||||||
|
int i = 0;
|
||||||
|
for (byte[] pattern : filters) {
|
||||||
|
failurePatterns[i++] = createFailurePattern(pattern);
|
||||||
|
}
|
||||||
|
this.failurePatterns = failurePatterns; // Must set after initialization finishes.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterGroupResult check(final byte[] bytes) {
|
||||||
|
int matchedLength = 0;
|
||||||
|
int matchedIndex = -1;
|
||||||
|
if (isEnabled()) {
|
||||||
|
int[][] failures = failurePatterns;
|
||||||
|
if (failures == null) {
|
||||||
|
buildFailurePatterns(); // Lazy load.
|
||||||
|
failures = failurePatterns;
|
||||||
|
}
|
||||||
|
for (int i = 0, length = filters.length; i < length; i++) {
|
||||||
|
byte[] filter = filters[i];
|
||||||
|
matchedIndex = indexOf(bytes, filter, failures[i]);
|
||||||
|
if (matchedIndex >= 0) {
|
||||||
|
matchedLength = filter.length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.shared.patches.litho;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
|
|
||||||
abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
public abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
||||||
|
|
||||||
private final List<T> filterGroups = new ArrayList<>();
|
private final List<T> filterGroups = new ArrayList<>();
|
||||||
private final TrieSearch<V> search = createSearchGraph();
|
private final TrieSearch<V> search = createSearchGraph();
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
protected final void addAll(final T... groups) {
|
public final void addAll(final T... groups) {
|
||||||
filterGroups.addAll(Arrays.asList(groups));
|
filterGroups.addAll(Arrays.asList(groups));
|
||||||
|
|
||||||
for (T group : groups) {
|
for (T group : groups) {
|
||||||
@@ -41,18 +42,7 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
|||||||
return filterGroups.iterator();
|
return filterGroups.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public FilterGroup.FilterGroupResult check(V stack) {
|
||||||
public void forEach(@NonNull Consumer<? super T> action) {
|
|
||||||
filterGroups.forEach(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Spliterator<T> spliterator() {
|
|
||||||
return filterGroups.spliterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FilterGroup.FilterGroupResult check(V stack) {
|
|
||||||
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult();
|
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult();
|
||||||
search.matches(stack, result);
|
search.matches(stack, result);
|
||||||
return result;
|
return result;
|
||||||
@@ -60,21 +50,21 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract TrieSearch<V> createSearchGraph();
|
protected abstract TrieSearch<V> createSearchGraph();
|
||||||
}
|
|
||||||
|
|
||||||
final class StringFilterGroupList extends FilterGroupList<String, StringFilterGroup> {
|
public static final class StringFilterGroupList extends FilterGroupList<String, StringFilterGroup> {
|
||||||
protected StringTrieSearch createSearchGraph() {
|
protected StringTrieSearch createSearchGraph() {
|
||||||
return new StringTrieSearch();
|
return new StringTrieSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If searching for a single byte pattern, then it is slightly better to use
|
* If searching for a single byte pattern, then it is slightly better to use
|
||||||
* {@link ByteArrayFilterGroup#check(byte[])} as it uses KMP which is faster
|
* {@link ByteArrayFilterGroup#check(byte[])} as it uses KMP which is faster
|
||||||
* than a prefix tree to search for only 1 pattern.
|
* than a prefix tree to search for only 1 pattern.
|
||||||
*/
|
*/
|
||||||
final class ByteArrayFilterGroupList extends FilterGroupList<byte[], ByteArrayFilterGroup> {
|
public static final class ByteArrayFilterGroupList extends FilterGroupList<byte[], ByteArrayFilterGroup> {
|
||||||
protected ByteTrieSearch createSearchGraph() {
|
protected ByteTrieSearch createSearchGraph() {
|
||||||
return new ByteTrieSearch();
|
return new ByteTrieSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.shared.patches.litho;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -7,9 +7,11 @@ import java.nio.ByteBuffer;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class LithoFilterPatch {
|
public final class LithoFilterPatch {
|
||||||
@@ -36,7 +38,7 @@ public final class LithoFilterPatch {
|
|||||||
builder.append(identifier);
|
builder.append(identifier);
|
||||||
builder.append(" Path: ");
|
builder.append(" Path: ");
|
||||||
builder.append(path);
|
builder.append(path);
|
||||||
if (Settings.DEBUG_PROTOBUFFER.get()) {
|
if (YouTubeAndMusicSettings.DEBUG_PROTOBUFFER.get()) {
|
||||||
builder.append(" BufferStrings: ");
|
builder.append(" BufferStrings: ");
|
||||||
findAsciiStrings(builder, buffer);
|
findAsciiStrings(builder, buffer);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.extension.shared.settings;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
|
import static java.lang.Boolean.FALSE;
|
||||||
|
|
||||||
|
public class YouTubeAndMusicSettings extends BaseSettings {
|
||||||
|
// Custom filter
|
||||||
|
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||||
|
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||||
|
|
||||||
|
// Miscellaneous
|
||||||
|
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false,
|
||||||
|
"revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG));
|
||||||
|
}
|
||||||
@@ -11,6 +11,9 @@ import java.util.List;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -153,8 +156,8 @@ public final class AdsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == playerShoppingShelf) {
|
if (matchedGroup == playerShoppingShelf) {
|
||||||
return contentIndex == 0 && playerShoppingShelfBuffer.check(buffer).isFiltered();
|
return contentIndex == 0 && playerShoppingShelfBuffer.check(buffer).isFiltered();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
|
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@ public final class AdvancedVideoQualityMenuFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
isVideoQualityMenuVisible = true;
|
isVideoQualityMenuVisible = true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final class ButtonsFilter extends Filter {
|
public final class ButtonsFilter extends Filter {
|
||||||
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.e";
|
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.e";
|
||||||
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.e";
|
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.e";
|
||||||
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.e";
|
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.e";
|
||||||
@@ -118,7 +122,7 @@ final class ButtonsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == likeSubscribeGlow) {
|
if (matchedGroup == likeSubscribeGlow) {
|
||||||
return (path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
return (path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final class CommentsFilter extends Filter {
|
public final class CommentsFilter extends Filter {
|
||||||
|
|
||||||
private static final String COMMENT_COMPOSER_PATH = "comment_composer.e";
|
private static final String COMMENT_COMPOSER_PATH = "comment_composer.e";
|
||||||
|
|
||||||
@@ -88,8 +90,8 @@ final class CommentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == chipBar) {
|
if (matchedGroup == chipBar) {
|
||||||
// Playlist sort button uses same components and must only filter if the player is opened.
|
// Playlist sort button uses same components and must only filter if the player is opened.
|
||||||
return PlayerType.getCurrent().isMaximizedOrFullscreen()
|
return PlayerType.getCurrent().isMaximizedOrFullscreen()
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final class DescriptionComponentsFilter extends Filter {
|
public final class DescriptionComponentsFilter extends Filter {
|
||||||
|
|
||||||
private static final String INFOCARDS_SECTION_PATH = "infocards_section.e";
|
private static final String INFOCARDS_SECTION_PATH = "infocards_section.e";
|
||||||
|
|
||||||
@@ -128,8 +131,8 @@ final class DescriptionComponentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
|
|
||||||
if (matchedGroup == aiGeneratedVideoSummarySection || matchedGroup == hypePoints) {
|
if (matchedGroup == aiGeneratedVideoSummarySection || matchedGroup == hypePoints) {
|
||||||
// Only hide if player is open, in case this component is used somewhere else.
|
// Only hide if player is open, in case this component is used somewhere else.
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
|
||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
|
||||||
|
|
||||||
abstract class FilterGroup<T> {
|
|
||||||
final static class FilterGroupResult {
|
|
||||||
private BooleanSetting setting;
|
|
||||||
private int matchedIndex;
|
|
||||||
private int matchedLength;
|
|
||||||
// In the future it might be useful to include which pattern matched,
|
|
||||||
// but for now that is not needed.
|
|
||||||
|
|
||||||
FilterGroupResult() {
|
|
||||||
this(null, -1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
FilterGroupResult(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
|
||||||
setValues(setting, matchedIndex, matchedLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValues(BooleanSetting setting, int matchedIndex, int matchedLength) {
|
|
||||||
this.setting = setting;
|
|
||||||
this.matchedIndex = matchedIndex;
|
|
||||||
this.matchedLength = matchedLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A null value if the group has no setting,
|
|
||||||
* or if no match is returned from {@link FilterGroupList#check(Object)}.
|
|
||||||
*/
|
|
||||||
public BooleanSetting getSetting() {
|
|
||||||
return setting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFiltered() {
|
|
||||||
return matchedIndex >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matched index of first pattern that matched, or -1 if nothing matched.
|
|
||||||
*/
|
|
||||||
public int getMatchedIndex() {
|
|
||||||
return matchedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Length of the matched filter pattern.
|
|
||||||
*/
|
|
||||||
public int getMatchedLength() {
|
|
||||||
return matchedLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final BooleanSetting setting;
|
|
||||||
protected final T[] filters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a new filter group.
|
|
||||||
*
|
|
||||||
* @param setting The associated setting.
|
|
||||||
* @param filters The filters.
|
|
||||||
*/
|
|
||||||
@SafeVarargs
|
|
||||||
public FilterGroup(final BooleanSetting setting, final T... filters) {
|
|
||||||
this.setting = setting;
|
|
||||||
this.filters = filters;
|
|
||||||
if (filters.length == 0) {
|
|
||||||
throw new IllegalArgumentException("Must use one or more filter patterns (zero specified)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return setting == null || setting.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return If {@link FilterGroupList} should include this group when searching.
|
|
||||||
* By default, all filters are included except non enabled settings that require reboot.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
public boolean includeInSearch() {
|
|
||||||
return isEnabled() || !setting.rebootApp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": " + (setting == null ? "(null setting)" : setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract FilterGroupResult check(final T stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
class StringFilterGroup extends FilterGroup<String> {
|
|
||||||
|
|
||||||
public StringFilterGroup(final BooleanSetting setting, final String... filters) {
|
|
||||||
super(setting, filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FilterGroupResult check(final String string) {
|
|
||||||
int matchedIndex = -1;
|
|
||||||
int matchedLength = 0;
|
|
||||||
if (isEnabled()) {
|
|
||||||
for (String pattern : filters) {
|
|
||||||
if (!string.isEmpty()) {
|
|
||||||
final int indexOf = string.indexOf(pattern);
|
|
||||||
if (indexOf >= 0) {
|
|
||||||
matchedIndex = indexOf;
|
|
||||||
matchedLength = pattern.length();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If you have more than 1 filter patterns, then all instances of
|
|
||||||
* this class should filtered using {@link ByteArrayFilterGroupList#check(byte[])},
|
|
||||||
* which uses a prefix tree to give better performance.
|
|
||||||
*/
|
|
||||||
class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
|
||||||
|
|
||||||
private volatile int[][] failurePatterns;
|
|
||||||
|
|
||||||
// Modified implementation from https://stackoverflow.com/a/1507813
|
|
||||||
private static int indexOf(final byte[] data, final byte[] pattern, final int[] failure) {
|
|
||||||
// Finds the first occurrence of the pattern in the byte array using
|
|
||||||
// KMP matching algorithm.
|
|
||||||
int patternLength = pattern.length;
|
|
||||||
for (int i = 0, j = 0, dataLength = data.length; i < dataLength; i++) {
|
|
||||||
while (j > 0 && pattern[j] != data[i]) {
|
|
||||||
j = failure[j - 1];
|
|
||||||
}
|
|
||||||
if (pattern[j] == data[i]) {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
if (j == patternLength) {
|
|
||||||
return i - patternLength + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] createFailurePattern(byte[] pattern) {
|
|
||||||
// Computes the failure function using a boot-strapping process,
|
|
||||||
// where the pattern is matched against itself.
|
|
||||||
final int patternLength = pattern.length;
|
|
||||||
final int[] failure = new int[patternLength];
|
|
||||||
|
|
||||||
for (int i = 1, j = 0; i < patternLength; i++) {
|
|
||||||
while (j > 0 && pattern[j] != pattern[i]) {
|
|
||||||
j = failure[j - 1];
|
|
||||||
}
|
|
||||||
if (pattern[j] == pattern[i]) {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
failure[i] = j;
|
|
||||||
}
|
|
||||||
return failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteArrayFilterGroup(BooleanSetting setting, byte[]... filters) {
|
|
||||||
super(setting, filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the Strings into byte arrays. Used to search for text in binary data.
|
|
||||||
*/
|
|
||||||
public ByteArrayFilterGroup(BooleanSetting setting, String... filters) {
|
|
||||||
super(setting, ByteTrieSearch.convertStringsToBytes(filters));
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void buildFailurePatterns() {
|
|
||||||
if (failurePatterns != null) return; // Thread race and another thread already initialized the search.
|
|
||||||
Logger.printDebug(() -> "Building failure array for: " + this);
|
|
||||||
int[][] failurePatterns = new int[filters.length][];
|
|
||||||
int i = 0;
|
|
||||||
for (byte[] pattern : filters) {
|
|
||||||
failurePatterns[i++] = createFailurePattern(pattern);
|
|
||||||
}
|
|
||||||
this.failurePatterns = failurePatterns; // Must set after initialization finishes.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FilterGroupResult check(final byte[] bytes) {
|
|
||||||
int matchedLength = 0;
|
|
||||||
int matchedIndex = -1;
|
|
||||||
if (isEnabled()) {
|
|
||||||
int[][] failures = failurePatterns;
|
|
||||||
if (failures == null) {
|
|
||||||
buildFailurePatterns(); // Lazy load.
|
|
||||||
failures = failurePatterns;
|
|
||||||
}
|
|
||||||
for (int i = 0, length = filters.length; i < length; i++) {
|
|
||||||
byte[] filter = filters[i];
|
|
||||||
matchedIndex = indexOf(bytes, filter, failures[i]);
|
|
||||||
if (matchedIndex >= 0) {
|
|
||||||
matchedLength = filter.length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FilterGroupResult(setting, matchedIndex, matchedLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class HideInfoCardsFilter extends Filter {
|
public final class HideInfoCardsFilter extends Filter {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import app.revanced.extension.shared.Utils;
|
|||||||
import app.revanced.extension.shared.ByteTrieSearch;
|
import app.revanced.extension.shared.ByteTrieSearch;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
import app.revanced.extension.shared.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
@@ -41,7 +43,7 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
|||||||
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final class KeywordContentFilter extends Filter {
|
public final class KeywordContentFilter extends Filter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strings found in the buffer for every videos. Full strings should be specified.
|
* Strings found in the buffer for every videos. Full strings should be specified.
|
||||||
@@ -554,8 +556,8 @@ final class KeywordContentFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (contentIndex != 0 && matchedGroup == startsWithFilter) {
|
if (contentIndex != 0 && matchedGroup == startsWithFilter) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import androidx.annotation.Nullable;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.StringTrieSearch;
|
import app.revanced.extension.shared.StringTrieSearch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
||||||
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
|
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
@@ -342,7 +345,7 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
// This identifier is used not only in players but also in search results:
|
// This identifier is used not only in players but also in search results:
|
||||||
// https://github.com/ReVanced/revanced-patches/issues/3245
|
// https://github.com/ReVanced/revanced-patches/issues/3245
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ public final class PlaybackSpeedMenuFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == oldPlaybackMenuGroup) {
|
if (matchedGroup == oldPlaybackMenuGroup) {
|
||||||
isOldPlaybackSpeedMenuVisible = true;
|
isOldPlaybackSpeedMenuVisible = true;
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
public final class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||||
|
|
||||||
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
||||||
@Override
|
@Override
|
||||||
@@ -94,7 +97,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (matchedGroup == videoQualityMenuFooter) {
|
if (matchedGroup == videoQualityMenuFooter) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import app.revanced.extension.youtube.patches.VideoInformation;
|
|||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.TrieSearch;
|
import app.revanced.extension.shared.TrieSearch;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for video id's in the proto buffer of Shorts dislike.
|
* Searches for video id's in the proto buffer of Shorts dislike.
|
||||||
@@ -84,13 +87,13 @@ public final class ReturnYouTubeDislikeFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (!Settings.RYD_ENABLED.get() || !Settings.RYD_SHORTS.get()) {
|
if (!Settings.RYD_ENABLED.get() || !Settings.RYD_SHORTS.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterGroup.FilterGroupResult result = videoIdFilterGroup.check(buffer);
|
FilterGroupResult result = videoIdFilterGroup.check(buffer);
|
||||||
if (result.isFiltered()) {
|
if (result.isFiltered()) {
|
||||||
String matchedVideoId = findVideoId(buffer);
|
String matchedVideoId = findVideoId(buffer);
|
||||||
// Matched video will be null if in incognito mode.
|
// Matched video will be null if in incognito mode.
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.patches.litho.Filter;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroup.*;
|
||||||
|
import app.revanced.extension.shared.patches.litho.FilterGroupList.*;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.NavigationBar;
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
@@ -339,7 +342,7 @@ public final class ShortsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(String identifier, String path, byte[] buffer,
|
public boolean isFiltered(String identifier, String path, byte[] buffer,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
if (contentType == FilterContentType.PATH) {
|
if (contentType == FilterContentType.PATH) {
|
||||||
if (matchedGroup == subscribeButton || matchedGroup == joinButton
|
if (matchedGroup == subscribeButton || matchedGroup == joinButton
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import android.graphics.Color;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
|
import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
import app.revanced.extension.shared.settings.EnumSetting;
|
import app.revanced.extension.shared.settings.EnumSetting;
|
||||||
@@ -49,7 +50,7 @@ import app.revanced.extension.youtube.patches.MiniplayerPatch;
|
|||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
|
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
|
||||||
|
|
||||||
public class Settings extends BaseSettings {
|
public class Settings extends YouTubeAndMusicSettings {
|
||||||
// Video
|
// Video
|
||||||
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
|
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
|
||||||
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
|
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
|
||||||
@@ -274,11 +275,6 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
||||||
new ChangeStartPageTypeAvailability());
|
new ChangeStartPageTypeAvailability());
|
||||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
||||||
|
|
||||||
// Custom filter
|
|
||||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
|
||||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
|
||||||
|
|
||||||
// Navigation buttons
|
// Navigation buttons
|
||||||
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
||||||
@@ -368,8 +364,6 @@ public class Settings extends BaseSettings {
|
|||||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
|
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
|
||||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS_AV1 = new BooleanSetting("revanced_spoof_video_streams_av1", FALSE, true,
|
public static final BooleanSetting SPOOF_VIDEO_STREAMS_AV1 = new BooleanSetting("revanced_spoof_video_streams_av1", FALSE, true,
|
||||||
"revanced_spoof_video_streams_av1_user_dialog_message", new SpoofClientAv1Availability());
|
"revanced_spoof_video_streams_av1_user_dialog_message", new SpoofClientAv1Availability());
|
||||||
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false,
|
|
||||||
"revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG));
|
|
||||||
|
|
||||||
// Swipe controls
|
// Swipe controls
|
||||||
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
||||||
@@ -382,7 +376,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
||||||
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
|
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
|
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
@@ -411,7 +405,9 @@ public class Settings extends BaseSettings {
|
|||||||
|
|
||||||
// SponsorBlock
|
// SponsorBlock
|
||||||
public static final BooleanSetting SB_ENABLED = new BooleanSetting("sb_enabled", TRUE);
|
public static final BooleanSetting SB_ENABLED = new BooleanSetting("sb_enabled", TRUE);
|
||||||
/** Do not use id setting directly. Instead use {@link SponsorBlockSettings}. */
|
/**
|
||||||
|
* Do not use id setting directly. Instead use {@link SponsorBlockSettings}.
|
||||||
|
*/
|
||||||
public static final StringSetting SB_PRIVATE_USER_ID = new StringSetting("sb_private_user_id_Do_Not_Share", "", parent(SB_ENABLED));
|
public static final StringSetting SB_PRIVATE_USER_ID = new StringSetting("sb_private_user_id_Do_Not_Share", "", parent(SB_ENABLED));
|
||||||
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
||||||
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
||||||
@@ -460,7 +456,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFFFF", false, false);
|
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFFFF", false, false);
|
||||||
|
|
||||||
// Deprecated migrations
|
// Deprecated migrations
|
||||||
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
||||||
|
|
||||||
private static final FloatSetting DEPRECATED_SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f, false, false);
|
private static final FloatSetting DEPRECATED_SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f, false, false);
|
||||||
private static final FloatSetting DEPRECATED_SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f, false, false);
|
private static final FloatSetting DEPRECATED_SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f, false, false);
|
||||||
@@ -512,7 +508,7 @@ public class Settings extends BaseSettings {
|
|||||||
// or is spoofing to a version the same or newer than this app.
|
// or is spoofing to a version the same or newer than this app.
|
||||||
if (!SPOOF_APP_VERSION_TARGET.isSetToDefault() &&
|
if (!SPOOF_APP_VERSION_TARGET.isSetToDefault() &&
|
||||||
(SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0
|
(SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0
|
||||||
|| (Utils.getAppVersionName().compareTo(SPOOF_APP_VERSION_TARGET.get()) <= 0))) {
|
|| (Utils.getAppVersionName().compareTo(SPOOF_APP_VERSION_TARGET.get()) <= 0))) {
|
||||||
Logger.printInfo(() -> "Resetting spoof app version");
|
Logger.printInfo(() -> "Resetting spoof app version");
|
||||||
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
||||||
SPOOF_APP_VERSION.resetToDefault();
|
SPOOF_APP_VERSION.resetToDefault();
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.48.1-dev.1
|
version = 5.50.0-dev.1
|
||||||
|
|||||||
@@ -472,6 +472,10 @@ public final class app/revanced/patches/music/layout/compactheader/HideCategoryB
|
|||||||
public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatchKt {
|
||||||
|
public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColorKt {
|
public final class app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColorKt {
|
||||||
public static final fun getChangeMiniplayerColor ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getChangeMiniplayerColor ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -497,6 +501,10 @@ public final class app/revanced/patches/music/misc/androidauto/BypassCertificate
|
|||||||
public static final fun getBypassCertificateChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getBypassCertificateChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatchKt {
|
||||||
|
public static final fun getUnlockAndroidAutoMediaBrowserPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt {
|
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt {
|
||||||
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -521,6 +529,10 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
|
|||||||
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/music/misc/litho/filter/LithoFilterPatchKt {
|
||||||
|
public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/privacy/SanitizeSharingLinksPatchKt {
|
public final class app/revanced/patches/music/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -932,6 +944,12 @@ public final class app/revanced/patches/shared/misc/hex/Replacement {
|
|||||||
public final fun getReplacementBytesPadded ()[B
|
public final fun getReplacementBytesPadded ()[B
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/shared/misc/litho/filter/LithoFilterPatchKt {
|
||||||
|
public static final fun getAddLithoFilter ()Lkotlin/jvm/functions/Function1;
|
||||||
|
public static final fun lithoFilterPatch (Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
public static synthetic fun lithoFilterPatch$default (Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/mapping/ResourceElement {
|
public final class app/revanced/patches/shared/misc/mapping/ResourceElement {
|
||||||
public final fun component1 ()Ljava/lang/String;
|
public final fun component1 ()Ljava/lang/String;
|
||||||
public final fun component2 ()Ljava/lang/String;
|
public final fun component2 ()Ljava/lang/String;
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
import org.w3c.dom.*
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
import javax.xml.transform.OutputKeys
|
||||||
|
import javax.xml.transform.TransformerFactory
|
||||||
|
import javax.xml.transform.dom.DOMSource
|
||||||
|
import javax.xml.transform.stream.StreamResult
|
||||||
|
|
||||||
group = "app.revanced"
|
group = "app.revanced"
|
||||||
|
|
||||||
patches {
|
patches {
|
||||||
@@ -22,25 +29,6 @@ dependencies {
|
|||||||
compileOnly(project(":patches:stub"))
|
compileOnly(project(":patches:stub"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
|
||||||
register<JavaExec>("preprocessCrowdinStrings") {
|
|
||||||
description = "Preprocess strings for Crowdin push"
|
|
||||||
|
|
||||||
dependsOn(compileKotlin)
|
|
||||||
|
|
||||||
classpath = sourceSets["main"].runtimeClasspath
|
|
||||||
mainClass.set("app.revanced.util.CrowdinPreprocessorKt")
|
|
||||||
|
|
||||||
args = listOf(
|
|
||||||
"src/main/resources/addresources/values/strings.xml",
|
|
||||||
// Ideally this would use build/tmp/crowdin/strings.xml
|
|
||||||
// But using that does not work with Crowdin pull because
|
|
||||||
// it does not recognize the strings.xml file belongs to this project.
|
|
||||||
"src/main/resources/addresources/values/strings.xml"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
freeCompilerArgs = listOf("-Xcontext-receivers")
|
freeCompilerArgs = listOf("-Xcontext-receivers")
|
||||||
@@ -55,4 +43,91 @@ publishing {
|
|||||||
credentials(PasswordCredentials::class)
|
credentials(PasswordCredentials::class)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register("processStringsForCrowdin") {
|
||||||
|
description = "Process strings file for Crowdin by commenting out non-standard tags."
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
// Comment out the non-standard tags. Otherwise, Crowdin interprets the file
|
||||||
|
// not as Android but instead a generic xml file where strings are
|
||||||
|
// identified by xml position and not key
|
||||||
|
val stringsXmlFile = project.projectDir.resolve("src/main/resources/addresources/values/strings.xml")
|
||||||
|
|
||||||
|
val builder = DocumentBuilderFactory.newInstance().apply {
|
||||||
|
isIgnoringComments = false
|
||||||
|
isCoalescing = false
|
||||||
|
isNamespaceAware = false
|
||||||
|
}.newDocumentBuilder()
|
||||||
|
|
||||||
|
val document = builder.newDocument()
|
||||||
|
val root = document.createElement("resources").also(document::appendChild)
|
||||||
|
|
||||||
|
fun walk(node: Node, appId: String? = null, patchId: String? = null, insideResources: Boolean = false) {
|
||||||
|
fun walkChildren(el: Element, appId: String?, patchId: String?, insideResources: Boolean) {
|
||||||
|
val children = el.childNodes
|
||||||
|
for (i in 0 until children.length) {
|
||||||
|
walk(children.item(i), appId, patchId, insideResources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when (node.nodeType) {
|
||||||
|
Node.COMMENT_NODE -> {
|
||||||
|
val comment = document.createComment(node.nodeValue)
|
||||||
|
if (insideResources) root.appendChild(comment) else document.insertBefore(comment, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.ELEMENT_NODE -> {
|
||||||
|
val element = node as Element
|
||||||
|
|
||||||
|
when (element.tagName) {
|
||||||
|
"resources" -> walkChildren(element, appId, patchId, insideResources = true)
|
||||||
|
|
||||||
|
"app" -> {
|
||||||
|
val newAppId = element.getAttribute("id")
|
||||||
|
|
||||||
|
root.appendChild(document.createComment(" <app id=\"$newAppId\"> "))
|
||||||
|
walkChildren(element, newAppId, patchId, insideResources)
|
||||||
|
root.appendChild(document.createComment(" </app> "))
|
||||||
|
}
|
||||||
|
|
||||||
|
"patch" -> {
|
||||||
|
val newPatchId = element.getAttribute("id")
|
||||||
|
|
||||||
|
root.appendChild(document.createComment(" <patch id=\"$newPatchId\"> "))
|
||||||
|
walkChildren(element, appId, newPatchId, insideResources)
|
||||||
|
root.appendChild(document.createComment(" </patch> "))
|
||||||
|
}
|
||||||
|
|
||||||
|
"string" -> {
|
||||||
|
val name = element.getAttribute("name")
|
||||||
|
val value = element.textContent
|
||||||
|
val fullName = "$appId.$patchId.$name"
|
||||||
|
|
||||||
|
val stringElement = document.createElement("string")
|
||||||
|
stringElement.setAttribute("name", fullName)
|
||||||
|
stringElement.appendChild(document.createTextNode(value))
|
||||||
|
root.appendChild(stringElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> walkChildren(element, appId, patchId, insideResources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.parse(stringsXmlFile).let {
|
||||||
|
val topLevel = it.childNodes
|
||||||
|
for (i in 0 until topLevel.length) {
|
||||||
|
val node = topLevel.item(i)
|
||||||
|
if (node != it.documentElement) walk(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(it.documentElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformerFactory.newInstance().newTransformer().apply {
|
||||||
|
setOutputProperty(OutputKeys.INDENT, "yes")
|
||||||
|
setOutputProperty(OutputKeys.ENCODING, "utf-8")
|
||||||
|
}.transform(DOMSource(document), StreamResult(stringsXmlFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patches.music.layout.hide.general
|
||||||
|
|
||||||
|
import app.revanced.patches.music.misc.litho.filter.lithoFilterPatch
|
||||||
|
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||||
|
import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch
|
||||||
|
|
||||||
|
val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
||||||
|
lithoFilterPatch = lithoFilterPatch,
|
||||||
|
settingsPatch = settingsPatch,
|
||||||
|
filterClasses = setOf("Lapp/revanced/extension/shared/patches/components/CustomFilter;"),
|
||||||
|
compatibleWithPackages = arrayOf("com.google.android.apps.youtube.music" to setOf("7.29.52", "8.10.52"))
|
||||||
|
)
|
||||||
@@ -5,24 +5,11 @@ import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
|||||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Deprecated("This patch is useless by itself and has been merged into another patch.", ReplaceWith("unlockAndroidAutoMediaBrowserPatch"))
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val bypassCertificateChecksPatch = bytecodePatch(
|
val bypassCertificateChecksPatch = bytecodePatch(
|
||||||
name = "Bypass certificate checks",
|
|
||||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(unlockAndroidAutoMediaBrowserPatch)
|
||||||
sharedExtensionPatch,
|
|
||||||
settingsPatch
|
|
||||||
)
|
|
||||||
|
|
||||||
compatibleWith(
|
|
||||||
"com.google.android.apps.youtube.music"(
|
|
||||||
"7.29.52",
|
|
||||||
"8.10.52"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
checkCertificateFingerprint.method.returnEarly(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patches.music.misc.androidauto
|
package app.revanced.patches.music.misc.androidauto
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
internal val checkCertificateFingerprint = fingerprint {
|
internal val checkCertificateFingerprint = fingerprint {
|
||||||
@@ -10,4 +9,13 @@ internal val checkCertificateFingerprint = fingerprint {
|
|||||||
"X509",
|
"X509",
|
||||||
"Failed to get certificate" // Partial String match.
|
"Failed to get certificate" // Partial String match.
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val searchMediaItemsConstructorFingerprint = fingerprint {
|
||||||
|
returns("V")
|
||||||
|
strings("ytm_media_browser/search_media_items")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val searchMediaItemsExecuteFingerprint = fingerprint {
|
||||||
|
parameters()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.patches.music.misc.androidauto
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.registersUsed
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val unlockAndroidAutoMediaBrowserPatch = bytecodePatch(
|
||||||
|
name = "Unlock Android Auto Media Browser",
|
||||||
|
description = "Unlocks Android Auto Media Browser which enables the search function including speech to text.",
|
||||||
|
) {
|
||||||
|
compatibleWith(
|
||||||
|
"com.google.android.apps.youtube.music"(
|
||||||
|
"7.29.52",
|
||||||
|
"8.10.52"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
checkCertificateFingerprint.method.returnEarly(true)
|
||||||
|
|
||||||
|
searchMediaItemsExecuteFingerprint
|
||||||
|
.match(searchMediaItemsConstructorFingerprint.classDef)
|
||||||
|
.method.apply {
|
||||||
|
val targetIndex = instructions.indexOfFirst {
|
||||||
|
it.opcode == Opcode.IGET_OBJECT && it.getReference<FieldReference>()?.type == "Ljava/lang/String;"
|
||||||
|
}
|
||||||
|
|
||||||
|
val register = instructions[targetIndex].registersUsed.first()
|
||||||
|
replaceInstruction(targetIndex, "const-string v$register, \"com.google.android.apps.youtube.music\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,20 +7,15 @@ import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
|
|||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val enableDebuggingPatch = enableDebuggingPatch(
|
val enableDebuggingPatch = enableDebuggingPatch(
|
||||||
block = {
|
sharedExtensionPatch = sharedExtensionPatch,
|
||||||
dependsOn(
|
settingsPatch = settingsPatch,
|
||||||
sharedExtensionPatch,
|
compatibleWithPackages = arrayOf(
|
||||||
settingsPatch,
|
"com.google.android.apps.youtube.music" to setOf(
|
||||||
|
"7.29.52",
|
||||||
|
"8.10.52"
|
||||||
)
|
)
|
||||||
|
),
|
||||||
compatibleWith(
|
|
||||||
"com.google.android.apps.youtube.music"(
|
|
||||||
"7.29.52",
|
|
||||||
"8.10.52"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
// String feature flag does not appear to be present with YT Music.
|
// String feature flag does not appear to be present with YT Music.
|
||||||
hookStringFeatureFlag = false,
|
hookStringFeatureFlag = false,
|
||||||
preferenceScreen = PreferenceScreen.MISC
|
preferenceScreen = PreferenceScreen.MISC,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package app.revanced.patches.music.misc.litho.filter
|
||||||
|
|
||||||
|
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.music.shared.conversionContextFingerprintToString
|
||||||
|
import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
val lithoFilterPatch = lithoFilterPatch(
|
||||||
|
componentCreateInsertionIndex = {
|
||||||
|
// No supported version clobbers p2 so we can just do our things before the return instruction.
|
||||||
|
indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
|
||||||
|
},
|
||||||
|
conversionContextFingerprintToString = conversionContextFingerprintToString,
|
||||||
|
) {
|
||||||
|
dependsOn(sharedExtensionPatch)
|
||||||
|
}
|
||||||
@@ -11,3 +11,19 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
|
|||||||
method.name == "onCreate" && classDef.type == YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE
|
method.name == "onCreate" && classDef.type == YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val conversionContextFingerprintToString = fingerprint {
|
||||||
|
parameters()
|
||||||
|
strings(
|
||||||
|
"ConversionContext{containerInternal=",
|
||||||
|
", gridColumnCount=",
|
||||||
|
", gridColumnIndex=",
|
||||||
|
", templateLoggerFactory=",
|
||||||
|
", rootDisposableContainer=",
|
||||||
|
", elementId=",
|
||||||
|
", identifierProperty="
|
||||||
|
)
|
||||||
|
custom { method, _ ->
|
||||||
|
method.name == "toString"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package app.revanced.patches.shared.layout.hide.general
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
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.music.misc.settings.PreferenceScreen
|
||||||
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
|
|
||||||
|
internal fun hideLayoutComponentsPatch(
|
||||||
|
lithoFilterPatch: Patch<*>,
|
||||||
|
settingsPatch: Patch<*>,
|
||||||
|
additionalDependencies: Set<Patch<*>> = emptySet(),
|
||||||
|
filterClasses: Set<String>,
|
||||||
|
vararg compatibleWithPackages: Pair<String, Set<String>?>,
|
||||||
|
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||||
|
) = bytecodePatch(
|
||||||
|
name = "Hide layout components",
|
||||||
|
description = "Adds options to hide general layout components.",
|
||||||
|
) {
|
||||||
|
dependsOn(
|
||||||
|
lithoFilterPatch,
|
||||||
|
settingsPatch,
|
||||||
|
*additionalDependencies.toTypedArray(),
|
||||||
|
addResourcesPatch,
|
||||||
|
)
|
||||||
|
|
||||||
|
compatibleWith(packages = compatibleWithPackages)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
addResources("shared", "layout.hide.general.hideLayoutComponentsPatch")
|
||||||
|
|
||||||
|
PreferenceScreen.GENERAL.addPreferences(
|
||||||
|
PreferenceScreenPreference(
|
||||||
|
key = "revanced_custom_filter_screen",
|
||||||
|
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||||
|
preferences = setOf(
|
||||||
|
SwitchPreference("revanced_custom_filter"),
|
||||||
|
TextPreference("revanced_custom_filter_strings", inputType = InputType.TEXT_MULTI_LINE),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
filterClasses.forEach { className ->
|
||||||
|
addLithoFilter(className)
|
||||||
|
}
|
||||||
|
|
||||||
|
executeBlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,23 +2,14 @@ package app.revanced.patches.shared.misc.debugging
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
import app.revanced.patcher.patch.Patch
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
import app.revanced.patches.shared.misc.settings.preference.*
|
||||||
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.util.*
|
||||||
import app.revanced.util.ResourceGroup
|
|
||||||
import app.revanced.util.copyResources
|
|
||||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@@ -29,23 +20,27 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
|||||||
* Patch shared with YouTube and YT Music.
|
* Patch shared with YouTube and YT Music.
|
||||||
*/
|
*/
|
||||||
internal fun enableDebuggingPatch(
|
internal fun enableDebuggingPatch(
|
||||||
block: BytecodePatchBuilder.() -> Unit = {},
|
sharedExtensionPatch: Patch<*>,
|
||||||
executeBlock: BytecodePatchContext.() -> Unit = {},
|
settingsPatch: Patch<*>,
|
||||||
|
vararg compatibleWithPackages: Pair<String, Set<String>>,
|
||||||
hookStringFeatureFlag: Boolean,
|
hookStringFeatureFlag: Boolean,
|
||||||
preferenceScreen: BasePreferenceScreen.Screen,
|
preferenceScreen: BasePreferenceScreen.Screen,
|
||||||
additionalDebugPreferences: List<BasePreference> = emptyList()
|
|
||||||
) = bytecodePatch(
|
) = bytecodePatch(
|
||||||
name = "Enable debugging",
|
name = "Enable debugging",
|
||||||
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
|
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
|
||||||
) {
|
) {
|
||||||
|
compatibleWith(packages = compatibleWithPackages)
|
||||||
|
|
||||||
dependsOn(
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
settingsPatch,
|
||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
resourcePatch {
|
resourcePatch {
|
||||||
execute {
|
execute {
|
||||||
copyResources(
|
copyResources(
|
||||||
"settings",
|
"settings",
|
||||||
ResourceGroup("drawable",
|
ResourceGroup(
|
||||||
|
"drawable",
|
||||||
// Action buttons.
|
// Action buttons.
|
||||||
"revanced_settings_copy_all.xml",
|
"revanced_settings_copy_all.xml",
|
||||||
"revanced_settings_deselect_all.xml",
|
"revanced_settings_deselect_all.xml",
|
||||||
@@ -61,38 +56,29 @@ internal fun enableDebuggingPatch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
block()
|
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
executeBlock()
|
|
||||||
|
|
||||||
addResources("shared", "misc.debugging.enableDebuggingPatch")
|
addResources("shared", "misc.debugging.enableDebuggingPatch")
|
||||||
|
|
||||||
val preferences = mutableSetOf<BasePreference>(
|
val preferences = setOf(
|
||||||
SwitchPreference("revanced_debug"),
|
SwitchPreference("revanced_debug"),
|
||||||
)
|
SwitchPreference("revanced_debug_protobuffer"),
|
||||||
|
SwitchPreference("revanced_debug_stacktrace"),
|
||||||
preferences.addAll(additionalDebugPreferences)
|
SwitchPreference("revanced_debug_toast_on_error"),
|
||||||
|
NonInteractivePreference(
|
||||||
preferences.addAll(
|
"revanced_debug_export_logs_to_clipboard",
|
||||||
listOf(
|
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
|
||||||
SwitchPreference("revanced_debug_stacktrace"),
|
selectable = true
|
||||||
SwitchPreference("revanced_debug_toast_on_error"),
|
),
|
||||||
NonInteractivePreference(
|
NonInteractivePreference(
|
||||||
"revanced_debug_export_logs_to_clipboard",
|
"revanced_debug_logs_clear_buffer",
|
||||||
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
|
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
|
||||||
selectable = true
|
selectable = true
|
||||||
),
|
),
|
||||||
NonInteractivePreference(
|
NonInteractivePreference(
|
||||||
"revanced_debug_logs_clear_buffer",
|
"revanced_debug_feature_flags_manager",
|
||||||
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
|
tag = "app.revanced.extension.shared.settings.preference.FeatureFlagsManagerPreference",
|
||||||
selectable = true
|
selectable = true
|
||||||
),
|
|
||||||
NonInteractivePreference(
|
|
||||||
"revanced_debug_feature_flags_manager",
|
|
||||||
tag = "app.revanced.extension.shared.settings.preference.FeatureFlagsManagerPreference",
|
|
||||||
selectable = true
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package app.revanced.patches.shared.misc.litho.filter
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.containsLiteralInstruction
|
||||||
|
import app.revanced.util.literal
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal val lithoFilterFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||||
|
custom { _, classDef ->
|
||||||
|
classDef.endsWith("/LithoFilterPatch;")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a method that use the protobuf of our component.
|
||||||
|
*/
|
||||||
|
internal val protobufBufferReferenceFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters("I", "Ljava/nio/ByteBuffer;")
|
||||||
|
opcodes(
|
||||||
|
Opcode.IPUT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.SUB_INT_2ADDR,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val componentContextParserFingerprint = fingerprint {
|
||||||
|
strings("Number of bits must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val emptyComponentFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
|
||||||
|
parameters()
|
||||||
|
strings("EmptyComponent")
|
||||||
|
custom { _, classDef ->
|
||||||
|
classDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val componentCreateFingerprint = fingerprint {
|
||||||
|
strings(
|
||||||
|
"Element missing correct type extension",
|
||||||
|
"Element missing type"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val lithoThreadExecutorFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||||
|
parameters("I", "I", "I")
|
||||||
|
custom { method, classDef ->
|
||||||
|
classDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" &&
|
||||||
|
method.containsLiteralInstruction(1L) // 1L = default thread timeout.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
@file:Suppress("SpellCheckingInspection")
|
||||||
|
|
||||||
|
package app.revanced.patches.shared.misc.litho.filter
|
||||||
|
|
||||||
|
import app.revanced.patcher.Fingerprint
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||||
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||||
|
import app.revanced.util.findFreeRegister
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to add a hook point to the extension stub.
|
||||||
|
*/
|
||||||
|
lateinit var addLithoFilter: (String) -> Unit
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of filters added to the static field array.
|
||||||
|
*/
|
||||||
|
private var filterCount = 0
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A patch that allows to filter Litho components based on their identifier or path.
|
||||||
|
*
|
||||||
|
* @param componentCreateInsertionIndex The index to insert the filtering code in the component create method.
|
||||||
|
* @param conversionContextFingerprintToString The fingerprint of the conversion context to string method.
|
||||||
|
* @param executeBlock The additional execution block of the patch.
|
||||||
|
* @param block The additional block to build the patch.
|
||||||
|
*/
|
||||||
|
internal fun lithoFilterPatch(
|
||||||
|
componentCreateInsertionIndex: Method.() -> Int,
|
||||||
|
conversionContextFingerprintToString: Fingerprint,
|
||||||
|
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||||
|
block: BytecodePatchBuilder.() -> Unit = {},
|
||||||
|
) = bytecodePatch(
|
||||||
|
description = "Hooks the method which parses the bytes into a ComponentContext to filter components.",
|
||||||
|
) {
|
||||||
|
dependsOn(
|
||||||
|
sharedExtensionPatch(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following patch inserts a hook into the method that parses the bytes into a ComponentContext.
|
||||||
|
* This method contains a StringBuilder object that represents the pathBuilder of the component.
|
||||||
|
* The pathBuilder is used to filter components by their path.
|
||||||
|
*
|
||||||
|
* Additionally, the method contains a reference to the component's identifier.
|
||||||
|
* The identifier is used to filter components by their identifier.
|
||||||
|
*
|
||||||
|
* The protobuf buffer is passed along from a different injection point before the filtering occurs.
|
||||||
|
* The buffer is a large byte array that represents the component tree.
|
||||||
|
* This byte array is searched for strings that indicate the current component.
|
||||||
|
*
|
||||||
|
* All modifications done here must allow all the original code to still execute
|
||||||
|
* even when filtering, otherwise memory leaks or poor app performance may occur.
|
||||||
|
*
|
||||||
|
* The following pseudocode shows how this patch works:
|
||||||
|
*
|
||||||
|
* class SomeOtherClass {
|
||||||
|
* // Called before ComponentContextParser.parseComponent() method.
|
||||||
|
* public void someOtherMethod(ByteBuffer byteBuffer) {
|
||||||
|
* ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class CreateComponentClass {
|
||||||
|
* public Component createComponent() {
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* if (extensionClass.shouldFilter(identifier, path)) { // Inserted by this patch.
|
||||||
|
* return emptyComponent;
|
||||||
|
* }
|
||||||
|
* return originalUnpatchedComponent; // Original code.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
execute {
|
||||||
|
// Remove dummy filter from extenion static field
|
||||||
|
// and add the filters included during patching.
|
||||||
|
lithoFilterFingerprint.method.apply {
|
||||||
|
removeInstructions(2, 4) // Remove dummy filter.
|
||||||
|
|
||||||
|
addLithoFilter = { classDescriptor ->
|
||||||
|
addInstructions(
|
||||||
|
2,
|
||||||
|
"""
|
||||||
|
new-instance v1, $classDescriptor
|
||||||
|
invoke-direct { v1 }, $classDescriptor-><init>()V
|
||||||
|
const/16 v2, ${filterCount++}
|
||||||
|
aput-object v1, v0, v2
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an interceptor to steal the protobuf of our component.
|
||||||
|
protobufBufferReferenceFingerprint.method.addInstruction(
|
||||||
|
0,
|
||||||
|
"invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Hook the method that parses bytes into a ComponentContext.
|
||||||
|
// Allow the method to run to completion, and override the
|
||||||
|
// return value with an empty component if it should be filtered.
|
||||||
|
// It is important to allow the original code to always run to completion,
|
||||||
|
// otherwise high memory usage and poor app performance can occur.
|
||||||
|
|
||||||
|
// Find the identifier/path fields of the conversion context.
|
||||||
|
val conversionContextIdentifierField = componentContextParserFingerprint.let {
|
||||||
|
// Identifier field is loaded just before the string declaration.
|
||||||
|
val index = it.method.indexOfFirstInstructionReversedOrThrow(
|
||||||
|
it.stringMatches!!.first().index
|
||||||
|
) {
|
||||||
|
// Our instruction reads a String from a field of the ConversionContext class.
|
||||||
|
val reference = getReference<FieldReference>()
|
||||||
|
reference?.definingClass == conversionContextFingerprintToString.originalClassDef.type
|
||||||
|
&& reference.type == "Ljava/lang/String;"
|
||||||
|
}
|
||||||
|
|
||||||
|
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
val conversionContextPathBuilderField = conversionContextFingerprintToString.originalClassDef
|
||||||
|
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
|
||||||
|
|
||||||
|
// Find class and methods to create an empty component.
|
||||||
|
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
|
||||||
|
// The only static method in the class.
|
||||||
|
method ->
|
||||||
|
AccessFlags.STATIC.isSet(method.accessFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
val emptyComponentField = classBy {
|
||||||
|
// Only one field that matches.
|
||||||
|
it.type == builderMethodDescriptor.returnType
|
||||||
|
}!!.immutableClass.fields.single()
|
||||||
|
|
||||||
|
// Match all component creations methods
|
||||||
|
componentCreateFingerprint.method.apply {
|
||||||
|
val insertIndex = componentCreateInsertionIndex()
|
||||||
|
val freeRegister = findFreeRegister(insertIndex)
|
||||||
|
val identifierRegister = findFreeRegister(insertIndex, freeRegister)
|
||||||
|
val pathRegister = findFreeRegister(insertIndex, freeRegister, identifierRegister)
|
||||||
|
|
||||||
|
addInstructionsAtControlFlowLabel(
|
||||||
|
insertIndex,
|
||||||
|
"""
|
||||||
|
move-object/from16 v$freeRegister, p2 # ConversionContext parameter
|
||||||
|
check-cast v$freeRegister, ${conversionContextFingerprintToString.originalClassDef.type} # Check we got the actual ConversionContext
|
||||||
|
|
||||||
|
# Get identifier and path from ConversionContext
|
||||||
|
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
|
||||||
|
iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField
|
||||||
|
|
||||||
|
# Check if the component should be filtered.
|
||||||
|
invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
|
||||||
|
move-result v$freeRegister
|
||||||
|
if-eqz v$freeRegister, :unfiltered
|
||||||
|
|
||||||
|
# Return an empty component
|
||||||
|
move-object/from16 v$freeRegister, p1
|
||||||
|
invoke-static { v$freeRegister }, $builderMethodDescriptor
|
||||||
|
move-result-object v$freeRegister
|
||||||
|
iget-object v$freeRegister, v$freeRegister, $emptyComponentField
|
||||||
|
return-object v$freeRegister
|
||||||
|
|
||||||
|
:unfiltered
|
||||||
|
nop
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if needed in music
|
||||||
|
// Change Litho thread executor to 1 thread to fix layout issue in unpatched YouTube.
|
||||||
|
lithoThreadExecutorFingerprint.method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorCorePoolSize(I)I
|
||||||
|
move-result p1
|
||||||
|
invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorMaxThreads(I)I
|
||||||
|
move-result p2
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
executeBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize {
|
||||||
|
// Save the number of filters added.
|
||||||
|
lithoFilterFingerprint.method.replaceInstruction(0, "const/16 v0, $filterCount")
|
||||||
|
}
|
||||||
|
|
||||||
|
block()
|
||||||
|
}
|
||||||
@@ -7,13 +7,13 @@ import app.revanced.patcher.patch.resourcePatch
|
|||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch
|
import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch
|
||||||
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.shared.misc.mapping.get
|
import app.revanced.patches.shared.misc.mapping.get
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch
|
import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch
|
||||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
|
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
|
|
||||||
|
|||||||
@@ -9,34 +9,27 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.get
|
import app.revanced.patches.shared.misc.mapping.get
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.shared.misc.settings.preference.*
|
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.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.util.findFreeRegister
|
import app.revanced.util.*
|
||||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
|
||||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
|
||||||
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.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
|
||||||
|
|
||||||
var expandButtonDownId = -1L
|
var expandButtonDownId = -1L
|
||||||
private set
|
private set
|
||||||
@@ -108,184 +101,170 @@ private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME =
|
|||||||
private const val COMMENTS_FILTER_CLASS_NAME =
|
private const val COMMENTS_FILTER_CLASS_NAME =
|
||||||
"Lapp/revanced/extension/youtube/patches/components/CommentsFilter;"
|
"Lapp/revanced/extension/youtube/patches/components/CommentsFilter;"
|
||||||
private const val CUSTOM_FILTER_CLASS_NAME =
|
private const val CUSTOM_FILTER_CLASS_NAME =
|
||||||
"Lapp/revanced/extension/youtube/patches/components/CustomFilter;"
|
"Lapp/revanced/extension/shared/patches/components/CustomFilter;"
|
||||||
private const val KEYWORD_FILTER_CLASS_NAME =
|
private const val KEYWORD_FILTER_CLASS_NAME =
|
||||||
"Lapp/revanced/extension/youtube/patches/components/KeywordContentFilter;"
|
"Lapp/revanced/extension/youtube/patches/components/KeywordContentFilter;"
|
||||||
|
|
||||||
val hideLayoutComponentsPatch = bytecodePatch(
|
|
||||||
name = "Hide layout components",
|
|
||||||
description = "Adds options to hide general layout components.",
|
|
||||||
|
|
||||||
) {
|
val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
||||||
dependsOn(
|
lithoFilterPatch = lithoFilterPatch,
|
||||||
lithoFilterPatch,
|
settingsPatch = settingsPatch,
|
||||||
settingsPatch,
|
additionalDependencies = setOf(
|
||||||
addResourcesPatch,
|
|
||||||
hideLayoutComponentsResourcePatch,
|
hideLayoutComponentsResourcePatch,
|
||||||
navigationBarHookPatch,
|
navigationBarHookPatch,
|
||||||
)
|
),
|
||||||
|
filterClasses = setOf(
|
||||||
compatibleWith(
|
LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR,
|
||||||
"com.google.android.youtube"(
|
DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME,
|
||||||
|
COMMENTS_FILTER_CLASS_NAME,
|
||||||
|
KEYWORD_FILTER_CLASS_NAME,
|
||||||
|
CUSTOM_FILTER_CLASS_NAME
|
||||||
|
),
|
||||||
|
compatibleWithPackages = arrayOf(
|
||||||
|
"com.google.android.youtube" to setOf(
|
||||||
"19.34.42",
|
"19.34.42",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
"20.13.41",
|
"20.13.41",
|
||||||
"20.14.43",
|
"20.14.43",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
|
addResources("youtube", "layout.hide.general.hideLayoutComponentsPatch")
|
||||||
|
|
||||||
execute {
|
PreferenceScreen.PLAYER.addPreferences(
|
||||||
addResources("youtube", "layout.hide.general.hideLayoutComponentsPatch")
|
PreferenceScreenPreference(
|
||||||
|
key = "revanced_hide_description_components_screen",
|
||||||
|
preferences = setOf(
|
||||||
|
SwitchPreference("revanced_hide_ai_generated_video_summary_section"),
|
||||||
|
SwitchPreference("revanced_hide_ask_section"),
|
||||||
|
SwitchPreference("revanced_hide_attributes_section"),
|
||||||
|
SwitchPreference("revanced_hide_chapters_section"),
|
||||||
|
SwitchPreference("revanced_hide_featured_links_section"),
|
||||||
|
SwitchPreference("revanced_hide_featured_videos_section"),
|
||||||
|
SwitchPreference("revanced_hide_info_cards_section"),
|
||||||
|
SwitchPreference("revanced_hide_how_this_was_made_section"),
|
||||||
|
SwitchPreference("revanced_hide_hype_points"),
|
||||||
|
SwitchPreference("revanced_hide_key_concepts_section"),
|
||||||
|
SwitchPreference("revanced_hide_podcast_section"),
|
||||||
|
SwitchPreference("revanced_hide_subscribe_button"),
|
||||||
|
SwitchPreference("revanced_hide_transcript_section"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PreferenceScreenPreference(
|
||||||
|
"revanced_comments_screen",
|
||||||
|
preferences = setOf(
|
||||||
|
SwitchPreference("revanced_hide_comments_ai_chat_summary"),
|
||||||
|
SwitchPreference("revanced_hide_comments_ai_summary"),
|
||||||
|
SwitchPreference("revanced_hide_comments_channel_guidelines"),
|
||||||
|
SwitchPreference("revanced_hide_comments_by_members_header"),
|
||||||
|
SwitchPreference("revanced_hide_comments_section"),
|
||||||
|
SwitchPreference("revanced_hide_comments_community_guidelines"),
|
||||||
|
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
||||||
|
SwitchPreference("revanced_hide_comments_emoji_and_timestamp_buttons"),
|
||||||
|
SwitchPreference("revanced_hide_comments_preview_comment"),
|
||||||
|
SwitchPreference("revanced_hide_comments_thanks_button"),
|
||||||
|
),
|
||||||
|
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||||
|
),
|
||||||
|
SwitchPreference("revanced_hide_channel_bar"),
|
||||||
|
SwitchPreference("revanced_hide_channel_watermark"),
|
||||||
|
SwitchPreference("revanced_hide_crowdfunding_box"),
|
||||||
|
SwitchPreference("revanced_hide_emergency_box"),
|
||||||
|
SwitchPreference("revanced_hide_info_panels"),
|
||||||
|
SwitchPreference("revanced_hide_join_membership_button"),
|
||||||
|
SwitchPreference("revanced_hide_medical_panels"),
|
||||||
|
SwitchPreference("revanced_hide_quick_actions"),
|
||||||
|
SwitchPreference("revanced_hide_related_videos"),
|
||||||
|
SwitchPreference("revanced_hide_subscribers_community_guidelines"),
|
||||||
|
SwitchPreference("revanced_hide_timed_reactions"),
|
||||||
|
)
|
||||||
|
|
||||||
PreferenceScreen.PLAYER.addPreferences(
|
PreferenceScreen.FEED.addPreferences(
|
||||||
PreferenceScreenPreference(
|
PreferenceScreenPreference(
|
||||||
key = "revanced_hide_description_components_screen",
|
key = "revanced_hide_keyword_content_screen",
|
||||||
preferences = setOf(
|
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||||
SwitchPreference("revanced_hide_ai_generated_video_summary_section"),
|
preferences = setOf(
|
||||||
SwitchPreference("revanced_hide_ask_section"),
|
SwitchPreference("revanced_hide_keyword_content_home"),
|
||||||
SwitchPreference("revanced_hide_attributes_section"),
|
SwitchPreference("revanced_hide_keyword_content_subscriptions"),
|
||||||
SwitchPreference("revanced_hide_chapters_section"),
|
SwitchPreference("revanced_hide_keyword_content_search"),
|
||||||
SwitchPreference("revanced_hide_featured_links_section"),
|
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
||||||
SwitchPreference("revanced_hide_featured_videos_section"),
|
NonInteractivePreference(
|
||||||
SwitchPreference("revanced_hide_info_cards_section"),
|
key = "revanced_hide_keyword_content_about",
|
||||||
SwitchPreference("revanced_hide_how_this_was_made_section"),
|
tag = "app.revanced.extension.shared.settings.preference.BulletPointPreference"
|
||||||
SwitchPreference("revanced_hide_hype_points"),
|
),
|
||||||
SwitchPreference("revanced_hide_key_concepts_section"),
|
NonInteractivePreference(
|
||||||
SwitchPreference("revanced_hide_podcast_section"),
|
key = "revanced_hide_keyword_content_about_whole_words",
|
||||||
SwitchPreference("revanced_hide_subscribe_button"),
|
tag = "app.revanced.extension.youtube.settings.preference.HtmlPreference",
|
||||||
SwitchPreference("revanced_hide_transcript_section"),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PreferenceScreenPreference(
|
),
|
||||||
"revanced_comments_screen",
|
PreferenceScreenPreference(
|
||||||
preferences = setOf(
|
key = "revanced_hide_filter_bar_screen",
|
||||||
SwitchPreference("revanced_hide_comments_ai_chat_summary"),
|
preferences = setOf(
|
||||||
SwitchPreference("revanced_hide_comments_ai_summary"),
|
SwitchPreference("revanced_hide_filter_bar_feed_in_feed"),
|
||||||
SwitchPreference("revanced_hide_comments_channel_guidelines"),
|
SwitchPreference("revanced_hide_filter_bar_feed_in_related_videos"),
|
||||||
SwitchPreference("revanced_hide_comments_by_members_header"),
|
SwitchPreference("revanced_hide_filter_bar_feed_in_search"),
|
||||||
SwitchPreference("revanced_hide_comments_section"),
|
SwitchPreference("revanced_hide_filter_bar_feed_in_history"),
|
||||||
SwitchPreference("revanced_hide_comments_community_guidelines"),
|
|
||||||
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
|
||||||
SwitchPreference("revanced_hide_comments_emoji_and_timestamp_buttons"),
|
|
||||||
SwitchPreference("revanced_hide_comments_preview_comment"),
|
|
||||||
SwitchPreference("revanced_hide_comments_thanks_button"),
|
|
||||||
),
|
|
||||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
|
||||||
),
|
),
|
||||||
SwitchPreference("revanced_hide_channel_bar"),
|
),
|
||||||
SwitchPreference("revanced_hide_channel_watermark"),
|
PreferenceScreenPreference(
|
||||||
SwitchPreference("revanced_hide_crowdfunding_box"),
|
key = "revanced_channel_screen",
|
||||||
SwitchPreference("revanced_hide_emergency_box"),
|
preferences = setOf(
|
||||||
SwitchPreference("revanced_hide_info_panels"),
|
SwitchPreference("revanced_hide_community_button"),
|
||||||
SwitchPreference("revanced_hide_join_membership_button"),
|
SwitchPreference("revanced_hide_for_you_shelf"),
|
||||||
SwitchPreference("revanced_hide_medical_panels"),
|
SwitchPreference("revanced_hide_join_button"),
|
||||||
SwitchPreference("revanced_hide_quick_actions"),
|
SwitchPreference("revanced_hide_links_preview"),
|
||||||
SwitchPreference("revanced_hide_related_videos"),
|
SwitchPreference("revanced_hide_members_shelf"),
|
||||||
SwitchPreference("revanced_hide_subscribers_community_guidelines"),
|
SwitchPreference("revanced_hide_store_button"),
|
||||||
SwitchPreference("revanced_hide_timed_reactions"),
|
SwitchPreference("revanced_hide_subscribe_button_in_channel_page"),
|
||||||
)
|
|
||||||
|
|
||||||
PreferenceScreen.FEED.addPreferences(
|
|
||||||
PreferenceScreenPreference(
|
|
||||||
key = "revanced_hide_keyword_content_screen",
|
|
||||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
|
||||||
preferences = setOf(
|
|
||||||
SwitchPreference("revanced_hide_keyword_content_home"),
|
|
||||||
SwitchPreference("revanced_hide_keyword_content_subscriptions"),
|
|
||||||
SwitchPreference("revanced_hide_keyword_content_search"),
|
|
||||||
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
|
||||||
NonInteractivePreference(
|
|
||||||
key = "revanced_hide_keyword_content_about",
|
|
||||||
tag = "app.revanced.extension.shared.settings.preference.BulletPointPreference"
|
|
||||||
),
|
|
||||||
NonInteractivePreference(
|
|
||||||
key = "revanced_hide_keyword_content_about_whole_words",
|
|
||||||
tag = "app.revanced.extension.youtube.settings.preference.HtmlPreference",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PreferenceScreenPreference(
|
),
|
||||||
key = "revanced_hide_filter_bar_screen",
|
SwitchPreference("revanced_hide_album_cards"),
|
||||||
preferences = setOf(
|
SwitchPreference("revanced_hide_artist_cards"),
|
||||||
SwitchPreference("revanced_hide_filter_bar_feed_in_feed"),
|
SwitchPreference("revanced_hide_chips_shelf"),
|
||||||
SwitchPreference("revanced_hide_filter_bar_feed_in_related_videos"),
|
SwitchPreference("revanced_hide_community_posts"),
|
||||||
SwitchPreference("revanced_hide_filter_bar_feed_in_search"),
|
SwitchPreference("revanced_hide_compact_banner"),
|
||||||
SwitchPreference("revanced_hide_filter_bar_feed_in_history"),
|
SwitchPreference("revanced_hide_expandable_card"),
|
||||||
),
|
SwitchPreference("revanced_hide_floating_microphone_button"),
|
||||||
),
|
SwitchPreference(
|
||||||
PreferenceScreenPreference(
|
key = "revanced_hide_horizontal_shelves",
|
||||||
key = "revanced_channel_screen",
|
tag = "app.revanced.extension.shared.settings.preference.BulletPointSwitchPreference"
|
||||||
preferences = setOf(
|
),
|
||||||
SwitchPreference("revanced_hide_community_button"),
|
SwitchPreference("revanced_hide_image_shelf"),
|
||||||
SwitchPreference("revanced_hide_for_you_shelf"),
|
SwitchPreference("revanced_hide_latest_posts"),
|
||||||
SwitchPreference("revanced_hide_join_button"),
|
SwitchPreference("revanced_hide_mix_playlists"),
|
||||||
SwitchPreference("revanced_hide_links_preview"),
|
SwitchPreference("revanced_hide_movies_section"),
|
||||||
SwitchPreference("revanced_hide_members_shelf"),
|
SwitchPreference("revanced_hide_notify_me_button"),
|
||||||
SwitchPreference("revanced_hide_store_button"),
|
SwitchPreference("revanced_hide_playables"),
|
||||||
SwitchPreference("revanced_hide_subscribe_button_in_channel_page"),
|
SwitchPreference("revanced_hide_show_more_button"),
|
||||||
),
|
SwitchPreference("revanced_hide_surveys"),
|
||||||
),
|
SwitchPreference("revanced_hide_ticket_shelf"),
|
||||||
SwitchPreference("revanced_hide_album_cards"),
|
SwitchPreference("revanced_hide_upload_time"),
|
||||||
SwitchPreference("revanced_hide_artist_cards"),
|
SwitchPreference("revanced_hide_video_recommendation_labels"),
|
||||||
SwitchPreference("revanced_hide_chips_shelf"),
|
SwitchPreference("revanced_hide_view_count"),
|
||||||
SwitchPreference("revanced_hide_community_posts"),
|
SwitchPreference("revanced_hide_visual_spacer"),
|
||||||
SwitchPreference("revanced_hide_compact_banner"),
|
SwitchPreference("revanced_hide_doodles"),
|
||||||
SwitchPreference("revanced_hide_expandable_card"),
|
)
|
||||||
SwitchPreference("revanced_hide_floating_microphone_button"),
|
|
||||||
SwitchPreference(
|
|
||||||
key = "revanced_hide_horizontal_shelves",
|
|
||||||
tag = "app.revanced.extension.shared.settings.preference.BulletPointSwitchPreference"
|
|
||||||
),
|
|
||||||
SwitchPreference("revanced_hide_image_shelf"),
|
|
||||||
SwitchPreference("revanced_hide_latest_posts"),
|
|
||||||
SwitchPreference("revanced_hide_mix_playlists"),
|
|
||||||
SwitchPreference("revanced_hide_movies_section"),
|
|
||||||
SwitchPreference("revanced_hide_notify_me_button"),
|
|
||||||
SwitchPreference("revanced_hide_playables"),
|
|
||||||
SwitchPreference("revanced_hide_show_more_button"),
|
|
||||||
SwitchPreference("revanced_hide_surveys"),
|
|
||||||
SwitchPreference("revanced_hide_ticket_shelf"),
|
|
||||||
SwitchPreference("revanced_hide_upload_time"),
|
|
||||||
SwitchPreference("revanced_hide_video_recommendation_labels"),
|
|
||||||
SwitchPreference("revanced_hide_view_count"),
|
|
||||||
SwitchPreference("revanced_hide_visual_spacer"),
|
|
||||||
SwitchPreference("revanced_hide_doodles"),
|
|
||||||
)
|
|
||||||
|
|
||||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
// region Mix playlists
|
||||||
PreferenceScreenPreference(
|
|
||||||
key = "revanced_custom_filter_screen",
|
|
||||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
|
||||||
preferences = setOf(
|
|
||||||
SwitchPreference("revanced_custom_filter"),
|
|
||||||
TextPreference("revanced_custom_filter_strings", inputType = InputType.TEXT_MULTI_LINE),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
addLithoFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
|
(if (is_20_09_or_greater) parseElementFromBufferFingerprint
|
||||||
addLithoFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME)
|
else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint
|
||||||
addLithoFilter(COMMENTS_FILTER_CLASS_NAME)
|
else parseElementFromBufferLegacy1901Fingerprint).let {
|
||||||
addLithoFilter(KEYWORD_FILTER_CLASS_NAME)
|
it.method.apply {
|
||||||
addLithoFilter(CUSTOM_FILTER_CLASS_NAME)
|
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)
|
||||||
|
|
||||||
// region Mix playlists
|
addInstructionsWithLabels(
|
||||||
|
insertIndex,
|
||||||
(if (is_20_09_or_greater) parseElementFromBufferFingerprint
|
"""
|
||||||
else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint
|
|
||||||
else parseElementFromBufferLegacy1901Fingerprint).let {
|
|
||||||
it.method.apply {
|
|
||||||
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(
|
|
||||||
insertIndex,
|
|
||||||
"""
|
|
||||||
invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
|
invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
|
||||||
move-result v$freeRegister
|
move-result v$freeRegister
|
||||||
if-eqz v$freeRegister, :show
|
if-eqz v$freeRegister, :show
|
||||||
@@ -294,193 +273,192 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
:show
|
:show
|
||||||
nop
|
nop
|
||||||
""",
|
""",
|
||||||
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
|
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Watermark (legacy code for old versions of YouTube)
|
// region Watermark (legacy code for old versions of YouTube)
|
||||||
|
|
||||||
showWatermarkFingerprint.match(
|
showWatermarkFingerprint.match(
|
||||||
playerOverlayFingerprint.originalClassDef,
|
playerOverlayFingerprint.originalClassDef,
|
||||||
).method.apply {
|
).method.apply {
|
||||||
val index = implementation!!.instructions.size - 5
|
val index = implementation!!.instructions.size - 5
|
||||||
|
|
||||||
removeInstruction(index)
|
removeInstruction(index)
|
||||||
addInstructions(
|
addInstructions(
|
||||||
index,
|
index,
|
||||||
"""
|
"""
|
||||||
invoke-static {}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->showWatermark()Z
|
invoke-static {}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->showWatermark()Z
|
||||||
move-result p2
|
move-result p2
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Show more button
|
// region Show more button
|
||||||
|
|
||||||
hideShowMoreButtonFingerprint.method.apply {
|
hideShowMoreButtonFingerprint.method.apply {
|
||||||
val moveRegisterIndex = hideShowMoreButtonFingerprint.patternMatch!!.endIndex
|
val moveRegisterIndex = hideShowMoreButtonFingerprint.patternMatch!!.endIndex
|
||||||
val viewRegister = getInstruction<OneRegisterInstruction>(moveRegisterIndex).registerA
|
val viewRegister = getInstruction<OneRegisterInstruction>(moveRegisterIndex).registerA
|
||||||
|
|
||||||
|
val insertIndex = moveRegisterIndex + 1
|
||||||
|
addInstruction(
|
||||||
|
insertIndex,
|
||||||
|
"invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||||
|
"->hideShowMoreButton(Landroid/view/View;)V",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region crowdfunding box
|
||||||
|
crowdfundingBoxFingerprint.let {
|
||||||
|
it.method.apply {
|
||||||
|
val insertIndex = it.patternMatch!!.endIndex
|
||||||
|
val objectRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
val insertIndex = moveRegisterIndex + 1
|
|
||||||
addInstruction(
|
addInstruction(
|
||||||
insertIndex,
|
insertIndex,
|
||||||
"invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
"invoke-static {v$objectRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||||
"->hideShowMoreButton(Landroid/view/View;)V",
|
"->hideCrowdfundingBox(Landroid/view/View;)V",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region crowdfunding box
|
// region hide album cards
|
||||||
crowdfundingBoxFingerprint.let {
|
|
||||||
it.method.apply {
|
|
||||||
val insertIndex = it.patternMatch!!.endIndex
|
|
||||||
val objectRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
|
||||||
|
|
||||||
addInstruction(
|
albumCardsFingerprint.let {
|
||||||
insertIndex,
|
it.method.apply {
|
||||||
"invoke-static {v$objectRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
val checkCastAnchorIndex = it.patternMatch!!.endIndex
|
||||||
"->hideCrowdfundingBox(Landroid/view/View;)V",
|
val insertIndex = checkCastAnchorIndex + 1
|
||||||
)
|
val register = getInstruction<OneRegisterInstruction>(checkCastAnchorIndex).registerA
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
addInstruction(
|
||||||
|
insertIndex,
|
||||||
// region hide album cards
|
"invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||||
|
|
||||||
albumCardsFingerprint.let {
|
|
||||||
it.method.apply {
|
|
||||||
val checkCastAnchorIndex = it.patternMatch!!.endIndex
|
|
||||||
val insertIndex = checkCastAnchorIndex + 1
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(checkCastAnchorIndex).registerA
|
|
||||||
|
|
||||||
addInstruction(
|
|
||||||
insertIndex,
|
|
||||||
"invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
|
||||||
"->hideAlbumCard(Landroid/view/View;)V",
|
"->hideAlbumCard(Landroid/view/View;)V",
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region hide floating microphone
|
// region hide floating microphone
|
||||||
|
|
||||||
showFloatingMicrophoneButtonFingerprint.method.apply {
|
showFloatingMicrophoneButtonFingerprint.method.apply {
|
||||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(fabButtonId)
|
val literalIndex = indexOfFirstLiteralInstructionOrThrow(fabButtonId)
|
||||||
val booleanIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.IGET_BOOLEAN)
|
val booleanIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.IGET_BOOLEAN)
|
||||||
val register = getInstruction<TwoRegisterInstruction>(booleanIndex).registerA
|
val register = getInstruction<TwoRegisterInstruction>(booleanIndex).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
booleanIndex + 1,
|
booleanIndex + 1,
|
||||||
"""
|
"""
|
||||||
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z
|
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z
|
||||||
move-result v$register
|
move-result v$register
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region 'Yoodles'
|
||||||
|
|
||||||
|
yoodlesImageViewFingerprint.method.apply {
|
||||||
|
findInstructionIndicesReversedOrThrow {
|
||||||
|
getReference<MethodReference>()?.name == "setImageDrawable"
|
||||||
|
}.forEach { insertIndex ->
|
||||||
|
val drawableRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
|
||||||
|
val imageViewRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
|
||||||
|
|
||||||
|
replaceInstruction(
|
||||||
|
insertIndex,
|
||||||
|
"invoke-static { v$imageViewRegister, v$drawableRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->" +
|
||||||
|
"setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region 'Yoodles'
|
|
||||||
|
|
||||||
yoodlesImageViewFingerprint.method.apply {
|
// region hide view count
|
||||||
findInstructionIndicesReversedOrThrow {
|
|
||||||
getReference<MethodReference>()?.name == "setImageDrawable"
|
|
||||||
}.forEach { insertIndex ->
|
|
||||||
val drawableRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
|
|
||||||
val imageViewRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
|
|
||||||
|
|
||||||
replaceInstruction(
|
hideViewCountFingerprint.method.apply {
|
||||||
insertIndex,
|
val startIndex = hideViewCountFingerprint.patternMatch!!.startIndex
|
||||||
"invoke-static { v$imageViewRegister, v$drawableRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->" +
|
var returnStringRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA
|
||||||
"setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V"
|
|
||||||
)
|
// Find the instruction where the text dimension is retrieved.
|
||||||
}
|
val applyDimensionIndex = indexOfFirstInstructionReversedOrThrow {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
opcode == Opcode.INVOKE_STATIC &&
|
||||||
|
reference?.definingClass == "Landroid/util/TypedValue;" &&
|
||||||
|
reference.returnType == "F" &&
|
||||||
|
reference.name == "applyDimension" &&
|
||||||
|
reference.parameterTypes == listOf("I", "F", "Landroid/util/DisplayMetrics;")
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// A float value is passed which is used to determine subtitle text size.
|
||||||
|
val floatDimensionRegister = getInstruction<OneRegisterInstruction>(
|
||||||
|
applyDimensionIndex + 1
|
||||||
|
).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
// region hide view count
|
applyDimensionIndex - 1,
|
||||||
|
"""
|
||||||
hideViewCountFingerprint.method.apply {
|
|
||||||
val startIndex = hideViewCountFingerprint.patternMatch!!.startIndex
|
|
||||||
var returnStringRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA
|
|
||||||
|
|
||||||
// Find the instruction where the text dimension is retrieved.
|
|
||||||
val applyDimensionIndex = indexOfFirstInstructionReversedOrThrow {
|
|
||||||
val reference = getReference<MethodReference>()
|
|
||||||
opcode == Opcode.INVOKE_STATIC &&
|
|
||||||
reference?.definingClass == "Landroid/util/TypedValue;" &&
|
|
||||||
reference.returnType == "F" &&
|
|
||||||
reference.name == "applyDimension" &&
|
|
||||||
reference.parameterTypes == listOf("I", "F", "Landroid/util/DisplayMetrics;")
|
|
||||||
}
|
|
||||||
|
|
||||||
// A float value is passed which is used to determine subtitle text size.
|
|
||||||
val floatDimensionRegister = getInstruction<OneRegisterInstruction>(
|
|
||||||
applyDimensionIndex + 1
|
|
||||||
).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
applyDimensionIndex - 1,
|
|
||||||
"""
|
|
||||||
invoke-static { v$returnStringRegister, v$floatDimensionRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->modifyFeedSubtitleSpan(Landroid/text/SpannableString;F)Landroid/text/SpannableString;
|
invoke-static { v$returnStringRegister, v$floatDimensionRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->modifyFeedSubtitleSpan(Landroid/text/SpannableString;F)Landroid/text/SpannableString;
|
||||||
move-result-object v$returnStringRegister
|
move-result-object v$returnStringRegister
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region hide filter bar
|
// region hide filter bar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patch a [Method] with a given [instructions].
|
* Patch a [Method] with a given [instructions].
|
||||||
*
|
*
|
||||||
* @param RegisterInstruction The type of instruction to get the register from.
|
* @param RegisterInstruction The type of instruction to get the register from.
|
||||||
* @param insertIndexOffset The offset to add to the end index of the [Match.patternMatch].
|
* @param insertIndexOffset The offset to add to the end index of the [Match.patternMatch].
|
||||||
* @param hookRegisterOffset The offset to add to the register of the hook.
|
* @param hookRegisterOffset The offset to add to the register of the hook.
|
||||||
* @param instructions The instructions to add with the register as a parameter.
|
* @param instructions The instructions to add with the register as a parameter.
|
||||||
*/
|
*/
|
||||||
fun <RegisterInstruction : OneRegisterInstruction> Fingerprint.patch(
|
fun <RegisterInstruction : OneRegisterInstruction> Fingerprint.patch(
|
||||||
insertIndexOffset: Int = 0,
|
insertIndexOffset: Int = 0,
|
||||||
hookRegisterOffset: Int = 0,
|
hookRegisterOffset: Int = 0,
|
||||||
instructions: (Int) -> String,
|
instructions: (Int) -> String,
|
||||||
) = method.apply {
|
) = method.apply {
|
||||||
val endIndex = patternMatch!!.endIndex
|
val endIndex = patternMatch!!.endIndex
|
||||||
|
|
||||||
val insertIndex = endIndex + insertIndexOffset
|
val insertIndex = endIndex + insertIndexOffset
|
||||||
val register =
|
val register =
|
||||||
getInstruction<RegisterInstruction>(endIndex + hookRegisterOffset).registerA
|
getInstruction<RegisterInstruction>(endIndex + hookRegisterOffset).registerA
|
||||||
|
|
||||||
addInstructions(insertIndex, instructions(register))
|
addInstructions(insertIndex, instructions(register))
|
||||||
}
|
}
|
||||||
|
|
||||||
filterBarHeightFingerprint.patch<TwoRegisterInstruction> { register ->
|
filterBarHeightFingerprint.patch<TwoRegisterInstruction> { register ->
|
||||||
"""
|
"""
|
||||||
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I
|
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I
|
||||||
move-result v$register
|
move-result v$register
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResultsChipBarFingerprint.patch<OneRegisterInstruction>(-1, -2) { register ->
|
searchResultsChipBarFingerprint.patch<OneRegisterInstruction>(-1, -2) { register ->
|
||||||
"""
|
"""
|
||||||
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I
|
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I
|
||||||
move-result v$register
|
move-result v$register
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
relatedChipCloudFingerprint.patch<OneRegisterInstruction>(1) { register ->
|
relatedChipCloudFingerprint.patch<OneRegisterInstruction>(1) { register ->
|
||||||
"invoke-static { v$register }, " +
|
"invoke-static { v$register }, " +
|
||||||
"$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V"
|
"$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.revanced.patches.all.misc.resources.addResources
|
|||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater
|
||||||
|
|||||||
@@ -1,33 +1,22 @@
|
|||||||
package app.revanced.patches.youtube.misc.debugging
|
package app.revanced.patches.youtube.misc.debugging
|
||||||
|
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch;
|
||||||
import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
|
import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val enableDebuggingPatch = enableDebuggingPatch(
|
val enableDebuggingPatch = enableDebuggingPatch(
|
||||||
block = {
|
sharedExtensionPatch = sharedExtensionPatch,
|
||||||
dependsOn(
|
settingsPatch = settingsPatch,
|
||||||
sharedExtensionPatch,
|
compatibleWithPackages = arrayOf(
|
||||||
settingsPatch,
|
"com.google.android.youtube" to setOf(
|
||||||
|
"19.34.42",
|
||||||
|
"20.07.39",
|
||||||
|
"20.13.41",
|
||||||
|
"20.14.43",
|
||||||
)
|
)
|
||||||
|
),
|
||||||
compatibleWith(
|
|
||||||
"com.google.android.youtube"(
|
|
||||||
"19.34.42",
|
|
||||||
"20.07.39",
|
|
||||||
"20.13.41",
|
|
||||||
"20.14.43",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
executeBlock = {
|
|
||||||
addResources("youtube", "misc.debugging.enableDebuggingPatch")
|
|
||||||
},
|
|
||||||
hookStringFeatureFlag = true,
|
hookStringFeatureFlag = true,
|
||||||
preferenceScreen = PreferenceScreen.MISC,
|
preferenceScreen = PreferenceScreen.MISC,
|
||||||
additionalDebugPreferences = listOf(SwitchPreference("revanced_debug_protobuffer"))
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,58 +1,8 @@
|
|||||||
package app.revanced.patches.youtube.misc.litho.filter
|
package app.revanced.patches.youtube.misc.litho.filter
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import app.revanced.util.containsLiteralInstruction
|
|
||||||
import app.revanced.util.literal
|
import app.revanced.util.literal
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal val componentContextParserFingerprint = fingerprint {
|
|
||||||
strings("Number of bits must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val componentCreateFingerprint = fingerprint {
|
|
||||||
strings(
|
|
||||||
"Element missing correct type extension",
|
|
||||||
"Element missing type"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val lithoFilterFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
|
||||||
custom { _, classDef ->
|
|
||||||
classDef.endsWith("/LithoFilterPatch;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val protobufBufferReferenceFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
|
||||||
returns("V")
|
|
||||||
parameters("I", "Ljava/nio/ByteBuffer;")
|
|
||||||
opcodes(
|
|
||||||
Opcode.IPUT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.SUB_INT_2ADDR,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val emptyComponentFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
|
|
||||||
parameters()
|
|
||||||
strings("EmptyComponent")
|
|
||||||
custom { _, classDef ->
|
|
||||||
classDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val lithoThreadExecutorFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
|
||||||
parameters("I", "I", "I")
|
|
||||||
custom { method, classDef ->
|
|
||||||
classDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" &&
|
|
||||||
method.containsLiteralInstruction(1L) // 1L = default thread timeout.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val lithoComponentNameUpbFeatureFlagFingerprint = fingerprint {
|
internal val lithoComponentNameUpbFeatureFlagFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
|||||||
@@ -3,225 +3,68 @@
|
|||||||
package app.revanced.patches.youtube.misc.litho.filter
|
package app.revanced.patches.youtube.misc.litho.filter
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
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.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
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_19_17_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_25_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.is_20_05_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
|
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
|
||||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
|
||||||
import app.revanced.util.findFreeRegister
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
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.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|
||||||
|
|
||||||
|
@Deprecated("Use the shared one instead", ReplaceWith("app.revanced.patches.shared.misc.litho.filter.addLithoFilter"))
|
||||||
lateinit var addLithoFilter: (String) -> Unit
|
lateinit var addLithoFilter: (String) -> Unit
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/LithoFilterPatch;"
|
val lithoFilterPatch = lithoFilterPatch(
|
||||||
|
componentCreateInsertionIndex = {
|
||||||
val lithoFilterPatch = bytecodePatch(
|
if (is_19_17_or_greater) {
|
||||||
description = "Hooks the method which parses the bytes into a ComponentContext to filter components.",
|
indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
|
||||||
|
} else {
|
||||||
|
// 19.16 clobbers p2 so must check at start of the method
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
conversionContextFingerprintToString = conversionContextFingerprintToString,
|
||||||
|
executeBlock = BytecodePatchContext::executeBlock,
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(versionCheckPatch)
|
||||||
sharedExtensionPatch,
|
}
|
||||||
versionCheckPatch,
|
|
||||||
)
|
private fun BytecodePatchContext.executeBlock() {
|
||||||
|
// region A/B test of new Litho native code.
|
||||||
var filterCount = 0
|
|
||||||
|
// Turn off native code that handles litho component names. If this feature is on then nearly
|
||||||
/**
|
// all litho components have a null name and identifier/path filtering is completely broken.
|
||||||
* The following patch inserts a hook into the method that parses the bytes into a ComponentContext.
|
//
|
||||||
* This method contains a StringBuilder object that represents the pathBuilder of the component.
|
// Flag was removed in 20.05. It appears a new flag might be used instead (45660109L),
|
||||||
* The pathBuilder is used to filter components by their path.
|
// but if the flag is forced on then litho filtering still works correctly.
|
||||||
*
|
if (is_19_25_or_greater && !is_20_05_or_greater) {
|
||||||
* Additionally, the method contains a reference to the component's identifier.
|
lithoComponentNameUpbFeatureFlagFingerprint.method.apply {
|
||||||
* The identifier is used to filter components by their identifier.
|
// Don't use return early, so the debug patch logs if this was originally on.
|
||||||
*
|
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN)
|
||||||
* The protobuf buffer is passed along from a different injection point before the filtering occurs.
|
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||||
* The buffer is a large byte array that represents the component tree.
|
|
||||||
* This byte array is searched for strings that indicate the current component.
|
addInstruction(insertIndex, "const/4 v$register, 0x0")
|
||||||
*
|
}
|
||||||
* All modifications done here must allow all the original code to still execute
|
}
|
||||||
* even when filtering, otherwise memory leaks or poor app performance may occur.
|
|
||||||
*
|
// Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf).
|
||||||
* The following pseudocode shows how this patch works:
|
// If this is enabled, then the litho protobuffer hook will always show an empty buffer
|
||||||
*
|
// since it's no longer handled by the hooked Java code.
|
||||||
* class SomeOtherClass {
|
lithoConverterBufferUpbFeatureFlagFingerprint.method.apply {
|
||||||
* // Called before ComponentContextParser.parseComponent() method.
|
val index = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT)
|
||||||
* public void someOtherMethod(ByteBuffer byteBuffer) {
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
* ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
|
|
||||||
* ...
|
addInstruction(index + 1, "const/4 v$register, 0x0")
|
||||||
* }
|
}
|
||||||
* }
|
|
||||||
*
|
// endregion
|
||||||
* class CreateComponentClass {
|
|
||||||
* public Component createComponent() {
|
// Set the addLithoFilter function to the one from the shared patch.
|
||||||
* ...
|
// This is done for backwards compatibility.
|
||||||
*
|
addLithoFilter = app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
* if (extensionClass.shouldFilter(identifier, path)) { // Inserted by this patch.
|
|
||||||
* return emptyComponent;
|
|
||||||
* }
|
|
||||||
* return originalUnpatchedComponent; // Original code.
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
execute {
|
|
||||||
// Remove dummy filter from extenion static field
|
|
||||||
// and add the filters included during patching.
|
|
||||||
lithoFilterFingerprint.method.apply {
|
|
||||||
removeInstructions(2, 4) // Remove dummy filter.
|
|
||||||
|
|
||||||
addLithoFilter = { classDescriptor ->
|
|
||||||
addInstructions(
|
|
||||||
2,
|
|
||||||
"""
|
|
||||||
new-instance v1, $classDescriptor
|
|
||||||
invoke-direct { v1 }, $classDescriptor-><init>()V
|
|
||||||
const/16 v2, ${filterCount++}
|
|
||||||
aput-object v1, v0, v2
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Pass the buffer into extension.
|
|
||||||
|
|
||||||
protobufBufferReferenceFingerprint.method.addInstruction(
|
|
||||||
0,
|
|
||||||
"invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Hook the method that parses bytes into a ComponentContext.
|
|
||||||
|
|
||||||
// Allow the method to run to completion, and override the
|
|
||||||
// return value with an empty component if it should be filtered.
|
|
||||||
// It is important to allow the original code to always run to completion,
|
|
||||||
// otherwise high memory usage and poor app performance can occur.
|
|
||||||
|
|
||||||
// Find the identifier/path fields of the conversion context.
|
|
||||||
val conversionContextIdentifierField = componentContextParserFingerprint.let {
|
|
||||||
// Identifier field is loaded just before the string declaration.
|
|
||||||
val index = it.method.indexOfFirstInstructionReversedOrThrow(
|
|
||||||
it.stringMatches!!.first().index
|
|
||||||
) {
|
|
||||||
val reference = getReference<FieldReference>()
|
|
||||||
reference?.definingClass == conversionContextFingerprintToString.originalClassDef.type
|
|
||||||
&& reference.type == "Ljava/lang/String;"
|
|
||||||
}
|
|
||||||
|
|
||||||
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val conversionContextPathBuilderField = conversionContextFingerprintToString.originalClassDef
|
|
||||||
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
|
|
||||||
|
|
||||||
// Find class and methods to create an empty component.
|
|
||||||
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
|
|
||||||
// The only static method in the class.
|
|
||||||
method -> AccessFlags.STATIC.isSet(method.accessFlags)
|
|
||||||
}
|
|
||||||
val emptyComponentField = classBy {
|
|
||||||
// Only one field that matches.
|
|
||||||
it.type == builderMethodDescriptor.returnType
|
|
||||||
}!!.immutableClass.fields.single()
|
|
||||||
|
|
||||||
componentCreateFingerprint.method.apply {
|
|
||||||
val insertIndex = if (is_19_17_or_greater) {
|
|
||||||
indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
|
|
||||||
} else {
|
|
||||||
// 19.16 clobbers p2 so must check at start of the method and not at the return index.
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
val freeRegister = findFreeRegister(insertIndex)
|
|
||||||
val identifierRegister = findFreeRegister(insertIndex, freeRegister)
|
|
||||||
val pathRegister = findFreeRegister(insertIndex, freeRegister, identifierRegister)
|
|
||||||
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
insertIndex,
|
|
||||||
"""
|
|
||||||
move-object/from16 v$freeRegister, p2
|
|
||||||
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
|
|
||||||
iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField
|
|
||||||
invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
|
|
||||||
move-result v$freeRegister
|
|
||||||
if-eqz v$freeRegister, :unfiltered
|
|
||||||
|
|
||||||
# Return an empty component
|
|
||||||
move-object/from16 v$freeRegister, p1
|
|
||||||
invoke-static { v$freeRegister }, $builderMethodDescriptor
|
|
||||||
move-result-object v$freeRegister
|
|
||||||
iget-object v$freeRegister, v$freeRegister, $emptyComponentField
|
|
||||||
return-object v$freeRegister
|
|
||||||
|
|
||||||
:unfiltered
|
|
||||||
nop
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
|
|
||||||
// region Change Litho thread executor to 1 thread to fix layout issue in unpatched YouTube.
|
|
||||||
|
|
||||||
lithoThreadExecutorFingerprint.method.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorCorePoolSize(I)I
|
|
||||||
move-result p1
|
|
||||||
invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorMaxThreads(I)I
|
|
||||||
move-result p2
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
|
|
||||||
// region A/B test of new Litho native code.
|
|
||||||
|
|
||||||
// Turn off native code that handles litho component names. If this feature is on then nearly
|
|
||||||
// all litho components have a null name and identifier/path filtering is completely broken.
|
|
||||||
//
|
|
||||||
// Flag was removed in 20.05. It appears a new flag might be used instead (45660109L),
|
|
||||||
// but if the flag is forced on then litho filtering still works correctly.
|
|
||||||
if (is_19_25_or_greater && !is_20_05_or_greater) {
|
|
||||||
lithoComponentNameUpbFeatureFlagFingerprint.method.apply {
|
|
||||||
// Don't use return early, so the debug patch logs if this was originally on.
|
|
||||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN)
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
|
||||||
|
|
||||||
addInstruction(insertIndex, "const/4 v$register, 0x0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf).
|
|
||||||
// If this is enabled, then the litho protobuffer hook will always show an empty buffer
|
|
||||||
// since it's no longer handled by the hooked Java code.
|
|
||||||
lithoConverterBufferUpbFeatureFlagFingerprint.method.apply {
|
|
||||||
val index = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT)
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
|
||||||
|
|
||||||
addInstruction(index + 1, "const/4 v$register, 0x0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
finalize {
|
|
||||||
lithoFilterFingerprint.method.replaceInstruction(0, "const/16 v0, $filterCount")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
|
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
|
||||||
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
|
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import app.revanced.patches.shared.misc.settings.preference.InputType
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package app.revanced.util
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Comments out the non-standard <app> and <patch> tags.
|
|
||||||
*
|
|
||||||
* Previously this was done on Crowdin after pushing.
|
|
||||||
* But Crowdin preprocessing has randomly failed but still used the unmodified
|
|
||||||
* strings.xml file, which effectively deletes all patch strings from Crowdin.
|
|
||||||
*/
|
|
||||||
internal fun main(args: Array<String>) {
|
|
||||||
if (args.size != 2) {
|
|
||||||
throw RuntimeException("Exactly two arguments are required: <input_file> <output_file>")
|
|
||||||
}
|
|
||||||
|
|
||||||
val inputFilePath = args[0]
|
|
||||||
val inputFile = File(inputFilePath)
|
|
||||||
if (!inputFile.exists()) {
|
|
||||||
throw RuntimeException(
|
|
||||||
"Input file not found: $inputFilePath currentDirectory: " + File(".").canonicalPath
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comment out the non-standard tags. Otherwise Crowdin interprets the file
|
|
||||||
// not as Android but instead a generic xml file where strings are
|
|
||||||
// identified by xml position and not key.
|
|
||||||
val content = inputFile.readText()
|
|
||||||
val tagRegex = """((<app\s+.*>)|(</app>)|(<patch\s+.*>)|(</patch>))""".toRegex()
|
|
||||||
val modifiedContent = content.replace(tagRegex, """<!-- $1 -->""")
|
|
||||||
|
|
||||||
// Write modified content to the output file (creates file if it doesn't exist).
|
|
||||||
val outputFilePath = args[1]
|
|
||||||
val outputFile = File(outputFilePath)
|
|
||||||
outputFile.parentFile?.mkdirs()
|
|
||||||
outputFile.writeText(modifiedContent)
|
|
||||||
|
|
||||||
println("Preprocessed strings.xml to: $outputFilePath")
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -224,12 +224,14 @@ Second \"item\" text"</string>
|
|||||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.announcements.announcementsPatch">
|
<patch id="misc.announcements.announcementsPatch">
|
||||||
<string name="revanced_announcements_dialog_dismiss">খাৰিজ কৰক</string>
|
<string name="youtube.misc.announcements.announcementsPatch.revanced_announcements_dialog_dismiss">খাৰিজ কৰক</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.loopVideoPatch">
|
<patch id="misc.loopvideo.loopVideoPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -21,60 +21,60 @@ Second \"item\" text"</string>
|
|||||||
<resources>
|
<resources>
|
||||||
<app id="shared">
|
<app id="shared">
|
||||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||||
<string name="revanced_custom_branding_name_title">نام برنامه</string>
|
<string name="shared.layout.branding.baseCustomBrandingPatch.revanced_custom_branding_name_title">نام برنامه</string>
|
||||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||||
<string name="revanced_custom_branding_icon_title">آیکون برنامه</string>
|
<string name="shared.layout.branding.baseCustomBrandingPatch.revanced_custom_branding_icon_title">آیکون برنامه</string>
|
||||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.checks.checkEnvironmentPatch">
|
<patch id="misc.checks.checkEnvironmentPatch">
|
||||||
<string name="revanced_check_environment_failed_title">بررسی ناموفق بود</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_failed_title">بررسی ناموفق بود</string>
|
||||||
<string name="revanced_check_environment_dialog_open_official_source_button">رفتن به وبسایت رسمی</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_dialog_open_official_source_button">رفتن به وبسایت رسمی</string>
|
||||||
<string name="revanced_check_environment_dialog_ignore_button">نادیده بگیر</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_dialog_ignore_button">نادیده بگیر</string>
|
||||||
<string name="revanced_check_environment_failed_message"><h5>به نظر نمیرسد این برنامه توسط شما وصله شده باشد.</h5><br>این برنامه ممکن است به درستی کار نکند، <b>ممکن است استفاده از آن مضر یا حتی خطرناک باشد</b>.<br><بر>این برنامه از قبل دریافت شده است یا این چک از قبل دریافت شده است else:<br><br><small>%1$s</small><br>اکیداً توصیه میشود که <b>این برنامه را حذف نصب کنید و خودتان آن را وصله کنید</b> برای اطمینان از اینکه از یک برنامه معتبر و ایمن استفاده میکنید.<p><br>اگر نادیده گرفته شود، این هشدار فقط دو بار نشان داده میشود.</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_failed_message"><h5>به نظر نمیرسد این برنامه توسط شما وصله شده باشد.</h5><br>این برنامه ممکن است به درستی کار نکند، <b>ممکن است استفاده از آن مضر یا حتی خطرناک باشد</b>.<br><بر>این برنامه از قبل دریافت شده است یا این چک از قبل دریافت شده است else:<br><br><small>%1$s</small><br>اکیداً توصیه میشود که <b>این برنامه را حذف نصب کنید و خودتان آن را وصله کنید</b> برای اطمینان از اینکه از یک برنامه معتبر و ایمن استفاده میکنید.<p><br>اگر نادیده گرفته شود، این هشدار فقط دو بار نشان داده میشود.</string>
|
||||||
<string name="revanced_check_environment_not_same_patching_device">روی دستگاه دیگری وصله شده است</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_same_patching_device">روی دستگاه دیگری وصله شده است</string>
|
||||||
<string name="revanced_check_environment_manager_not_expected_installer">به وسیله ReVanced Manager نصب نشده است</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_manager_not_expected_installer">به وسیله ReVanced Manager نصب نشده است</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time">بیشتر از ۱۰ دقیقه پیش وصله شده است</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time">بیشتر از ۱۰ دقیقه پیش وصله شده است</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time_days">%s روز پیش وصله شده است</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time_days">%s روز پیش وصله شده است</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time_invalid">تاریخ ایجاد APK مخدوش شده است</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time_invalid">تاریخ ایجاد APK مخدوش شده است</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.settings.settingsResourcePatch">
|
<patch id="misc.settings.settingsResourcePatch">
|
||||||
<string name="revanced_settings_submenu_title">تنظیمات</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_submenu_title">تنظیمات</string>
|
||||||
<string name="revanced_settings_reset">بازنشانی</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_reset">بازنشانی</string>
|
||||||
<string name="revanced_settings_restart">راهاندازی مجدد</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_restart">راهاندازی مجدد</string>
|
||||||
<string name="revanced_settings_import">واردکردن</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_import">واردکردن</string>
|
||||||
<string name="revanced_settings_import_copy">رونوشت</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_import_copy">رونوشت</string>
|
||||||
<string name="revanced_settings_import_reset">بازگرداندن تنظیمات ReVanced به پیشفرض</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_import_reset">بازگرداندن تنظیمات ReVanced به پیشفرض</string>
|
||||||
<string name="revanced_settings_import_success">%d تنظیمات وارد شدند</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_import_success">%d تنظیمات وارد شدند</string>
|
||||||
<string name="revanced_settings_import_failure_parse">واردکردن انجام نشد: %s</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_import_failure_parse">واردکردن انجام نشد: %s</string>
|
||||||
<string name="revanced_settings_search_hint">تنظیمات جستجو</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_search_hint">تنظیمات جستجو</string>
|
||||||
<string name="revanced_settings_search_no_results_title">نتایجی برای %s یافت نشد</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_search_no_results_title">نتایجی برای %s یافت نشد</string>
|
||||||
<string name="revanced_settings_search_no_results_summary">کلیدواژه دیگری را امتحان کنید</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_search_no_results_summary">کلیدواژه دیگری را امتحان کنید</string>
|
||||||
<string name="revanced_settings_search_remove_message">حذف از تاریخچه جستجو؟</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_search_remove_message">حذف از تاریخچه جستجو؟</string>
|
||||||
<string name="revanced_settings_search_empty_history_title">تاریخچه جستجو خالی است</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_search_empty_history_title">تاریخچه جستجو خالی است</string>
|
||||||
<string name="revanced_settings_search_history_title">نمایش تاریخچه جستجوی تنظیمات</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_search_history_title">نمایش تاریخچه جستجوی تنظیمات</string>
|
||||||
<string name="revanced_show_menu_icons_title">نمایش آیکون تنظیمات ReVanced</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_show_menu_icons_title">نمایش آیکون تنظیمات ReVanced</string>
|
||||||
<string name="revanced_show_menu_icons_summary_on">نمادهای تنظیمات نشان داده میشوند</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_show_menu_icons_summary_on">نمادهای تنظیمات نشان داده میشوند</string>
|
||||||
<string name="revanced_show_menu_icons_summary_off">نمادهای تنظیمات نمایش داده نمی شوند</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_show_menu_icons_summary_off">نمادهای تنظیمات نمایش داده نمی شوند</string>
|
||||||
<string name="revanced_language_title">زبان ReVanced</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_language_title">زبان ReVanced</string>
|
||||||
<string name="revanced_language_DEFAULT">زبان برنامه</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_language_DEFAULT">زبان برنامه</string>
|
||||||
<string name="revanced_pref_import_export_title">وارد کردن/صادر کردن</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_pref_import_export_title">وارد کردن/صادر کردن</string>
|
||||||
<string name="revanced_pref_import_export_summary">وارد کردن / صادر کردن تنظیمات ReVanced</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_pref_import_export_summary">وارد کردن / صادر کردن تنظیمات ReVanced</string>
|
||||||
<!-- Settings about dialog. -->
|
<!-- Settings about dialog. -->
|
||||||
<string name="revanced_settings_about_links_body">شما درحال استفاده از نسخه <i>%s</i> از پچ Revanced هستید</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_about_links_body">شما درحال استفاده از نسخه <i>%s</i> از پچ Revanced هستید</string>
|
||||||
<string name="revanced_settings_about_links_dev_header">توجه</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_about_links_dev_header">توجه</string>
|
||||||
<string name="revanced_settings_about_links_header">لینکهای رسمی</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_about_links_header">لینکهای رسمی</string>
|
||||||
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
|
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
|
||||||
and changes made here must also be made there. -->
|
and changes made here must also be made there. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||||
<string name="gms_core_toast_not_installed_message">MicroG GmsCore نصب نشده است. آنرا نصب کنید.</string>
|
<string name="shared.misc.gms.gmsCoreSupportResourcePatch.gms_core_toast_not_installed_message">MicroG GmsCore نصب نشده است. آنرا نصب کنید.</string>
|
||||||
<string name="gms_core_dialog_title">اقدام لازم است</string>
|
<string name="shared.misc.gms.gmsCoreSupportResourcePatch.gms_core_dialog_title">اقدام لازم است</string>
|
||||||
<string name="gms_core_dialog_open_website_text">باز کردن تارنما</string>
|
<string name="shared.misc.gms.gmsCoreSupportResourcePatch.gms_core_dialog_open_website_text">باز کردن تارنما</string>
|
||||||
<string name="gms_core_dialog_continue_text">ادامه</string>
|
<string name="shared.misc.gms.gmsCoreSupportResourcePatch.gms_core_dialog_continue_text">ادامه</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
@@ -82,22 +82,22 @@ Second \"item\" text"</string>
|
|||||||
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
|
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.debugging.enableDebuggingPatch">
|
<patch id="misc.debugging.enableDebuggingPatch">
|
||||||
<string name="revanced_debug_screen_title">عیبیابی</string>
|
<string name="shared.misc.debugging.enableDebuggingPatch.revanced_debug_screen_title">عیبیابی</string>
|
||||||
<string name="revanced_debug_screen_summary">فعال یا غیرفعال کردن گزینههای عیب یابی</string>
|
<string name="shared.misc.debugging.enableDebuggingPatch.revanced_debug_screen_summary">فعال یا غیرفعال کردن گزینههای عیب یابی</string>
|
||||||
<string name="revanced_debug_title">گزارش عیب</string>
|
<string name="shared.misc.debugging.enableDebuggingPatch.revanced_debug_title">گزارش عیب</string>
|
||||||
<string name="revanced_debug_summary_on">لاگ عیب فعال است</string>
|
<string name="shared.misc.debugging.enableDebuggingPatch.revanced_debug_summary_on">لاگ عیب فعال است</string>
|
||||||
<string name="revanced_debug_summary_off">لاگ عیب غیرفعال است</string>
|
<string name="shared.misc.debugging.enableDebuggingPatch.revanced_debug_summary_off">لاگ عیب غیرفعال است</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.privacy.sanitizeSharingLinksPatch">
|
<patch id="misc.privacy.sanitizeSharingLinksPatch">
|
||||||
</patch>
|
</patch>
|
||||||
</app>
|
</app>
|
||||||
<app id="youtube">
|
<app id="youtube">
|
||||||
<patch id="misc.settings.settingsPatch">
|
<patch id="misc.settings.settingsPatch">
|
||||||
<string name="revanced_settings_screen_00_about_title">درباره</string>
|
<string name="youtube.misc.settings.settingsPatch.revanced_settings_screen_00_about_title">درباره</string>
|
||||||
<string name="revanced_settings_screen_04_general_title">عمومی</string>
|
<string name="youtube.misc.settings.settingsPatch.revanced_settings_screen_04_general_title">عمومی</string>
|
||||||
<string name="revanced_settings_screen_05_player_title">اجراکننده</string>
|
<string name="youtube.misc.settings.settingsPatch.revanced_settings_screen_05_player_title">اجراکننده</string>
|
||||||
<string name="revanced_settings_screen_07_seekbar_title">نوار جریان پخش</string>
|
<string name="youtube.misc.settings.settingsPatch.revanced_settings_screen_07_seekbar_title">نوار جریان پخش</string>
|
||||||
<string name="revanced_settings_screen_12_video_title">ويدئو</string>
|
<string name="youtube.misc.settings.settingsPatch.revanced_settings_screen_12_video_title">ويدئو</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
|
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
|
||||||
</patch>
|
</patch>
|
||||||
@@ -108,8 +108,8 @@ Second \"item\" text"</string>
|
|||||||
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
|
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
|
||||||
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
|
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
|
||||||
This button usually appears when searching for a YT creator. -->
|
This button usually appears when searching for a YT creator. -->
|
||||||
<string name="revanced_hide_show_more_button_title">پنهان سازی دکمه \'نمایش بیشتر\'</string>
|
<string name="youtube.layout.hide.general.hideLayoutComponentsPatch.revanced_hide_show_more_button_title">پنهان سازی دکمه \'نمایش بیشتر\'</string>
|
||||||
<string name="revanced_hide_ticket_shelf_title">پنهان سازی قفسه بلیط</string>
|
<string name="youtube.layout.hide.general.hideLayoutComponentsPatch.revanced_hide_ticket_shelf_title">پنهان سازی قفسه بلیط</string>
|
||||||
<!-- 'People also watched' and 'You might also like' should be translated using the same localized wording YouTube displays. -->
|
<!-- 'People also watched' and 'You might also like' should be translated using the same localized wording YouTube displays. -->
|
||||||
<!-- https://logos.fandom.com/wiki/YouTube/Yoodles -->
|
<!-- https://logos.fandom.com/wiki/YouTube/Yoodles -->
|
||||||
<!-- 'Join' should be translated using the same localized wording YouTube displays.
|
<!-- 'Join' should be translated using the same localized wording YouTube displays.
|
||||||
@@ -241,12 +241,12 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
|
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
|
||||||
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title'. -->
|
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title'. -->
|
||||||
<string name="revanced_sb_general">عمومی</string>
|
<string name="youtube.layout.sponsorblock.sponsorBlockResourcePatch.revanced_sb_general">عمومی</string>
|
||||||
<string name="revanced_sb_settings_copy">رونوشت</string>
|
<string name="youtube.layout.sponsorblock.sponsorBlockResourcePatch.revanced_sb_settings_copy">رونوشت</string>
|
||||||
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
||||||
<!-- A segment start and end time, such as "02:10 to 03:40". -->
|
<!-- A segment start and end time, such as "02:10 to 03:40". -->
|
||||||
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
||||||
<string name="revanced_sb_about_title">درباره</string>
|
<string name="youtube.layout.sponsorblock.sponsorBlockResourcePatch.revanced_sb_about_title">درباره</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.formfactor.changeFormFactorPatch">
|
<patch id="layout.formfactor.changeFormFactorPatch">
|
||||||
</patch>
|
</patch>
|
||||||
@@ -283,6 +283,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
@@ -356,10 +358,10 @@ Second \"item\" text"</string>
|
|||||||
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title'. -->
|
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title'. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.settings.settingsPatch">
|
<patch id="misc.settings.settingsPatch">
|
||||||
<string name="revanced_about_title">درباره</string>
|
<string name="twitch.misc.settings.settingsPatch.revanced_about_title">درباره</string>
|
||||||
<string name="revanced_twitch_debug_title">گزارش عیب</string>
|
<string name="twitch.misc.settings.settingsPatch.revanced_twitch_debug_title">گزارش عیب</string>
|
||||||
<string name="revanced_twitch_debug_summary_on">لاگ عیب فعال است</string>
|
<string name="twitch.misc.settings.settingsPatch.revanced_twitch_debug_summary_on">لاگ عیب فعال است</string>
|
||||||
<string name="revanced_twitch_debug_summary_off">لاگ عیب غیرفعال است</string>
|
<string name="twitch.misc.settings.settingsPatch.revanced_twitch_debug_summary_off">لاگ عیب غیرفعال است</string>
|
||||||
</patch>
|
</patch>
|
||||||
</app>
|
</app>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -38,8 +38,7 @@ Second \"item\" text"</string>
|
|||||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||||
<string name="revanced_spoof_video_streams_screen_summary">प्लेबैक समस्याओं को रोकने के लिए क्लाइंट वीडियो स्ट्रीम को स्पूफ करें</string>
|
<string name="shared.misc.fix.playback.spoofVideoStreamsPatch.revanced_spoof_video_streams_screen_summary">प्लेबैक समस्याओं को रोकने के लिए क्लाइंट वीडियो स्ट्रीम को स्पूफ करें</string>
|
||||||
<string name="revanced_spoof_video_streams_screen_summary">प्लेबैक समस्याओं को रोकने के लिए क्लाइंट वीडियो स्ट्रीम को स्पूफ करें</string>
|
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.audio.forceOriginalAudioPatch">
|
<patch id="misc.audio.forceOriginalAudioPatch">
|
||||||
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
|
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
|
||||||
@@ -231,6 +230,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -26,12 +26,12 @@ Second \"item\" text"</string>
|
|||||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.checks.checkEnvironmentPatch">
|
<patch id="misc.checks.checkEnvironmentPatch">
|
||||||
<string name="revanced_check_environment_failed_title">Provjere nisu uspjele</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_failed_title">Provjere nisu uspjele</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.settings.settingsResourcePatch">
|
<patch id="misc.settings.settingsResourcePatch">
|
||||||
<string name="revanced_settings_save">Sačuvaj</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_save">Sačuvaj</string>
|
||||||
<!-- Settings about dialog. -->
|
<!-- Settings about dialog. -->
|
||||||
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
|
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
|
||||||
and changes made here must also be made there. -->
|
and changes made here must also be made there. -->
|
||||||
@@ -231,6 +231,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -26,25 +26,25 @@ Second \"item\" text"</string>
|
|||||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.checks.checkEnvironmentPatch">
|
<patch id="misc.checks.checkEnvironmentPatch">
|
||||||
<string name="revanced_check_environment_failed_title">ಪರಿಶೀಲನೆ ವಿಫಲವಾಗಿದೆ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_failed_title">ಪರಿಶೀಲನೆ ವಿಫಲವಾಗಿದೆ</string>
|
||||||
<string name="revanced_check_environment_dialog_open_official_source_button">ಅಧಿಕೃತ ಜಾಲತಾಣ ತೆರೆಯಿರಿ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_dialog_open_official_source_button">ಅಧಿಕೃತ ಜಾಲತಾಣ ತೆರೆಯಿರಿ</string>
|
||||||
<string name="revanced_check_environment_dialog_ignore_button">ನಿರ್ಲಕ್ಷಿಸು</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.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>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_failed_message"><h5>ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ನೀವು ಪ್ಯಾಚ್ ಮಾಡಿದಂತೆ ಕಾಣುತ್ತಿಲ್ಲ.</h5><br>ಈ ಅಪ್ಲಿಕೇಶನ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು, <b> ಹಾಗು ಉಪಯೋಗಿಸಲು ಹಾನಿಕಾರಕ ಅಥವಾ ಅಪಾಯಕಾರಿಯಾಗಿರಬಹುದು</b>.<br><br>ಈ ಅಪ್ಲಿಕೇಶನ್ ಮೊದಲೇ ಪ್ಯಾಚ್ ಆಗಿದೆ ಅಥವಾ ಬೇರೆಯವರಿಂದ ಪಡೆದದ್ದು ಎಂದು ಈ ಪರಿಶೀಲನೆಗಳು ಸೂಚಿಸುತ್ತವೆ:<br><br><small>%1$s</small><br>ನೀವು ದೃಢೀಕೃತ ಮತ್ತು ಸುರಕ್ಷಿತ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು <b>ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ ಮತ್ತು ನೀವೇ ಪ್ಯಾಚ್ ಮಾಡಿ</b> ಎಂದು ಬಲವಾಗಿ ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ.<p><br>ನಿರ್ಲಕ್ಷಿಸಿದರೆ, ಈ ಎಚ್ಚರಿಕೆಯನ್ನು ಎರಡು ಬಾರಿ ಮಾತ್ರ ತೋರಿಸಲಾಗುತ್ತದೆ.</string>
|
||||||
<string name="revanced_check_environment_not_same_patching_device">ಬೇರೆ ಸಾಧನದಲ್ಲಿ ಪ್ಯಾಚ್ ಮಾಡಲಾಗಿದೆ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_same_patching_device">ಬೇರೆ ಸಾಧನದಲ್ಲಿ ಪ್ಯಾಚ್ ಮಾಡಲಾಗಿದೆ</string>
|
||||||
<string name="revanced_check_environment_manager_not_expected_installer">ReVanced Manager ನಿಂದ ಸ್ಥಾಪಿಸಿದ್ದಲ್ಲ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_manager_not_expected_installer">ReVanced Manager ನಿಂದ ಸ್ಥಾಪಿಸಿದ್ದಲ್ಲ</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time">10 ನಿಮಿಷಗಳಿಗಿಂತ ಮುಂಚೆ ಪ್ಯಾಚ್ ಮಾಡಲಾಗಿದೆ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time">10 ನಿಮಿಷಗಳಿಗಿಂತ ಮುಂಚೆ ಪ್ಯಾಚ್ ಮಾಡಲಾಗಿದೆ</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time_days">%s ದಿನಗಳ ಹಿಂದೆ ಪ್ಯಾಚ್ ಮಾಡಲಾಗಿದೆ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time_days">%s ದಿನಗಳ ಹಿಂದೆ ಪ್ಯಾಚ್ ಮಾಡಲಾಗಿದೆ</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time_invalid">APK ನಿರ್ಮಾಣ ದಿನಾಂಕವು ಭ್ರಷ್ಟಗೊಂಡಿದೆ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time_invalid">APK ನಿರ್ಮಾಣ ದಿನಾಂಕವು ಭ್ರಷ್ಟಗೊಂಡಿದೆ</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.settings.settingsResourcePatch">
|
<patch id="misc.settings.settingsResourcePatch">
|
||||||
<string name="revanced_settings_submenu_title">ಸಂಯೋಜನೆಗಳು</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_submenu_title">ಸಂಯೋಜನೆಗಳು</string>
|
||||||
<string name="revanced_settings_confirm_user_dialog_title">ನೀವು ಮುಂದುವರಿಯಲು ಖಚಿತವಾಗಿ ಬಯಸುತ್ತೀರಾ?</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_confirm_user_dialog_title">ನೀವು ಮುಂದುವರಿಯಲು ಖಚಿತವಾಗಿ ಬಯಸುತ್ತೀರಾ?</string>
|
||||||
<string name="revanced_settings_reset">ಮರುಹೊಂದಿಸಿ</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_reset">ಮರುಹೊಂದಿಸಿ</string>
|
||||||
<string name="revanced_settings_reset_color">ಬಣ್ಣ ಮರುಹೊಂದಿಸಿ</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_reset_color">ಬಣ್ಣ ಮರುಹೊಂದಿಸಿ</string>
|
||||||
<string name="revanced_settings_color_invalid">ಅಮಾನ್ಯ ಬಣ್ಣ</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_color_invalid">ಅಮಾನ್ಯ ಬಣ್ಣ</string>
|
||||||
<string name="revanced_settings_restart_title">ಮರುಪ್ರಾರಂಭದ ಅಗತ್ಯವಿದೆ</string>
|
<string name="shared.misc.settings.settingsResourcePatch.revanced_settings_restart_title">ಮರುಪ್ರಾರಂಭದ ಅಗತ್ಯವಿದೆ</string>
|
||||||
<!-- Settings about dialog. -->
|
<!-- Settings about dialog. -->
|
||||||
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
|
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
|
||||||
and changes made here must also be made there. -->
|
and changes made here must also be made there. -->
|
||||||
@@ -244,6 +244,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -224,12 +224,14 @@ Second \"item\" text"</string>
|
|||||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.announcements.announcementsPatch">
|
<patch id="misc.announcements.announcementsPatch">
|
||||||
<string name="revanced_announcements_dialog_dismiss">Melepaskan</string>
|
<string name="youtube.misc.announcements.announcementsPatch.revanced_announcements_dialog_dismiss">Melepaskan</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.loopVideoPatch">
|
<patch id="misc.loopvideo.loopVideoPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -229,6 +229,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||||
@@ -26,8 +26,8 @@ Second \"item\" text"</string>
|
|||||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.checks.checkEnvironmentPatch">
|
<patch id="misc.checks.checkEnvironmentPatch">
|
||||||
<string name="revanced_check_environment_manager_not_expected_installer">ReVanced Manager द्वारा स्थापित छैन</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_manager_not_expected_installer">ReVanced Manager द्वारा स्थापित छैन</string>
|
||||||
<string name="revanced_check_environment_not_near_patch_time_invalid">APK निर्माण मिति खराब भएको छ</string>
|
<string name="shared.misc.checks.checkEnvironmentPatch.revanced_check_environment_not_near_patch_time_invalid">APK निर्माण मिति खराब भएको छ</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||||
</patch>
|
</patch>
|
||||||
@@ -231,6 +231,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
<patch id="misc.loopvideo.button.loopVideoButtonPatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
<patch id="misc.hapticfeedback.disableHapticFeedbackPatch">
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user