9 | * This class will not be included and "replaced" by the real package's class. 10 | */ 11 | public class Toolbar extends ViewGroup { 12 | public Toolbar(Context context) { 13 | super(context); 14 | } 15 | 16 | @Override 17 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "revanced-integrations" 2 | 3 | pluginManagement { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | } 9 | 10 | @Suppress("UnstableApiUsage") 11 | dependencyResolutionManagement { 12 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | } 18 | 19 | buildCache { 20 | local { 21 | isEnabled = "CI" !in System.getenv() 22 | } 23 | } 24 | 25 | include(":app") 26 | include(":stub") 27 | -------------------------------------------------------------------------------- /stub/src/main/java/android/support/v7/widget/RecyclerView.java: -------------------------------------------------------------------------------- 1 | package android.support.v7.widget; 2 | 3 | import android.content.Context; 4 | import android.view.ViewGroup; 5 | 6 | /** 7 | * "CompileOnly" class 8 | *
9 | * This class will not be included and "replaced" by the real package's class.
10 | */
11 | public class RecyclerView extends ViewGroup {
12 | public RecyclerView(Context context) {
13 | super(context);
14 | }
15 |
16 | @Override
17 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/reddit/patches/RecentlyVisitedShelfPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.reddit.patches;
2 |
3 | import java.util.Collections;
4 | import java.util.List;
5 |
6 | import app.revanced.integrations.reddit.settings.Settings;
7 |
8 | @SuppressWarnings("unused")
9 | @Deprecated(forRemoval = true)
10 | public final class RecentlyVisitedShelfPatch {
11 |
12 | public static List> hideRecentlyVisitedShelf(List> list) {
13 | return Settings.HIDE_RECENTLY_VISITED_SHELF.get() ? Collections.emptyList() : list;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/swipecontrols/misc/Rectangle.kt:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.swipecontrols.misc
2 |
3 | /**
4 | * a simple rectangle class
5 | */
6 | data class Rectangle(
7 | val x: Int,
8 | val y: Int,
9 | val width: Int,
10 | val height: Int,
11 | ) {
12 | val left = x
13 | val right = x + width
14 | val top = y
15 | val bottom = y + height
16 | }
17 |
18 | /**
19 | * is the point within this rectangle?
20 | */
21 | operator fun Rectangle.contains(p: Point): Boolean =
22 | p.x in left..right && p.y in top..bottom
23 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/music/patches/utils/VideoTypeHookPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.music.patches.utils;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import app.revanced.integrations.music.shared.VideoType;
6 |
7 | @SuppressWarnings("unused")
8 | public class VideoTypeHookPatch {
9 | /**
10 | * Injection point.
11 | */
12 | public static void setVideoType(@Nullable Enum> musicVideoType) {
13 | if (musicVideoType == null)
14 | return;
15 |
16 | VideoType.setFromString(musicVideoType.name());
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/patches/utils/LockModeStateHookPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.patches.utils;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import app.revanced.integrations.youtube.shared.LockModeState;
6 |
7 | @SuppressWarnings("unused")
8 | public class LockModeStateHookPatch {
9 | /**
10 | * Injection point.
11 | */
12 | public static void setLockModeState(@Nullable Enum> lockModeState) {
13 | if (lockModeState == null) return;
14 |
15 | LockModeState.setFromString(lockModeState.name());
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/music/patches/utils/PlayerTypeHookPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.music.patches.utils;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import app.revanced.integrations.music.shared.PlayerType;
6 |
7 | @SuppressWarnings("unused")
8 | public class PlayerTypeHookPatch {
9 | /**
10 | * Injection point.
11 | */
12 | public static void setPlayerType(@Nullable Enum> musicPlayerType) {
13 | if (musicPlayerType == null)
14 | return;
15 |
16 | PlayerType.setFromString(musicPlayerType.name());
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/shared/patches/spans/SpanType.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.shared.patches.spans;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | public enum SpanType {
6 | CLICKABLE("ClickableSpan"),
7 | FOREGROUND_COLOR("ForegroundColorSpan"),
8 | ABSOLUTE_SIZE("AbsoluteSizeSpan"),
9 | TYPEFACE("TypefaceSpan"),
10 | IMAGE("ImageSpan"),
11 | CUSTOM_CHARACTER_STYLE("CustomCharacterStyle"),
12 | UNKNOWN("Unknown");
13 |
14 | @NonNull
15 | public final String type;
16 |
17 | SpanType(@NonNull String type) {
18 | this.type = type;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/shared/patches/AutoCaptionsPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.shared.patches;
2 |
3 | import app.revanced.integrations.shared.settings.BaseSettings;
4 |
5 | @SuppressWarnings("unused")
6 | public final class AutoCaptionsPatch {
7 |
8 | private static boolean captionsButtonStatus;
9 |
10 | public static boolean disableAutoCaptions() {
11 | return BaseSettings.DISABLE_AUTO_CAPTIONS.get() &&
12 | !captionsButtonStatus;
13 | }
14 |
15 | public static void setCaptionsButtonStatus(boolean status) {
16 | captionsButtonStatus = status;
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/swipecontrols/controller/gesture/core/GestureController.kt:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.swipecontrols.controller.gesture.core
2 |
3 | import android.view.MotionEvent
4 |
5 | /**
6 | * describes a class that accepts motion events and detects gestures
7 | */
8 | interface GestureController {
9 | /**
10 | * accept a touch event and try to detect the desired gestures using it
11 | *
12 | * @param motionEvent the motion event that was submitted
13 | * @return was a gesture detected?
14 | */
15 | fun submitTouchEvent(motionEvent: MotionEvent): Boolean
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/patches/utils/BottomSheetHookPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.patches.utils;
2 |
3 | import app.revanced.integrations.youtube.shared.BottomSheetState;
4 |
5 | @SuppressWarnings("unused")
6 | public class BottomSheetHookPatch {
7 | /**
8 | * Injection point.
9 | */
10 | public static void onAttachedToWindow() {
11 | BottomSheetState.set(BottomSheetState.OPEN);
12 | }
13 |
14 | /**
15 | * Injection point.
16 | */
17 | public static void onDetachedFromWindow() {
18 | BottomSheetState.set(BottomSheetState.CLOSED);
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/patches/video/SpoofDeviceDimensionsPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.patches.video;
2 |
3 | import app.revanced.integrations.youtube.settings.Settings;
4 |
5 | @SuppressWarnings("unused")
6 | public class SpoofDeviceDimensionsPatch {
7 | private static final boolean SPOOF = Settings.SPOOF_DEVICE_DIMENSIONS.get();
8 |
9 | public static int getMinHeightOrWidth(int minHeightOrWidth) {
10 | return SPOOF ? 64 : minHeightOrWidth;
11 | }
12 |
13 | public static int getMaxHeightOrWidth(int maxHeightOrWidth) {
14 | return SPOOF ? 4096 : maxHeightOrWidth;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/whitelist/VideoChannel.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.whitelist;
2 |
3 | import java.io.Serializable;
4 |
5 | public final class VideoChannel implements Serializable {
6 | private final String channelName;
7 | private final String channelId;
8 |
9 | public VideoChannel(String channelName, String channelId) {
10 | this.channelName = channelName;
11 | this.channelId = channelId;
12 | }
13 |
14 | public String getChannelName() {
15 | return channelName;
16 | }
17 |
18 | public String getChannelId() {
19 | return channelId;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -dontobfuscate
2 | -dontoptimize
3 | -keepattributes *
4 | -keep class app.revanced.** {
5 | *;
6 | }
7 | -keep class com.google.** {
8 | *;
9 | }
10 | -keep class org.schabi.newpipe.extractor.timeago.patterns.** {
11 | *;
12 | }
13 | -keep class org.mozilla.javascript.** {
14 | *;
15 | }
16 | -keep class org.mozilla.classfile.ClassFileWriter
17 | -dontwarn java.awt.**
18 | -dontwarn javax.swing.**
19 | -dontwarn org.mozilla.javascript.tools.**
20 | -dontwarn java.beans.BeanDescriptor
21 | -dontwarn java.beans.BeanInfo
22 | -dontwarn java.beans.IntrospectionException
23 | -dontwarn java.beans.Introspector
24 | -dontwarn java.beans.PropertyDescriptor
25 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/shared/patches/components/ByteArrayFilterGroupList.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.shared.patches.components;
2 |
3 | import app.revanced.integrations.shared.utils.ByteTrieSearch;
4 |
5 | /**
6 | * If searching for a single byte pattern, then it is slightly better to use
7 | * {@link ByteArrayFilterGroup#check(byte[])} as it uses KMP which is faster
8 | * than a prefix tree to search for only 1 pattern.
9 | */
10 | public final class ByteArrayFilterGroupList extends FilterGroupList
9 | * This class will not be included and "replaced" by the real package's class.
10 | */
11 | public class CoordinatorLayout extends ViewGroup {
12 | public CoordinatorLayout(Context context) {
13 | super(context);
14 | }
15 |
16 | @Override
17 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
18 | }
19 |
20 | @Override
21 | public void setVisibility(int visibility) {
22 | super.setVisibility(visibility);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/discord_ping_on_release.yml:
--------------------------------------------------------------------------------
1 | name: Ping Discord on release
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | notify-discord:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: sarisia/actions-status-discord@v1
12 | if: always()
13 | with:
14 | webhook: ${{ secrets.DISCORD_WEBHOOK_URL }}
15 | username: ReVanced Extended
16 | color: 0xff5252
17 | nodetail: true
18 | notimestamp: true
19 | content: "<@&1271198160613539872>"
20 | title: "Integrations `${{ github.event.release.tag_name }}` has been released!"
21 | description: |
22 | Click [here](${{ github.event.release.html_url }}) to read the changelog.
--------------------------------------------------------------------------------
/stub/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @file:Suppress("UnstableApiUsage")
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | }
6 |
7 | android {
8 | namespace = "app.revanced.integrations.stub"
9 | compileSdk = 34
10 |
11 | defaultConfig {
12 | multiDexEnabled = false
13 | minSdk = 24
14 | }
15 |
16 | buildTypes {
17 | release {
18 | isMinifyEnabled = false
19 | proguardFiles(
20 | getDefaultProguardFile("proguard-android-optimize.txt"),
21 | "proguard-rules.pro",
22 | )
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility = JavaVersion.VERSION_17
27 | targetCompatibility = JavaVersion.VERSION_17
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/music/patches/components/PlayerFlyoutMenuFilter.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.music.patches.components;
2 |
3 | import app.revanced.integrations.music.settings.Settings;
4 | import app.revanced.integrations.shared.patches.components.Filter;
5 | import app.revanced.integrations.shared.patches.components.StringFilterGroup;
6 |
7 | @SuppressWarnings("unused")
8 | public final class PlayerFlyoutMenuFilter extends Filter {
9 |
10 | public PlayerFlyoutMenuFilter() {
11 | addIdentifierCallbacks(
12 | new StringFilterGroup(
13 | Settings.HIDE_FLYOUT_MENU_3_COLUMN_COMPONENT,
14 | "music_highlight_menu_item_carousel.eml",
15 | "tile_button_carousel.eml"
16 | )
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/stub/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/shared/utils/Event.kt:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.shared.utils
2 |
3 | /**
4 | * generic event provider class
5 | */
6 | class Event
14 | * These two settings belong to different patches, and since the default value for this setting is true,
15 | * it is essential to ensure that each patch is included to ensure independent operation.
16 | */
17 | public static int hideCastButton(int original) {
18 | return Settings.HIDE_TOOLBAR_CAST_BUTTON.get()
19 | && PatchStatus.ToolBarComponents()
20 | && Settings.HIDE_PLAYER_CAST_BUTTON.get()
21 | && PatchStatus.PlayerButtons()
22 | ? View.GONE
23 | : original;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/shared/patches/components/StringFilterGroup.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.shared.patches.components;
2 |
3 | import app.revanced.integrations.shared.settings.BooleanSetting;
4 |
5 | public class StringFilterGroup extends FilterGroup
20 | * To fix this, show the reboot dialog when the app is installed for the first time.
21 | */
22 | public static void onCreate(@NonNull Activity mActivity) {
23 | if (BaseSettings.SETTINGS_INITIALIZED.get())
24 | return;
25 |
26 | showRestartDialog(mActivity, "revanced_extended_restart_first_run", 3000);
27 | Utils.runOnMainThreadDelayed(() -> BaseSettings.SETTINGS_INITIALIZED.save(true), 3000);
28 | }
29 |
30 | public static void setDeviceInformation(@NonNull Activity mActivity) {
31 | ExtendedUtils.setApplicationLabel();
32 | ExtendedUtils.setVersionName();
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/shared/returnyoutubedislike/requests/ReturnYouTubeDislikeRoutes.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.shared.returnyoutubedislike.requests;
2 |
3 | import static app.revanced.integrations.shared.requests.Route.Method.GET;
4 | import static app.revanced.integrations.shared.requests.Route.Method.POST;
5 |
6 | import java.io.IOException;
7 | import java.net.HttpURLConnection;
8 |
9 | import app.revanced.integrations.shared.requests.Requester;
10 | import app.revanced.integrations.shared.requests.Route;
11 |
12 | public class ReturnYouTubeDislikeRoutes {
13 | public static final String RYD_API_URL = "https://returnyoutubedislikeapi.com/";
14 |
15 | public static final Route SEND_VOTE = new Route(POST, "interact/vote");
16 | public static final Route CONFIRM_VOTE = new Route(POST, "interact/confirmVote");
17 | public static final Route GET_DISLIKES = new Route(GET, "votes?videoId={video_id}");
18 | public static final Route GET_REGISTRATION = new Route(GET, "puzzle/registration?userId={user_id}");
19 | public static final Route CONFIRM_REGISTRATION = new Route(POST, "puzzle/registration?userId={user_id}");
20 |
21 | public ReturnYouTubeDislikeRoutes() {
22 | }
23 |
24 | public static HttpURLConnection getRYDConnectionFromRoute(Route route, String... params) throws IOException {
25 | return Requester.getConnectionFromRoute(RYD_API_URL, route, params);
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/patches/misc/WatchHistoryPatch.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.patches.misc;
2 |
3 | import android.net.Uri;
4 |
5 | import app.revanced.integrations.shared.utils.Logger;
6 | import app.revanced.integrations.youtube.settings.Settings;
7 |
8 | @SuppressWarnings("unused")
9 | public final class WatchHistoryPatch {
10 |
11 | public enum WatchHistoryType {
12 | ORIGINAL,
13 | REPLACE,
14 | BLOCK
15 | }
16 |
17 | private static final Uri UNREACHABLE_HOST_URI = Uri.parse("https://127.0.0.0");
18 | private static final String WWW_TRACKING_URL_AUTHORITY = "www.youtube.com";
19 |
20 | public static Uri replaceTrackingUrl(Uri trackingUrl) {
21 | final WatchHistoryType watchHistoryType = Settings.WATCH_HISTORY_TYPE.get();
22 | if (watchHistoryType != WatchHistoryType.ORIGINAL) {
23 | try {
24 | if (watchHistoryType == WatchHistoryType.REPLACE) {
25 | return trackingUrl.buildUpon().authority(WWW_TRACKING_URL_AUTHORITY).build();
26 | } else if (watchHistoryType == WatchHistoryType.BLOCK) {
27 | return UNREACHABLE_HOST_URI;
28 | }
29 | } catch (Exception ex) {
30 | Logger.printException(() -> "replaceTrackingUrl failure", ex);
31 | }
32 | }
33 |
34 | return trackingUrl;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/music/patches/components/ShareSheetMenuFilter.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.music.patches.components;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import app.revanced.integrations.music.patches.misc.ShareSheetPatch;
6 | import app.revanced.integrations.music.settings.Settings;
7 | import app.revanced.integrations.shared.patches.components.Filter;
8 | import app.revanced.integrations.shared.patches.components.StringFilterGroup;
9 |
10 | /**
11 | * Abuse LithoFilter for {@link ShareSheetPatch}.
12 | */
13 | public final class ShareSheetMenuFilter extends Filter {
14 | // Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
15 | public static volatile boolean isShareSheetMenuVisible;
16 |
17 | public ShareSheetMenuFilter() {
18 | addIdentifierCallbacks(
19 | new StringFilterGroup(
20 | Settings.CHANGE_SHARE_SHEET,
21 | "share_sheet_container.eml"
22 | )
23 | );
24 | }
25 |
26 | @Override
27 | public boolean isFiltered(String path, @Nullable String identifier, String allValue, byte[] protobufBufferArray,
28 | StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
29 | isShareSheetMenuVisible = true;
30 |
31 | return false;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/patches/components/ShareSheetMenuFilter.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.patches.components;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import app.revanced.integrations.shared.patches.components.Filter;
6 | import app.revanced.integrations.shared.patches.components.StringFilterGroup;
7 | import app.revanced.integrations.youtube.patches.misc.ShareSheetPatch;
8 | import app.revanced.integrations.youtube.settings.Settings;
9 |
10 | /**
11 | * Abuse LithoFilter for {@link ShareSheetPatch}.
12 | */
13 | public final class ShareSheetMenuFilter extends Filter {
14 | // Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
15 | public static volatile boolean isShareSheetMenuVisible;
16 |
17 | public ShareSheetMenuFilter() {
18 | addIdentifierCallbacks(
19 | new StringFilterGroup(
20 | Settings.CHANGE_SHARE_SHEET,
21 | "share_sheet_container.eml"
22 | )
23 | );
24 | }
25 |
26 | @Override
27 | public boolean isFiltered(String path, @Nullable String identifier, String allValue, byte[] protobufBufferArray,
28 | StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
29 | isShareSheetMenuVisible = true;
30 |
31 | return false;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/music/patches/components/LayoutComponentsFilter.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.music.patches.components;
2 |
3 | import app.revanced.integrations.music.settings.Settings;
4 | import app.revanced.integrations.shared.patches.components.Filter;
5 | import app.revanced.integrations.shared.patches.components.StringFilterGroup;
6 |
7 | @SuppressWarnings("unused")
8 | public final class LayoutComponentsFilter extends Filter {
9 |
10 | public LayoutComponentsFilter() {
11 |
12 | final StringFilterGroup buttonShelf = new StringFilterGroup(
13 | Settings.HIDE_BUTTON_SHELF,
14 | "entry_point_button_shelf.eml"
15 | );
16 |
17 | final StringFilterGroup carouselShelf = new StringFilterGroup(
18 | Settings.HIDE_CAROUSEL_SHELF,
19 | "music_grid_item_carousel.eml"
20 | );
21 |
22 | final StringFilterGroup playlistCardShelf = new StringFilterGroup(
23 | Settings.HIDE_PLAYLIST_CARD_SHELF,
24 | "music_container_card_shelf.eml"
25 | );
26 |
27 | final StringFilterGroup sampleShelf = new StringFilterGroup(
28 | Settings.HIDE_SAMPLE_SHELF,
29 | "immersive_card_shelf.eml"
30 | );
31 |
32 | addIdentifierCallbacks(
33 | buttonShelf,
34 | carouselShelf,
35 | playlistCardShelf,
36 | sampleShelf
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/patches/components/VideoQualityMenuFilter.java:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.patches.components;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import app.revanced.integrations.shared.patches.components.Filter;
6 | import app.revanced.integrations.shared.patches.components.StringFilterGroup;
7 | import app.revanced.integrations.youtube.patches.video.RestoreOldVideoQualityMenuPatch;
8 | import app.revanced.integrations.youtube.settings.Settings;
9 |
10 | /**
11 | * Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}.
12 | */
13 | public final class VideoQualityMenuFilter extends Filter {
14 | // Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
15 | public static volatile boolean isVideoQualityMenuVisible;
16 |
17 | public VideoQualityMenuFilter() {
18 | addPathCallbacks(
19 | new StringFilterGroup(
20 | Settings.RESTORE_OLD_VIDEO_QUALITY_MENU,
21 | "quick_quality_sheet_content.eml-js"
22 | )
23 | );
24 | }
25 |
26 | @Override
27 | public boolean isFiltered(String path, @Nullable String identifier, String allValue, byte[] protobufBufferArray,
28 | StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
29 | isVideoQualityMenuVisible = true;
30 |
31 | return false;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/app/revanced/integrations/youtube/shared/BottomSheetState.kt:
--------------------------------------------------------------------------------
1 | package app.revanced.integrations.youtube.shared
2 |
3 | import app.revanced.integrations.shared.utils.Event
4 | import app.revanced.integrations.shared.utils.Logger
5 |
6 | /**
7 | * BottomSheetState bottom sheet state.
8 | */
9 | enum class BottomSheetState {
10 | CLOSED,
11 | OPEN;
12 |
13 | companion object {
14 |
15 | @JvmStatic
16 | fun set(enum: BottomSheetState) {
17 | if (current != enum) {
18 | Logger.printDebug { "BottomSheetState changed to: ${enum.name}" }
19 | current = enum
20 | }
21 | }
22 |
23 | /**
24 | * The current bottom sheet state.
25 | */
26 | @JvmStatic
27 | var current
28 | get() = currentBottomSheetState
29 | private set(value) {
30 | currentBottomSheetState = value
31 | onChange(currentBottomSheetState)
32 | }
33 |
34 | @Volatile // value is read/write from different threads
35 | private var currentBottomSheetState = CLOSED
36 |
37 | /**
38 | * bottom sheet state change listener
39 | */
40 | @JvmStatic
41 | val onChange = Event
39 | * Only used by old clients.
40 | * It is presumed to have been deprecated, and if it is confirmed that it is no longer used, remove it.
41 | */
42 | public static boolean hideVideoAds(boolean original) {
43 | return !hideVideoAdsEnabled && original;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-issue.yml:
--------------------------------------------------------------------------------
1 | name: ⭐ Feature request
2 | description: Create a detailed feature request.
3 | title: 'feat: