mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-24 11:11:03 +00:00
feat(YouTube Music): Add Hide layout components patch (#6365)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -521,6 +525,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 +940,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;
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -173,6 +173,14 @@ You will not be notified of any unexpected events."</string>
|
|||||||
<string name="revanced_debug_feature_flags_manager_toast_saved">Flags saved</string>
|
<string name="revanced_debug_feature_flags_manager_toast_saved">Flags saved</string>
|
||||||
<string name="revanced_debug_feature_flags_manager_toast_reset">Flags reset</string>
|
<string name="revanced_debug_feature_flags_manager_toast_reset">Flags reset</string>
|
||||||
<string name="revanced_debug_feature_flags_manager_toast_copied">Flags copied to clipboard</string>
|
<string name="revanced_debug_feature_flags_manager_toast_copied">Flags copied to clipboard</string>
|
||||||
|
<string name="revanced_debug_protobuffer_title">Log protocol buffer</string>
|
||||||
|
<string name="revanced_debug_protobuffer_summary_on">Debug logs include proto buffer</string>
|
||||||
|
<string name="revanced_debug_protobuffer_summary_off">Debug logs do not include proto buffer</string>
|
||||||
|
<string name="revanced_debug_protobuffer_user_dialog_message">"Enabling this setting will log additional layout data, including on-screen text for some UI components.
|
||||||
|
|
||||||
|
This can help identify components when creating custom filters.
|
||||||
|
|
||||||
|
However, enabling this will also log some user data such as your IP address."</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.privacy.sanitizeSharingLinksPatch">
|
<patch id="misc.privacy.sanitizeSharingLinksPatch">
|
||||||
<string name="revanced_sanitize_sharing_links_title">Sanitize sharing links</string>
|
<string name="revanced_sanitize_sharing_links_title">Sanitize sharing links</string>
|
||||||
@@ -182,6 +190,17 @@ You will not be notified of any unexpected events."</string>
|
|||||||
<string name="revanced_replace_music_with_youtube_summary_on">Shared links use youtube.com</string>
|
<string name="revanced_replace_music_with_youtube_summary_on">Shared links use youtube.com</string>
|
||||||
<string name="revanced_replace_music_with_youtube_summary_off">Shared links use music.youtube.com</string>
|
<string name="revanced_replace_music_with_youtube_summary_off">Shared links use music.youtube.com</string>
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="layout.hide.general.hideLayoutComponentsPatch">
|
||||||
|
<string name="revanced_custom_filter_screen_title">Custom filter</string>
|
||||||
|
<string name="revanced_custom_filter_screen_summary">Hide components using custom filters</string>
|
||||||
|
<string name="revanced_custom_filter_title">Enable custom filter</string>
|
||||||
|
<string name="revanced_custom_filter_summary_on">Custom filter is enabled</string>
|
||||||
|
<string name="revanced_custom_filter_summary_off">Custom filter is disabled</string>
|
||||||
|
<string name="revanced_custom_filter_strings_title">Custom filter</string>
|
||||||
|
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||||
|
<string name="revanced_custom_filter_strings_summary">List of component path builder strings to filter separated by new line</string>
|
||||||
|
<string name="revanced_custom_filter_toast_invalid_syntax">Invalid custom filter: %s</string>
|
||||||
|
</patch>
|
||||||
</app>
|
</app>
|
||||||
<app id="youtube">
|
<app id="youtube">
|
||||||
<patch id="misc.settings.settingsPatch">
|
<patch id="misc.settings.settingsPatch">
|
||||||
@@ -206,16 +225,6 @@ You will not be notified of any unexpected events."</string>
|
|||||||
<string name="revanced_shorts_disable_background_playback_summary_on">Shorts background play is disabled</string>
|
<string name="revanced_shorts_disable_background_playback_summary_on">Shorts background play is disabled</string>
|
||||||
<string name="revanced_shorts_disable_background_playback_summary_off">Shorts background play is enabled</string>
|
<string name="revanced_shorts_disable_background_playback_summary_off">Shorts background play is enabled</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.debugging.enableDebuggingPatch">
|
|
||||||
<string name="revanced_debug_protobuffer_title">Log protocol buffer</string>
|
|
||||||
<string name="revanced_debug_protobuffer_summary_on">Debug logs include proto buffer</string>
|
|
||||||
<string name="revanced_debug_protobuffer_summary_off">Debug logs do not include proto buffer</string>
|
|
||||||
<string name="revanced_debug_protobuffer_user_dialog_message">"Enabling this setting will log additional layout data, including on-screen text for some UI components.
|
|
||||||
|
|
||||||
This can help identify components when creating custom filters.
|
|
||||||
|
|
||||||
However, enabling this will also log some user data such as your IP address."</string>
|
|
||||||
</patch>
|
|
||||||
<patch id="layout.hide.general.hideLayoutComponentsPatch">
|
<patch id="layout.hide.general.hideLayoutComponentsPatch">
|
||||||
<string name="revanced_hide_album_cards_title">Hide album cards</string>
|
<string name="revanced_hide_album_cards_title">Hide album cards</string>
|
||||||
<string name="revanced_hide_album_cards_summary_on">Album cards are hidden</string>
|
<string name="revanced_hide_album_cards_summary_on">Album cards are hidden</string>
|
||||||
@@ -446,15 +455,6 @@ If a Doodle is currently showing in your region and this hide setting is on, the
|
|||||||
<string name="revanced_hide_comments_thanks_button_title">Hide Thanks button</string>
|
<string name="revanced_hide_comments_thanks_button_title">Hide Thanks button</string>
|
||||||
<string name="revanced_hide_comments_thanks_button_summary_on">Thanks button is hidden</string>
|
<string name="revanced_hide_comments_thanks_button_summary_on">Thanks button is hidden</string>
|
||||||
<string name="revanced_hide_comments_thanks_button_summary_off">Thanks button is shown</string>
|
<string name="revanced_hide_comments_thanks_button_summary_off">Thanks button is shown</string>
|
||||||
<string name="revanced_custom_filter_screen_title">Custom filter</string>
|
|
||||||
<string name="revanced_custom_filter_screen_summary">Hide components using custom filters</string>
|
|
||||||
<string name="revanced_custom_filter_title">Enable custom filter</string>
|
|
||||||
<string name="revanced_custom_filter_summary_on">Custom filter is enabled</string>
|
|
||||||
<string name="revanced_custom_filter_summary_off">Custom filter is disabled</string>
|
|
||||||
<string name="revanced_custom_filter_strings_title">Custom filter</string>
|
|
||||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
|
||||||
<string name="revanced_custom_filter_strings_summary">List of component path builder strings to filter separated by new line</string>
|
|
||||||
<string name="revanced_custom_filter_toast_invalid_syntax">Invalid custom filter: %s</string>
|
|
||||||
<string name="revanced_hide_view_count_title">Hide view count</string>
|
<string name="revanced_hide_view_count_title">Hide view count</string>
|
||||||
<string name="revanced_hide_view_count_summary_on">View count is hidden in feed and search results</string>
|
<string name="revanced_hide_view_count_summary_on">View count is hidden in feed and search results</string>
|
||||||
<string name="revanced_hide_view_count_summary_off">View count is shown in feed and search results</string>
|
<string name="revanced_hide_view_count_summary_off">View count is shown in feed and search results</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user