chore: Merge branch dev to main (#567)

This commit is contained in:
oSumAtrIX 2024-03-02 19:57:05 +01:00 committed by GitHub
commit 3b6a6626db
19 changed files with 246 additions and 100 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
</manifest>

View File

@ -12,6 +12,7 @@ import android.os.Handler;
import android.os.Looper;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
@ -26,10 +27,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.text.Bidi;
import java.util.Locale;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
@ -102,7 +100,6 @@ public class Utils {
view.setVisibility(View.GONE);
}
/**
* General purpose pool for network calls and other background tasks.
* All tasks run at max thread priority.
@ -205,7 +202,9 @@ public class Utils {
public static <T extends View> T getChildView(@NonNull ViewGroup viewGroup, @NonNull MatchFilter filter) {
for (int i = 0, childCount = viewGroup.getChildCount(); i < childCount; i++) {
View childAt = viewGroup.getChildAt(i);
//noinspection unchecked
if (filter.matches(childAt)) {
//noinspection unchecked
return (T) childAt;
}
}
@ -345,6 +344,12 @@ public class Utils {
}
}
public enum NetworkType {
NONE,
MOBILE,
OTHER,
}
public static boolean isNetworkConnected() {
NetworkType networkType = getNetworkType();
return networkType == NetworkType.MOBILE
@ -393,48 +398,104 @@ public class Utils {
}
}
/**
* {@link PreferenceScreen} and {@link PreferenceGroup} sorting styles.
*/
private enum Sort {
/**
* Sort by the localized preference title.
*/
BY_TITLE("_sort_by_title"),
/**
* Sort by the preference keys.
*/
BY_KEY("_sort_by_key"),
/**
* Unspecified sorting.
*/
UNSORTED("_sort_by_unsorted");
final String keySuffix;
Sort(String keySuffix) {
this.keySuffix = keySuffix;
}
/**
* Defaults to {@link #UNSORTED} if key is null or has no sort suffix.
*/
@NonNull
static Sort fromKey(@Nullable String key) {
if (key != null) {
for (Sort sort : values()) {
if (key.endsWith(sort.keySuffix)) {
return sort;
}
}
}
return UNSORTED;
}
}
private static final Regex punctuationRegex = new Regex("\\p{P}+");
/**
* Sort the preferences by title and ignore the casing.
*
* Android Preferences are automatically sorted by title,
* but if using a localized string key it sorts on the key and not the actual title text that's used at runtime.
*
* @param menuDepthToSort Maximum menu depth to sort. Menus deeper than this value
* will show preferences in the order created in patches.
* Strips all punctuation and converts to lower case. A null parameter returns an empty string.
*/
public static void sortPreferenceGroupByTitle(PreferenceGroup group, int menuDepthToSort) {
if (menuDepthToSort == 0) return;
SortedMap<String, Preference> preferences = new TreeMap<>();
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference preference = group.getPreference(i);
if (preference instanceof PreferenceGroup) {
sortPreferenceGroupByTitle((PreferenceGroup) preference, menuDepthToSort - 1);
}
preferences.put(removePunctuationConvertToLowercase(preference.getTitle()), preference);
}
int prefIndex = 0;
for (Preference pref : preferences.values()) {
int indexToSet = prefIndex++;
if (pref instanceof PreferenceGroup || pref.getIntent() != null) {
// Place preference groups last.
// Use an offset to push the group to the end.
indexToSet += 1000;
}
pref.setOrder(indexToSet);
}
}
public static String removePunctuationConvertToLowercase(CharSequence original) {
public static String removePunctuationConvertToLowercase(@Nullable CharSequence original) {
if (original == null) return "";
return punctuationRegex.replace(original, "").toLowerCase();
}
public enum NetworkType {
NONE,
MOBILE,
OTHER,
/**
* Sort a PreferenceGroup and all it's sub groups by title or key.
*
* Sort order is determined by the preferences key {@link Sort} suffix.
*
* If a preference has no key or no {@link Sort} suffix,
* then the preferences are left unsorted.
*/
public static void sortPreferenceGroups(@NonNull PreferenceGroup group) {
Sort sort = Sort.fromKey(group.getKey());
SortedMap<String, Preference> preferences = new TreeMap<>();
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference preference = group.getPreference(i);
if (preference instanceof PreferenceGroup) {
sortPreferenceGroups((PreferenceGroup) preference);
}
final String sortValue;
switch (sort) {
case BY_TITLE:
sortValue = removePunctuationConvertToLowercase(preference.getTitle());
break;
case BY_KEY:
sortValue = preference.getKey();
break;
case UNSORTED:
continue; // Keep original sorting.
default:
throw new IllegalStateException();
}
preferences.put(sortValue, preference);
}
int index = 0;
for (Preference pref : preferences.values()) {
int order = index++;
// If the preference is a PreferenceScreen or is an intent preference, move to the top.
if (pref instanceof PreferenceScreen || pref.getIntent() != null) {
// Arbitrary high number.
order -= 1000;
}
pref.setOrder(order);
}
}
}

View File

@ -85,7 +85,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
if (identifier == 0) return;
addPreferencesFromResource(identifier);
Utils.sortPreferenceGroupByTitle(getPreferenceScreen(), 2);
Utils.sortPreferenceGroups(getPreferenceScreen());
}
private void showSettingUserDialogConfirmation(SwitchPreference switchPref, BooleanSetting setting) {

View File

@ -0,0 +1,42 @@
package app.revanced.integrations.syncforreddit;
import android.os.StrictMode;
import app.revanced.integrations.shared.Logger;
import java.net.HttpURLConnection;
import java.net.URL;
public final class FixSLinksPatch {
public static String resolveSLink(String link) {
if (link.matches(".*reddit\\.com/r/[^/]+/s/[^/]+")) {
Logger.printInfo(() -> "Resolving " + link);
try {
URL url = new URL(link);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("HEAD");
// Disable strict mode in order to allow network access on the main thread.
// This is not ideal, but it's the easiest solution for now.
final var currentPolicy = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
connection.connect();
String location = connection.getHeaderField("location");
connection.disconnect();
// Restore the original strict mode policy.
StrictMode.setThreadPolicy(currentPolicy);
Logger.printInfo(() -> "Resolved " + link + " -> " + location);
return location;
} catch (Exception e) {
Logger.printException(() -> "Failed to resolve " + link, e);
}
}
return link;
}
}

View File

@ -28,7 +28,7 @@ public class AppCompatActivityHook {
public static void startSettingsActivity() {
Logger.printDebug(() -> "Launching ReVanced settings");
final var context = app.revanced.integrations.shared.Utils.getContext();
final var context = Utils.getContext();
if (context != null) {
Intent intent = new Intent(context, SettingsActivity.class);

View File

@ -9,8 +9,7 @@ import org.json.JSONObject
// https://raw.githubusercontent.com/Dr-TSNG/TwiFucker/880cdf1c1622e54ab45561ffcb4f53d94ed97bae/app/src/main/java/icu/nullptr/twifucker/hook/JsonHook.kt
internal object TwiFucker {
// root
private fun JSONObject.jsonGetInstructions(): JSONArray? =
optJSONObject("timeline")?.optJSONArray("instructions")
private fun JSONObject.jsonGetInstructions(): JSONArray? = optJSONObject("timeline")?.optJSONArray("instructions")
private fun JSONObject.jsonGetData(): JSONObject? = optJSONObject("data")
@ -42,10 +41,12 @@ internal object TwiFucker {
// data
private fun JSONObject.dataGetInstructions(): JSONArray? {
val timeline = optJSONObject("user_result")?.optJSONObject("result")
?.optJSONObject("timeline_response")?.optJSONObject("timeline")
?: optJSONObject("timeline_response")?.optJSONObject("timeline")
?: optJSONObject("timeline_response")
val timeline =
optJSONObject("user_result")?.optJSONObject("result")
?.optJSONObject("timeline_response")?.optJSONObject("timeline")
?: optJSONObject("timeline_response")?.optJSONObject("timeline")
?: optJSONObject("search")?.optJSONObject("timeline_response")?.optJSONObject("timeline")
?: optJSONObject("timeline_response")
return timeline?.optJSONArray("instructions")
}
@ -64,7 +65,6 @@ internal object TwiFucker {
}
}?.optJSONObject("legacy")
// entry
private fun JSONObject.entryHasPromotedMetadata(): Boolean =
optJSONObject("content")?.optJSONObject("item")?.optJSONObject("content")
@ -77,11 +77,9 @@ internal object TwiFucker {
optJSONObject("content")?.optJSONArray("items")
?: optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items")
private fun JSONObject.entryIsTweetDetailRelatedTweets(): Boolean =
optString("entryId").startsWith("tweetdetailrelatedtweets-")
private fun JSONObject.entryIsTweetDetailRelatedTweets(): Boolean = optString("entryId").startsWith("tweetdetailrelatedtweets-")
private fun JSONObject.entryGetTrends(): JSONArray? =
optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items")
private fun JSONObject.entryGetTrends(): JSONArray? = optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items")
// trend
private fun JSONObject.trendHasPromotedMetadata(): Boolean =
@ -104,8 +102,7 @@ internal object TwiFucker {
// instruction
private fun JSONObject.instructionTimelineAddEntries(): JSONArray? = optJSONArray("entries")
private fun JSONObject.instructionGetAddEntries(): JSONArray? =
optJSONObject("addEntries")?.optJSONArray("entries")
private fun JSONObject.instructionGetAddEntries(): JSONArray? = optJSONObject("addEntries")?.optJSONArray("entries")
private fun JSONObject.instructionCheckAndRemove(action: (JSONArray) -> Unit) {
instructionTimelineAddEntries()?.let(action)
@ -164,9 +161,10 @@ internal object TwiFucker {
entriesRemoveTweetDetailRelatedTweets()
}
private fun JSONObject.entryIsWhoToFollow(): Boolean = optString("entryId").let {
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-")
}
private fun JSONObject.entryIsWhoToFollow(): Boolean =
optString("entryId").let {
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-")
}
private fun JSONObject.itemContainsPromotedUser(): Boolean =
optJSONObject("item")?.optJSONObject("content")
@ -217,4 +215,4 @@ internal object TwiFucker {
instruction.instructionCheckAndRemove(action)
}
}
}
}

View File

@ -0,0 +1,12 @@
package app.revanced.integrations.twitter.patches.links;
import android.content.Context;
import android.content.Intent;
public final class OpenLinksWithAppChooserPatch {
public static void openWithChooser(final Context context, final Intent intent) {
intent.setAction("android.intent.action.VIEW");
context.startActivity(Intent.createChooser(intent, null));
}
}

View File

@ -422,7 +422,7 @@ public final class AlternativeThumbnailsPatch {
private static final int CACHE_LIMIT = 1000;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
protected boolean removeEldestEntry(Entry eldest) {
return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit.
}
};

View File

@ -1,9 +1,9 @@
package app.revanced.integrations.youtube.patches;
import android.content.Intent;
import app.revanced.integrations.youtube.settings.Settings;
import android.net.Uri;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class ChangeStartPagePatch {
@ -12,6 +12,10 @@ public final class ChangeStartPagePatch {
if (startPage.isEmpty()) return;
Logger.printDebug(() -> "Changing start page to " + startPage);
intent.setAction("com.google.android.youtube.action." + startPage);
if (startPage.startsWith("www"))
intent.setData(Uri.parse(startPage));
else
intent.setAction("com.google.android.youtube.action." + startPage);
}
}

View File

@ -3,29 +3,27 @@ package app.revanced.integrations.youtube.patches;
import android.annotation.SuppressLint;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.youtube.settings.Settings;
/** @noinspection unused*/
public final class DisableSuggestedVideoEndScreenPatch {
@SuppressLint("StaticFieldLeak")
private static View lastView;
private static ImageView lastView;
public static void closeEndScreen(final ImageView imageView) {
if (!Settings.DISABLE_SUGGESTED_VIDEO_END_SCREEN.get()) return;
// Get a parent view which can be listened to for layout changes.
final var parent = imageView.getParent().getParent();
// Prevent adding the listener multiple times.
if (lastView == parent) return;
if (lastView == imageView) return;
lastView = imageView;
lastView = (ViewGroup)parent;
lastView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
// Disable sound effects to prevent the click sound.
imageView.setSoundEffectsEnabled(false);
imageView.performClick();
imageView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (imageView.isShown()) imageView.callOnClick();
});
}
}

View File

@ -667,7 +667,7 @@ public class ReturnYouTubeDislikePatch {
*
* Called when the user likes or dislikes.
*
* @param vote int that matches {@link ReturnYouTubeDislike.Vote#value}
* @param vote int that matches {@link Vote#value}
*/
public static void sendVote(int vote) {
try {

View File

@ -14,7 +14,7 @@ import app.revanced.integrations.youtube.StringTrieSearch;
@SuppressWarnings("unused")
public final class AdsFilter extends Filter {
// region Fullscreen ad
private static long lastTimeClosedFullscreenAd = 0;
private static volatile long lastTimeClosedFullscreenAd;
private static final Instrumentation instrumentation = new Instrumentation();
private final StringFilterGroup fullscreenAd;
@ -168,6 +168,9 @@ public final class AdsFilter extends Filter {
Logger.printDebug(() -> "Closing fullscreen ad");
Utils.runOnMainThreadDelayed(() -> instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK), 1000);
Utils.runOnMainThreadDelayed(() -> {
// Must run off main thread (Odd, but whatever).
Utils.runOnBackgroundThread(() -> instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK));
}, 1000);
}
}

View File

@ -49,10 +49,6 @@ final class ButtonsFilter extends Filter {
);
bufferButtonsGroupList.addAll(
new ByteArrayFilterGroup(
Settings.HIDE_LIVE_CHAT_BUTTON,
"yt_outline_message_bubble_overlap"
),
new ByteArrayFilterGroup(
Settings.HIDE_REPORT_BUTTON,
"yt_outline_flag"

View File

@ -45,7 +45,7 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 5;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
protected boolean removeEldestEntry(Entry eldest) {
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
}
};

View File

@ -17,6 +17,9 @@ public final class ShortsFilter extends Filter {
public static PivotBar pivotBar; // Set by patch.
private final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.eml";
private final StringFilterGroup shortsCompactFeedVideoPath;
private final ByteArrayFilterGroup shortsCompactFeedVideoBuffer;
private final StringFilterGroup channelBar;
private final StringFilterGroup subscribeButton;
private final StringFilterGroup subscribeButtonPaused;
@ -35,7 +38,6 @@ public final class ShortsFilter extends Filter {
"shorts_grid",
"shorts_video_cell",
"shorts_pivot_item"
);
// Feed Shorts shelf header.
// Use a different filter group for this pattern, as it requires an additional check after matching.
@ -52,6 +54,15 @@ public final class ShortsFilter extends Filter {
addIdentifierCallbacks(shorts, shelfHeader, thanksButton);
// Shorts that appear in the feed/search when the device is using tablet layout.
shortsCompactFeedVideoPath = new StringFilterGroup(Settings.HIDE_SHORTS,
"compact_video.eml");
// Filter out items that use the 'frame0' thumbnail.
// This is a valid thumbnail for both regular videos and Shorts,
// but it appears these thumbnails are used only for Shorts.
shortsCompactFeedVideoBuffer = new ByteArrayFilterGroup(Settings.HIDE_SHORTS,
"/frame0.jpg");
// Shorts player components.
var joinButton = new StringFilterGroup(
Settings.HIDE_SHORTS_JOIN_BUTTON,
@ -89,6 +100,7 @@ public final class ShortsFilter extends Filter {
);
addPathCallbacks(
shortsCompactFeedVideoPath,
joinButton, subscribeButton, subscribeButtonPaused,
channelBar, soundButton, infoPanel, videoActionButton
);
@ -122,6 +134,13 @@ public final class ShortsFilter extends Filter {
matchedGroup == subscribeButtonPaused
) return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
if (matchedGroup == shortsCompactFeedVideoPath) {
if (contentIndex == 0 && shortsCompactFeedVideoBuffer.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
// Video action buttons (comment, share, remix) have the same path.
if (matchedGroup == videoActionButton) {
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) return super.isFiltered(
@ -132,10 +151,11 @@ public final class ShortsFilter extends Filter {
// Filter other path groups from pathFilterGroupList, only when reelChannelBar is visible
// to avoid false positives.
if (path.startsWith(REEL_CHANNEL_BAR_PATH))
if (matchedGroup == subscribeButton) return super.isFiltered(
if (matchedGroup == subscribeButton) {
if (path.startsWith(REEL_CHANNEL_BAR_PATH)) return super.isFiltered(
identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex
);
}
return false;
} else if (matchedGroup == shelfHeader) {

View File

@ -1,12 +1,26 @@
package app.revanced.integrations.youtube.patches.spoof;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.youtube.settings.Settings;
@SuppressWarnings("unused")
public class SpoofAppVersionPatch {
private static final boolean SPOOF_APP_VERSION_ENABLED = Settings.SPOOF_APP_VERSION.get();
private static final String SPOOF_APP_VERSION_TARGET = Settings.SPOOF_APP_VERSION_TARGET.get();
private static final boolean SPOOF_APP_VERSION_ENABLED;
private static final String SPOOF_APP_VERSION_TARGET;
static {
// TODO: remove this migration code
// Spoof targets below 17.33 that no longer reliably work.
if (Settings.SPOOF_APP_VERSION_TARGET.get().compareTo("17.33.01") < 0) {
Logger.printInfo(() -> "Resetting spoof app version target");
Settings.SPOOF_APP_VERSION_TARGET.resetToDefault();
}
// End migration
SPOOF_APP_VERSION_ENABLED = Settings.SPOOF_APP_VERSION.get();
SPOOF_APP_VERSION_TARGET = Settings.SPOOF_APP_VERSION_TARGET.get();
}
/**
* Injection point

View File

@ -2,10 +2,10 @@ package app.revanced.integrations.youtube.requests;
public class Route {
private final String route;
private final Route.Method method;
private final Method method;
private final int paramCount;
public Route(Route.Method method, String route) {
public Route(Method method, String route) {
this.method = method;
this.route = route;
this.paramCount = countMatches(route, '{');
@ -14,11 +14,11 @@ public class Route {
throw new IllegalArgumentException("Not enough parameters");
}
public Route.Method getMethod() {
public Method getMethod() {
return method;
}
public Route.CompiledRoute compile(String... params) {
public CompiledRoute compile(String... params) {
if (params.length != paramCount)
throw new IllegalArgumentException("Error compiling route [" + route + "], incorrect amount of parameters provided. " +
"Expected: " + paramCount + ", provided: " + params.length);
@ -29,7 +29,7 @@ public class Route {
int paramEnd = compiledRoute.indexOf("}");
compiledRoute.replace(paramStart, paramEnd + 1, params[i]);
}
return new Route.CompiledRoute(this, compiledRoute.toString());
return new CompiledRoute(this, compiledRoute.toString());
}
public static class CompiledRoute {
@ -45,7 +45,7 @@ public class Route {
return compiledRoute;
}
public Route.Method getMethod() {
public Method getMethod() {
return baseRoute.method;
}
}

View File

@ -15,15 +15,14 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.settings.BaseSettings;
import app.revanced.integrations.shared.settings.BooleanSetting;
import app.revanced.integrations.shared.settings.FloatSetting;
import app.revanced.integrations.shared.settings.IntegerSetting;
import app.revanced.integrations.shared.settings.LongSetting;
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
import app.revanced.integrations.shared.settings.BaseSettings;
import app.revanced.integrations.shared.settings.StringSetting;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
import app.revanced.integrations.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings {
@ -129,7 +128,7 @@ public class Settings extends BaseSettings {
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity",100, true);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "17.08.35", true, parent(SPOOF_APP_VERSION));
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "17.33.42", true, parent(SPOOF_APP_VERSION));
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
public static final BooleanSetting USE_TABLET_MINIPLAYER = new BooleanSetting("revanced_tablet_miniplayer", FALSE, true);
@ -167,7 +166,6 @@ public class Settings extends BaseSettings {
// Action buttons
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
public static final BooleanSetting HIDE_LIVE_CHAT_BUTTON = new BooleanSetting("revanced_hide_live_chat_button", FALSE);
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", TRUE);

View File

@ -150,7 +150,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
pref.getContext().startActivity(i);
return false;
});
preferenceScreen.addPreference(aboutWebsitePreference);
aboutCategory.addPreference(aboutWebsitePreference);
// RYD API connection statistics