diff --git a/CHANGELOG.md b/CHANGELOG.md index d51fca00..7a080ae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.116.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0...v0.116.1-dev.1) (2023-08-27) + + +### Bug Fixes + +* Revert previous release ([a178a22](https://github.com/ReVanced/revanced-integrations/commit/a178a223c283abe420e197d10863d7fe64534f32)) + # [0.116.0](https://github.com/ReVanced/revanced-integrations/compare/v0.115.1...v0.116.0) (2023-08-26) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0e98a7b6..6e77d08b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,7 @@ plugins { android { compileSdk = 33 + buildToolsVersion = "33.0.1" namespace = "app.revanced.integrations" defaultConfig { @@ -43,7 +44,7 @@ android { dependencies { compileOnly(project(mapOf("path" to ":dummy"))) compileOnly("androidx.annotation:annotation:1.6.0") - compileOnly("androidx.appcompat:appcompat:1.7.0-alpha03") + compileOnly("androidx.appcompat:appcompat:1.7.0-alpha02") compileOnly("com.squareup.okhttp3:okhttp:5.0.0-alpha.11") compileOnly("com.squareup.retrofit2:retrofit:2.9.0") } diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java deleted file mode 100644 index 1e4d3f05..00000000 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ /dev/null @@ -1,409 +0,0 @@ -package app.revanced.integrations.patches; - -import androidx.annotation.GuardedBy; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.chromium.net.UrlResponseInfo; - -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.LogHelper; -import app.revanced.integrations.utils.ReVancedUtils; - -/** - * Alternative YouTube thumbnails, showing the beginning/middle/end of the video. - * (ie: sd1.jpg, sd2.jpg, sd3.jpg). - * - * Has an additional option to use 'fast' thumbnails, - * where it forces sd thumbnail quality and skips verifying if the alt thumbnail image exists. - * The UI loading time will be the same or better than using the the original thumbnails, - * but thumbnails will initially fail to load for all live streams, unreleased, and occasionally very old videos. - * If a failed thumbnail load is reloaded (ie: scroll off, then on screen), then the original thumbnail - * is reloaded instead. Fast thumbnails requires using SD or lower thumbnail resolution, - * because a noticeable number of videos do not have hq720 and too many fail to load. - * - * Ideas for improvements: - * - Selectively allow using original thumbnails in some situations, - * such as videos subscription feed, watch history, or in search results. - * - Save to a temporary file the video id's verified to have alt thumbnails. - * This would speed up loading the watch history and users saved playlists. - */ -public final class AlternativeThumbnailsPatch { - - private enum ThumbnailQuality { - // In order of lowest to highest resolution. - DEFAULT("default", ""), // effective alt name is 1.jpg, 2.jpg, 3.jpg - MQDEFAULT("mqdefault", "mq"), - HQDEFAULT("hqdefault", "hq"), - SDDEFAULT("sddefault", "sd"), - HQ720("hq720", "hq720_"), - MAXRESDEFAULT("maxresdefault", "maxres"); - - /** - * Lookup map of original name to enum. - */ - private static final Map originalNameToEnum = new HashMap<>(); - - /** - * Lookup map of alt name to enum. ie: "hq720_1" to {@link #HQ720}. - */ - private static final Map altNameToEnum = new HashMap<>(); - - static { - for (ThumbnailQuality quality : values()) { - originalNameToEnum.put(quality.originalName, quality); - - for (int i = 1; i <= 3; i++) { - altNameToEnum.put(quality.altImageName + i, quality); - } - } - } - - /** - * Convert an alt image name to enum. - * ie: "hq720_2" returns {@link #HQ720}. - */ - @Nullable - static ThumbnailQuality altImageNameToQuality(@NonNull String altImageName) { - return altNameToEnum.get(altImageName); - } - - /** - * Original quality to effective alt quality to use. - * ie: If fast alt image is enabled, then "hq720" returns {@link #SDDEFAULT}. - */ - @Nullable - static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { - ThumbnailQuality quality = originalNameToEnum.get(originalSize); - if (quality == null) { - return null; // Not a thumbnail for a regular video. - } - - final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean(); - switch (quality) { - case SDDEFAULT: - // SD alt images have somewhat worse quality with washed out color and poor contrast. - // But the 720 images look much better and don't suffer from these issues. - // For unknown reasons, the 720 thumbnails are used only for the home feed, - // while SD is used for the search and subscription feed - // (even though search and subscriptions use the exact same layout as the home feed). - // Of note, this image quality issue only appears with the alt thumbnail images, - // and the regular thumbnails have identical color/contrast quality for all sizes. - // Fix this by falling thru and upgrading SD to 720. - case HQ720: - if (useFastQuality) { - return SDDEFAULT; // SD is max resolution for fast alt images. - } - return HQ720; - case MAXRESDEFAULT: - if (useFastQuality) { - return SDDEFAULT; - } - return MAXRESDEFAULT; - default: - return quality; - } - } - - final String originalName; - final String altImageName; - - ThumbnailQuality(String originalName, String altImageName) { - this.originalName = originalName; - this.altImageName = altImageName; - } - - String getAltImageNameToUse() { - return altImageName + SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); - } - } - - /** - * Uses HTTP HEAD requests to verify and keep track of which thumbnail sizes - * are available and not available. - */ - private static class VerifiedQualities { - /** - * After a quality is verified as not available, how long until the quality is re-verified again. - * Used only if fast mode is not enabled. Intended for live streams and unreleased videos - * that are now finished and available (and thus, the alt thumbnails are also now available). - */ - private static final long NOT_AVAILABLE_TIMEOUT_MILLISECONDS = 10 * 60 * 1000; // 10 minutes. - - /** - * Cache used to verify if an alternative thumbnails exists for a given video id. - */ - @GuardedBy("itself") - private static final Map altVideoIdLookup = new LinkedHashMap<>(100) { - private static final int CACHE_LIMIT = 1000; - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > CACHE_LIMIT; // Evict oldest entry if over the cache limit. - } - }; - - private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, boolean returnNullIfDoesNotExist) { - synchronized (altVideoIdLookup) { - VerifiedQualities verified = altVideoIdLookup.get(videoId); - if (verified == null) { - if (returnNullIfDoesNotExist) { - return null; - } - verified = new VerifiedQualities(); - altVideoIdLookup.put(videoId, verified); - } - return verified; - } - } - - static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull ThumbnailQuality quality, - @NonNull String imageUrl) { - VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean()); - if (verified == null) return true; // Fast alt thumbnails is enabled. - return verified.verifyYouTubeThumbnailExists(videoId, quality, imageUrl); - } - - static void setAltThumbnailDoesNotExist(@NonNull String videoId, @NonNull ThumbnailQuality quality) { - VerifiedQualities verified = getVerifiedQualities(videoId, false); - verified.setQualityVerified(videoId, quality, false); - } - - /** - * Highest quality verified as existing. - */ - @Nullable - ThumbnailQuality highestQualityVerified; - /** - * Lowest quality verified as not existing. - */ - @Nullable - ThumbnailQuality lowestQualityNotAvailable; - - /** - * System time, of when to invalidate {@link #lowestQualityNotAvailable}. - * Used only if fast mode is not enabled. - */ - long timeToReVerifyLowestQuality; - - synchronized void setQualityVerified(String videoId, ThumbnailQuality quality, boolean isVerified) { - if (isVerified) { - if (highestQualityVerified == null || highestQualityVerified.ordinal() < quality.ordinal()) { - highestQualityVerified = quality; - } - } else { - if (lowestQualityNotAvailable == null || lowestQualityNotAvailable.ordinal() > quality.ordinal()) { - lowestQualityNotAvailable = quality; - timeToReVerifyLowestQuality = System.currentTimeMillis() + NOT_AVAILABLE_TIMEOUT_MILLISECONDS; - } - LogHelper.printDebug(() -> quality + " not available for video: " + videoId); - } - } - - /** - * Verify if a video alt thumbnail exists. Does so by making a minimal HEAD http request. - */ - synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonNull ThumbnailQuality quality, - @NonNull String imageUrl) { - if (highestQualityVerified != null && highestQualityVerified.ordinal() >= quality.ordinal()) { - return true; // Previously verified as existing. - } - - final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean(); - if (lowestQualityNotAvailable != null && lowestQualityNotAvailable.ordinal() <= quality.ordinal()) { - if (fastQuality || System.currentTimeMillis() < timeToReVerifyLowestQuality) { - return false; // Previously verified as not existing. - } - // Enough time has passed, and should re-verify again. - LogHelper.printDebug(() -> "Resetting lowest verified quality for: " + videoId); - lowestQualityNotAvailable = null; - } - - if (fastQuality) { - return true; // Unknown if it exists or not. Use the URL anyways and update afterwards if loading fails. - } - - boolean imageFileFound; - try { - LogHelper.printDebug(() -> "Verifying image: " + imageUrl); - // This hooked code is running on a low priority thread, and it's slightly faster - // to run the url connection thru the integrations thread pool which runs at the highest priority. - final long start = System.currentTimeMillis(); - imageFileFound = ReVancedUtils.submitOnBackgroundThread(() -> { - final int connectionTimeoutMillis = 5000; - HttpURLConnection connection = (HttpURLConnection) new URL(imageUrl).openConnection(); - connection.setConnectTimeout(connectionTimeoutMillis); - connection.setReadTimeout(connectionTimeoutMillis); - connection.setRequestMethod("HEAD"); - // Even with a HEAD request, the response is the same size as a full GET request. - // Using an empty range fixes this. - connection.setRequestProperty("Range", "bytes=0-0"); - final int responseCode = connection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_PARTIAL) { - String contentType = connection.getContentType(); - return (contentType != null && contentType.startsWith("image")); - } - if (responseCode != HttpURLConnection.HTTP_NOT_FOUND) { - LogHelper.printDebug(() -> "Unexpected response code: " + responseCode + " for url: " + imageUrl); - } - return false; - }).get(); - LogHelper.printDebug(() -> "Alt verification took: " + (System.currentTimeMillis() - start) + "ms"); - } catch (ExecutionException | InterruptedException ex) { - LogHelper.printInfo(() -> "Could not verify alt url: " + imageUrl, ex); - imageFileFound = false; - } - - setQualityVerified(videoId, quality, imageFileFound); - return imageFileFound; - } - } - - /** - * YouTube video thumbnail url, decoded into it's relevant parts. - */ - private static class DecodedThumbnailUrl { - /** - * YouTube thumbnail URL prefix. Can be '/vi/' or '/vi_webp/' - */ - private static final String YOUTUBE_THUMBNAIL_PREFIX = "https://i.ytimg.com/vi"; - - @Nullable - static DecodedThumbnailUrl decodeImageUrl(String url) { - final int videoIdStartIndex = url.indexOf('/', YOUTUBE_THUMBNAIL_PREFIX.length()) + 1; - if (videoIdStartIndex <= 0) return null; - final int videoIdEndIndex = url.indexOf('/', videoIdStartIndex); - if (videoIdEndIndex < 0) return null; - final int imageSizeStartIndex = videoIdEndIndex + 1; - final int imageSizeEndIndex = url.indexOf('.', imageSizeStartIndex); - if (imageSizeEndIndex < 0) return null; - int imageExtensionEndIndex = url.indexOf('?', imageSizeEndIndex); - if (imageExtensionEndIndex < 0) imageExtensionEndIndex = url.length(); - return new DecodedThumbnailUrl(url, videoIdStartIndex, videoIdEndIndex, - imageSizeStartIndex, imageSizeEndIndex, imageExtensionEndIndex); - } - - /** Full usable url, but stripped of any tracking information. */ - final String sanitizedUrl; - /** Url up to the video id. */ - final String urlPrefix; - final String videoId; - /** Quality, such as hq720 or sddefault. */ - final String imageQuality; - /** jpg or webp */ - final String imageExtension; - /** User view tracking parameters, only present on some images. */ - final String urlTrackingParameters; - - private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEndIndex, - int imageSizeStartIndex, int imageSizeEndIndex, int imageExtensionEndIndex) { - sanitizedUrl = fullUrl.substring(0, imageExtensionEndIndex); - urlPrefix = fullUrl.substring(0, videoIdStartIndex); - videoId = fullUrl.substring(videoIdStartIndex, videoIdEndIndex); - imageQuality = fullUrl.substring(imageSizeStartIndex, imageSizeEndIndex); - imageExtension = fullUrl.substring(imageSizeEndIndex + 1, imageExtensionEndIndex); - urlTrackingParameters = (imageExtensionEndIndex == fullUrl.length()) - ? "" : fullUrl.substring(imageExtensionEndIndex); - } - } - - static { - // Fix any bad imported data. - final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); - if (altThumbnailType < 1 || altThumbnailType > 3) { - LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); - SettingsEnum.ALT_THUMBNAIL_TYPE.saveValue(SettingsEnum.ALT_THUMBNAIL_TYPE.defaultValue); - } - } - - /** - * Injection point. Called off the main thread and by multiple threads at the same time. - * - * @param originalUrl Image url for all url images loaded, including video thumbnails. - */ - public static String overrideImageURL(String originalUrl) { - try { - if (!SettingsEnum.ALT_THUMBNAIL.getBoolean()) { - return originalUrl; - } - DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(originalUrl); - if (decodedUrl == null) { - return originalUrl; // Not a thumbnail. - } - - // Keep any tracking parameters out of the logs, and log only the base url. - LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - - ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); - if (qualityToUse == null) return originalUrl; // Video is a short. - - // Images could be upgraded to webp if they are not already, but this fails quite often, - // especially for new videos uploaded in the last hour. - // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. - // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). - - StringBuilder builder = new StringBuilder(originalUrl.length() + 2); - builder.append(decodedUrl.urlPrefix); - builder.append(decodedUrl.videoId).append('/'); - builder.append(qualityToUse.getAltImageNameToUse()); - builder.append('.').append(decodedUrl.imageExtension); - - String sanitizedReplacement = builder.toString(); - if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { - return originalUrl; - } - - LogHelper.printDebug(() -> "Replaced url: " + sanitizedReplacement); - - // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. - // This likely is used for recommendations, so they are retained if present. - builder.append(decodedUrl.urlTrackingParameters); - return builder.toString(); - } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails failure", ex); - return originalUrl; - } - } - - /** - * Injection point. - * - * Cronet considers all completed connections as a success, even if the response is 404 or 5xx. - */ - public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { - try { - if (responseInfo.getHttpStatusCode() == 404 && SettingsEnum.ALT_THUMBNAIL.getBoolean()) { - // Fast alt thumbnails is enabled and the thumbnail is not available. - // The video is: - // - live stream - // - upcoming unreleased video - // - very old - // - very low view count - // Take note of this, so if the image reloads the original thumbnail will be used. - DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(responseInfo.getUrl()); - if (decodedUrl == null) { - return; // Not a thumbnail. - } - - ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); - if (quality == null) { - // Video is a short or unknown quality, but the url returned 404. Should never happen. - LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); - return; - } - - VerifiedQualities.setAltThumbnailDoesNotExist(decodedUrl.videoId, quality); - } - } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails callback failure", ex); - } - } - -} diff --git a/app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java b/app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java deleted file mode 100644 index a2b4a94c..00000000 --- a/app/src/main/java/app/revanced/integrations/patches/CustomPlayerOverlayOpacityPatch.java +++ /dev/null @@ -1,22 +0,0 @@ -package app.revanced.integrations.patches; - -import android.widget.ImageView; - -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.ReVancedUtils; - -public class CustomPlayerOverlayOpacityPatch { - private static final int DEFAULT_OPACITY = (int) SettingsEnum.PLAYER_OVERLAY_OPACITY.defaultValue; - - public static void changeOpacity(ImageView imageView) { - int opacity = SettingsEnum.PLAYER_OVERLAY_OPACITY.getInt(); - - if (opacity < 0 || opacity > 100) { - ReVancedUtils.showToastLong("Player overlay opacity must be between 0-100"); - SettingsEnum.PLAYER_OVERLAY_OPACITY.saveValue(DEFAULT_OPACITY); - opacity = DEFAULT_OPACITY; - } - - imageView.setImageAlpha((opacity * 255) / 100); - } -} diff --git a/app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java b/app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java deleted file mode 100644 index 638abd23..00000000 --- a/app/src/main/java/app/revanced/integrations/patches/EnableTabletLayoutPatch.java +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.integrations.patches; - -import app.revanced.integrations.settings.SettingsEnum; - -public final class EnableTabletLayoutPatch { - public static boolean enableTabletLayout() { - return SettingsEnum.TABLET_LAYOUT.getBoolean(); - } -} diff --git a/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java b/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java index bc23e1bd..1640c7bf 100644 --- a/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/OpenLinksExternallyPatch.java @@ -4,14 +4,16 @@ import app.revanced.integrations.settings.SettingsEnum; public class OpenLinksExternallyPatch { /** - * Return the intent to open links with. If empty, the link will be opened with the default browser. + * Override 'android.support.customtabs.action.CustomTabsService', + * in order to open links in the default browser. This is done by returning an empty string, + * for the service that handles custom tabs in the Android support library + * which opens links in the default service instead. * - * @param originalIntent The original intent to open links with. - * @return The intent to open links with. Empty means the link will be opened with the default browser. + * @param original The original custom tabs service. + * @return The new, default service to open links with or the original service. */ - public static String getIntent(String originalIntent) { - if (SettingsEnum.EXTERNAL_BROWSER.getBoolean()) return ""; - - return originalIntent; + public static String enableExternalBrowser(String original) { + if (SettingsEnum.EXTERNAL_BROWSER.getBoolean()) original = ""; + return original; } } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java index 40192d0d..3797f53a 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/AdsFilter.java @@ -86,7 +86,7 @@ public final class AdsFilter extends Filter { "cta_shelf_card" ); - this.pathFilterGroupList.addAll( + this.pathFilterGroups.addAll( generalAds, buttonedAd, merchandise, @@ -95,16 +95,16 @@ public final class AdsFilter extends Filter { webLinkPanel, movieAds ); - this.identifierFilterGroupList.addAll(carouselAd); + this.identifierFilterGroups.addAll(carouselAd); } @Override - public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (exceptions.matches(path)) return false; - return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } /** diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java index 19cddf3f..5722e640 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ButtonsFilter.java @@ -1,114 +1,59 @@ package app.revanced.integrations.patches.components; -import android.os.Build; - import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import app.revanced.integrations.settings.SettingsEnum; -@RequiresApi(api = Build.VERSION_CODES.N) final class ButtonsFilter extends Filter { - private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml"; - - private final StringFilterGroup actionBarGroup; - private final StringFilterGroup bufferFilterPathGroup; - private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList(); + private final StringFilterGroup actionBarRule; public ButtonsFilter() { - actionBarGroup = new StringFilterGroup( + actionBarRule = new StringFilterGroup( null, - VIDEO_ACTION_BAR_PATH + "video_action_bar" ); - identifierFilterGroupList.addAll(actionBarGroup); - - bufferFilterPathGroup = new StringFilterGroup( - null, - "|CellType|CollectionType|CellType|ContainerType|button.eml|" - ); - pathFilterGroupList.addAll( + pathFilterGroups.addAll( new StringFilterGroup( SettingsEnum.HIDE_LIKE_DISLIKE_BUTTON, - "|segmented_like_dislike_button" + "|like_button", + "dislike_button" ), new StringFilterGroup( SettingsEnum.HIDE_DOWNLOAD_BUTTON, - "|download_button.eml|" + "download_button" ), new StringFilterGroup( SettingsEnum.HIDE_PLAYLIST_BUTTON, - "|save_to_playlist_button" + "save_to_playlist_button" ), new StringFilterGroup( SettingsEnum.HIDE_CLIP_BUTTON, "|clip_button.eml|" ), - bufferFilterPathGroup - ); - - bufferButtonsGroupList.addAll( - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_LIVE_CHAT_BUTTON, - "yt_outline_message_bubble_overlap" + new StringFilterGroup( + SettingsEnum.HIDE_ACTION_BUTTONS, + "ContainerType|video_action_button", + "|CellType|CollectionType|CellType|ContainerType|button.eml|" ), - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_REPORT_BUTTON, - "yt_outline_flag" - ), - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_SHARE_BUTTON, - "yt_outline_share" - ), - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_REMIX_BUTTON, - "yt_outline_youtube_shorts_plus" - ), - // Check for clip button both here and using a path filter, - // as there's a chance the path is a generic action button and won't contain 'clip_button' - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_CLIP_BUTTON, - "yt_outline_scissors" - ), - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_SHOP_BUTTON, - "yt_outline_bag" - ), - new ByteArrayAsStringFilterGroup( - SettingsEnum.HIDE_THANKS_BUTTON, - "yt_outline_dollar_sign_heart" - ) + actionBarRule ); } private boolean isEveryFilterGroupEnabled() { - for (var group : pathFilterGroupList) - if (!group.isEnabled()) return false; - - for (var group : bufferButtonsGroupList) - if (!group.isEnabled()) return false; + for (StringFilterGroup rule : pathFilterGroups) + if (!rule.isEnabled()) return false; return true; } @Override - public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { - // If the current matched group is the action bar group, - // in case every filter group is enabled, hide the action bar. - if (matchedGroup == actionBarGroup) { - if (!isEveryFilterGroupEnabled()) { - return false; - } - } else if (matchedGroup == bufferFilterPathGroup) { - // Make sure the current path is the right one - // to avoid false positives. - if (!path.startsWith(VIDEO_ACTION_BAR_PATH)) return false; - - // In case the group list has no match, return false. - if (!bufferButtonsGroupList.check(protobufBufferArray).isFiltered()) return false; + if (matchedGroup == actionBarRule) { + return isEveryFilterGroupEnabled(); } - return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java index 089fb948..22a9cba6 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/CommentsFilter.java @@ -18,7 +18,7 @@ final class CommentsFilter extends Filter { "comments_entry_point_simplebox" ); - this.pathFilterGroupList.addAll( + this.pathFilterGroups.addAll( comments, previewComment ); diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java index 67811a89..53860b9e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LayoutComponentsFilter.java @@ -142,7 +142,7 @@ public final class LayoutComponentsFilter extends Filter { "chips_shelf" ); - this.pathFilterGroupList.addAll( + this.pathFilterGroups.addAll( channelBar, communityPosts, paidContent, @@ -165,19 +165,19 @@ public final class LayoutComponentsFilter extends Filter { custom ); - this.identifierFilterGroupList.addAll( + this.identifierFilterGroups.addAll( graySeparator, chipsShelf ); } @Override - public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (matchedGroup != custom && exceptions.matches(path)) return false; // Exceptions are not filtered. - return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java index 90aa6781..b61c9df6 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java @@ -81,7 +81,8 @@ class StringFilterGroup extends FilterGroup { @Override public FilterGroupResult check(final String string) { - return new FilterGroupResult(setting, isEnabled() && ReVancedUtils.containsAny(string, filters)); + return new FilterGroupResult(setting, + (setting == null || setting.getBoolean()) && ReVancedUtils.containsAny(string, filters)); } } @@ -273,14 +274,25 @@ abstract class Filter { * will never be called for any matches. */ - protected final StringFilterGroupList pathFilterGroupList = new StringFilterGroupList(); - protected final StringFilterGroupList identifierFilterGroupList = new StringFilterGroupList(); + protected final StringFilterGroupList pathFilterGroups = new StringFilterGroupList(); + protected final StringFilterGroupList identifierFilterGroups = new StringFilterGroupList(); + /** + * A collection of {@link ByteArrayFilterGroup} that are always searched for (no matter what). + * + * If possible, avoid adding values to this list and instead use a path or identifier filter + * for the item you are looking for. Then inside + * {@link #isFiltered(String, String, byte[], FilterGroupList, FilterGroup, int)}, + * the buffer can then be searched using using a different + * {@link ByteArrayFilterGroupList} or a {@link ByteArrayFilterGroup}. + * This way, the expensive buffer searching only occurs if the cheap and fast path/identifier is already found. + */ + protected final ByteArrayFilterGroupList protobufBufferFilterGroups = new ByteArrayFilterGroupList(); /** * Called after an enabled filter has been matched. * Default implementation is to always filter the matched item. * Subclasses can perform additional or different checks if needed. - *

+ * * Method is called off the main thread. * * @param matchedList The list the group filter belongs to. @@ -289,13 +301,15 @@ abstract class Filter { * @return True if the litho item should be filtered out. */ @SuppressWarnings("rawtypes") - boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { if (SettingsEnum.DEBUG.getBoolean()) { - if (pathFilterGroupList == matchedList) { + if (pathFilterGroups == matchedList) { LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered path: " + path); - } else if (identifierFilterGroupList == matchedList) { + } else if (identifierFilterGroups == matchedList) { LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered identifier: " + identifier); + } else if (protobufBufferFilterGroups == matchedList) { + LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered from protobuf-buffer"); } } return true; @@ -309,14 +323,13 @@ public final class LithoFilterPatch { * Simple wrapper to pass the litho parameters through the prefix search. */ private static final class LithoFilterParameters { - @Nullable - final String identifier; final String path; + final String identifier; final byte[] protoBuffer; - LithoFilterParameters(@Nullable String lithoIdentifier, StringBuilder lithoPath, ByteBuffer protoBuffer) { - this.identifier = lithoIdentifier; + LithoFilterParameters(StringBuilder lithoPath, String lithoIdentifier, ByteBuffer protoBuffer) { this.path = lithoPath.toString(); + this.identifier = lithoIdentifier; this.protoBuffer = protoBuffer.array(); } @@ -329,10 +342,9 @@ public final class LithoFilterPatch { builder.append(identifier); builder.append(" Path: "); builder.append(path); - if (SettingsEnum.DEBUG_PROTOBUFFER.getBoolean()) { - builder.append(" BufferStrings: "); - findAsciiStrings(builder, protoBuffer); - } + // TODO: allow turning on/off buffer logging with a debug setting? + builder.append(" BufferStrings: "); + findAsciiStrings(builder, protoBuffer); return builder.toString(); } @@ -354,9 +366,7 @@ public final class LithoFilterPatch { int value = buffer[end]; if (value < minimumAscii || value > maximumAscii || end == length - 1) { if (end - start >= minimumAsciiStringLength) { - for (int i = start; i < end; i++) { - builder.append((char) buffer[i]); - } + builder.append(new String(buffer, start, end - start)); builder.append(delimitingCharacter); } start = end + 1; @@ -372,24 +382,22 @@ public final class LithoFilterPatch { private static final StringTrieSearch pathSearchTree = new StringTrieSearch(); private static final StringTrieSearch identifierSearchTree = new StringTrieSearch(); - - /** - * Because litho filtering is multi-threaded and the buffer is passed in from a different injection point, - * the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads. - */ - private static final ThreadLocal bufferThreadLocal = new ThreadLocal<>(); + private static final ByteTrieSearch protoSearchTree = new ByteTrieSearch(); static { for (Filter filter : filters) { - filterGroupLists(pathSearchTree, filter, filter.pathFilterGroupList); - filterGroupLists(identifierSearchTree, filter, filter.identifierFilterGroupList); + filterGroupLists(pathSearchTree, filter, filter.pathFilterGroups); + filterGroupLists(identifierSearchTree, filter, filter.identifierFilterGroups); + filterGroupLists(protoSearchTree, filter, filter.protobufBufferFilterGroups); } LogHelper.printDebug(() -> "Using: " + pathSearchTree.numberOfPatterns() + " path filters" + " (" + pathSearchTree.getEstimatedMemorySize() + " KB), " + identifierSearchTree.numberOfPatterns() + " identifier filters" - + " (" + identifierSearchTree.getEstimatedMemorySize() + " KB)"); + + " (" + identifierSearchTree.getEstimatedMemorySize() + " KB), " + + protoSearchTree.numberOfPatterns() + " buffer filters" + + " (" + protoSearchTree.getEstimatedMemorySize() + " KB)"); } private static void filterGroupLists(TrieSearch pathSearchTree, @@ -402,7 +410,7 @@ public final class LithoFilterPatch { pathSearchTree.addPattern(pattern, (textSearched, matchedStartIndex, callbackParameter) -> { if (!group.isEnabled()) return false; LithoFilterParameters parameters = (LithoFilterParameters) callbackParameter; - return filter.isFiltered(parameters.identifier, parameters.path, parameters.protoBuffer, + return filter.isFiltered(parameters.path, parameters.identifier, parameters.protoBuffer, list, group, matchedStartIndex); } ); @@ -414,36 +422,21 @@ public final class LithoFilterPatch { * Injection point. Called off the main thread. */ @SuppressWarnings("unused") - public static void setProtoBuffer(@NonNull ByteBuffer protobufBuffer) { - // Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes. - // This is intentional, as it appears the buffer can be set once and then filtered multiple times. - // The buffer will be cleared from memory after a new buffer is set by the same thread, - // or when the calling thread eventually dies. - bufferThreadLocal.set(protobufBuffer); - } - - /** - * Injection point. Called off the main thread, and commonly called by multiple threads at the same time. - */ - @SuppressWarnings("unused") - public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) { + public static boolean filter(@NonNull StringBuilder pathBuilder, @Nullable String lithoIdentifier, + @NonNull ByteBuffer protobufBuffer) { try { // It is assumed that protobufBuffer is empty as well in this case. if (pathBuilder.length() == 0) return false; - ByteBuffer protobufBuffer = bufferThreadLocal.get(); - if (protobufBuffer == null) { - LogHelper.printException(() -> "Proto buffer is null"); // Should never happen - return false; - } - LithoFilterParameters parameter = new LithoFilterParameters(lithoIdentifier, pathBuilder, protobufBuffer); + LithoFilterParameters parameter = new LithoFilterParameters(pathBuilder, lithoIdentifier, protobufBuffer); LogHelper.printDebug(() -> "Searching " + parameter); + if (pathSearchTree.matches(parameter.path, parameter)) return true; if (parameter.identifier != null) { if (identifierSearchTree.matches(parameter.identifier, parameter)) return true; } - if (pathSearchTree.matches(parameter.path, parameter)) return true; + if (protoSearchTree.matches(parameter.protoBuffer, parameter)) return true; } catch (Exception ex) { LogHelper.printException(() -> "Litho filter failure", ex); } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java index 00b11af3..ab2e63b4 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/PlaybackSpeedMenuFilterPatch.java @@ -8,14 +8,14 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter { public static volatile boolean isPlaybackSpeedMenuVisible; public PlaybackSpeedMenuFilterPatch() { - pathFilterGroupList.addAll(new StringFilterGroup( + pathFilterGroups.addAll(new StringFilterGroup( null, "playback_speed_sheet_content.eml-js" )); } @Override - boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { isPlaybackSpeedMenuVisible = true; diff --git a/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java index c394f84f..3f435e08 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/PlayerFlyoutMenuItemsFilter.java @@ -15,7 +15,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter { @RequiresApi(api = Build.VERSION_CODES.N) public PlayerFlyoutMenuItemsFilter() { - identifierFilterGroupList.addAll(new StringFilterGroup(null, "overflow_menu_item.eml|")); + identifierFilterGroups.addAll(new StringFilterGroup(null, "overflow_menu_item.eml|")); flyoutFilterGroupList.addAll( new ByteArrayAsStringFilterGroup( @@ -62,12 +62,12 @@ public class PlayerFlyoutMenuItemsFilter extends Filter { } @Override - boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { // Only 1 group is added to the parent class, so the matched group must be the overflow menu. if (matchedIndex == 0 && flyoutFilterGroupList.check(protobufBufferArray).isFiltered()) { // Super class handles logging. - return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); } return false; } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java index b6e1f3fa..48542a06 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/ShortsFilter.java @@ -12,21 +12,45 @@ import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar; import app.revanced.integrations.settings.SettingsEnum; public final class ShortsFilter extends Filter { - private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.eml"; + private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar"; public static PivotBar pivotBar; // Set by patch. private final StringFilterGroup channelBar; private final StringFilterGroup soundButton; private final StringFilterGroup infoPanel; - private final StringFilterGroup shortsShelfHeader; public ShortsFilter() { - // Home / subscription feed components. - var thanksButton = new StringFilterGroup( + channelBar = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_CHANNEL_BAR, + REEL_CHANNEL_BAR_PATH + ); + + soundButton = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_SOUND_BUTTON, + "reel_pivot_button" + ); + + infoPanel = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_INFO_PANEL, + "shorts_info_panel_overview" + ); + + final var thanksButton = new StringFilterGroup( SettingsEnum.HIDE_SHORTS_THANKS_BUTTON, "suggested_action" ); - var shorts = new StringFilterGroup( + + final var subscribeButton = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_SUBSCRIBE_BUTTON, + "subscribe_button" + ); + + final var joinButton = new StringFilterGroup( + SettingsEnum.HIDE_SHORTS_JOIN_BUTTON, + "sponsor_button" + ); + + final var shorts = new StringFilterGroup( SettingsEnum.HIDE_SHORTS, "shorts_shelf", "inline_shorts", @@ -34,58 +58,22 @@ public final class ShortsFilter extends Filter { "shorts_video_cell", "shorts_pivot_item" ); - // Use a different filter group for this pattern, as it requires an additional check after matching. - shortsShelfHeader = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS, - "shelf_header.eml" - ); - identifierFilterGroupList.addAll(shorts, shortsShelfHeader, thanksButton); - - // Shorts player components. - var joinButton = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_JOIN_BUTTON, - "sponsor_button" - ); - var subscribeButton = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_SUBSCRIBE_BUTTON, - "subscribe_button" - ); - channelBar = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_CHANNEL_BAR, - REEL_CHANNEL_BAR_PATH - ); - soundButton = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_SOUND_BUTTON, - "reel_pivot_button" - ); - infoPanel = new StringFilterGroup( - SettingsEnum.HIDE_SHORTS_INFO_PANEL, - "shorts_info_panel_overview" - ); - pathFilterGroupList.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel); + pathFilterGroups.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel); + identifierFilterGroups.addAll(shorts, thanksButton); } @Override - boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { - if (matchedList == pathFilterGroupList) { - // Always filter if matched. - if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) - return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) return true; - // Filter other path groups from pathFilterGroupList, only when reelChannelBar is visible - // to avoid false positives. - if (!path.startsWith(REEL_CHANNEL_BAR_PATH)) - return false; - } else if (matchedGroup == shortsShelfHeader) { - // Because the header is used in watch history and possibly other places, check for the index, - // which is 0 when the shelf header is used for Shorts. - if (matchedIndex != 0) return false; + // Filter the path only when reelChannelBar is visible. + if (pathFilterGroups == matchedList) { + return path.contains(REEL_CHANNEL_BAR_PATH); } - // Super class handles logging. - return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex); + return identifierFilterGroups == matchedList; } public static void hideShortsShelf(final View shortsShelfView) { diff --git a/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java index 8b5d9643..fb94de05 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/VideoQualityMenuFilterPatch.java @@ -10,14 +10,14 @@ public final class VideoQualityMenuFilterPatch extends Filter { public static volatile boolean isVideoQualityMenuVisible; public VideoQualityMenuFilterPatch() { - pathFilterGroupList.addAll(new StringFilterGroup( + pathFilterGroups.addAll(new StringFilterGroup( SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU, "quick_quality_sheet_content.eml-js" )); } @Override - boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, + boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray, FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) { isVideoQualityMenuVisible = true; diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index c7e6c432..ef07eff4 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -1,12 +1,26 @@ package app.revanced.integrations.settings; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static app.revanced.integrations.settings.SettingsEnum.ReturnType.BOOLEAN; +import static app.revanced.integrations.settings.SettingsEnum.ReturnType.FLOAT; +import static app.revanced.integrations.settings.SettingsEnum.ReturnType.INTEGER; +import static app.revanced.integrations.settings.SettingsEnum.ReturnType.LONG; +import static app.revanced.integrations.settings.SettingsEnum.ReturnType.STRING; +import static app.revanced.integrations.settings.SharedPrefCategory.RETURN_YOUTUBE_DISLIKE; +import static app.revanced.integrations.settings.SharedPrefCategory.SPONSOR_BLOCK; +import static app.revanced.integrations.utils.StringRef.str; + import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import app.revanced.integrations.sponsorblock.SponsorBlockSettings; -import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; import app.revanced.integrations.utils.StringRef; +import app.revanced.integrations.utils.LogHelper; + import org.json.JSONException; import org.json.JSONObject; @@ -15,13 +29,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import static app.revanced.integrations.settings.SettingsEnum.ReturnType.*; -import static app.revanced.integrations.settings.SharedPrefCategory.RETURN_YOUTUBE_DISLIKE; -import static app.revanced.integrations.settings.SharedPrefCategory.SPONSOR_BLOCK; -import static app.revanced.integrations.utils.StringRef.str; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; - public enum SettingsEnum { // External downloader @@ -82,18 +89,12 @@ public enum SettingsEnum { // Action buttons HIDE_LIKE_DISLIKE_BUTTON("revanced_hide_like_dislike_button", BOOLEAN, FALSE), - HIDE_LIVE_CHAT_BUTTON("revanced_hide_live_chat_button", BOOLEAN, FALSE), - HIDE_SHARE_BUTTON("revanced_hide_share_button", BOOLEAN, FALSE), - HIDE_REPORT_BUTTON("revanced_hide_report_button", BOOLEAN, FALSE), - HIDE_REMIX_BUTTON("revanced_hide_remix_button", BOOLEAN, TRUE), HIDE_DOWNLOAD_BUTTON("revanced_hide_download_button", BOOLEAN, FALSE), - HIDE_THANKS_BUTTON("revanced_hide_thanks_button", BOOLEAN, TRUE), - HIDE_CLIP_BUTTON("revanced_hide_clip_button", BOOLEAN, TRUE), HIDE_PLAYLIST_BUTTON("revanced_hide_playlist_button", BOOLEAN, FALSE), - HIDE_SHOP_BUTTON("revanced_hide_shop_button", BOOLEAN, TRUE), + HIDE_CLIP_BUTTON("revanced_hide_clip_button", BOOLEAN, FALSE, "revanced_hide_clip_button_user_dialog_message"), + HIDE_ACTION_BUTTONS("revanced_hide_action_buttons", BOOLEAN, FALSE), // Layout - PLAYER_OVERLAY_OPACITY("revanced_player_overlay_opacity", INTEGER, 100, true), DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE), HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true), HIDE_ARTIST_CARDS("revanced_hide_artist_cards", BOOLEAN, FALSE), @@ -114,6 +115,7 @@ public enum SettingsEnum { HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE), HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true), HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", BOOLEAN, FALSE), + HIDE_PLAYER_OVERLAY("revanced_hide_player_overlay", BOOLEAN, FALSE, true), HIDE_PREVIEW_COMMENT("revanced_hide_preview_comment", BOOLEAN, FALSE, true), HIDE_SEEKBAR("revanced_hide_seekbar", BOOLEAN, FALSE), HIDE_SEEKBAR_THUMBNAIL("revanced_hide_seekbar_thumbnail", BOOLEAN, FALSE), @@ -127,8 +129,9 @@ public enum SettingsEnum { SPOOF_APP_VERSION("revanced_spoof_app_version", BOOLEAN, FALSE, true, "revanced_spoof_app_version_user_dialog_message"), SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.08.35", true, parents(SPOOF_APP_VERSION)), USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true), - TABLET_LAYOUT("revanced_tablet_layout", BOOLEAN, FALSE, true, "revanced_tablet_layout_user_dialog_message"), WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true), + @Deprecated + DEPRECATED_SEEKBAR_COLOR("revanced_seekbar_color", STRING, "#FF0000"), // TODO: delete this SEEKBAR_CUSTOM_COLOR("revanced_seekbar_custom_color", BOOLEAN, TRUE, true), SEEKBAR_CUSTOM_COLOR_VALUE("revanced_seekbar_custom_color_value", STRING, "#FF0000", true, parents(SEEKBAR_CUSTOM_COLOR)), HIDE_FILTER_BAR_FEED_IN_FEED("revanced_hide_filter_bar_feed_in_feed", BOOLEAN, FALSE, true), @@ -146,10 +149,6 @@ public enum SettingsEnum { HIDE_SHORTS_NAVIGATION_BAR("revanced_hide_shorts_navigation_bar", BOOLEAN, TRUE, true), HIDE_SHORTS("revanced_hide_shorts", BOOLEAN, FALSE, true), - ALT_THUMBNAIL("revanced_alt_thumbnail", BOOLEAN, FALSE), - ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2, parents(ALT_THUMBNAIL)), - ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE, parents(ALT_THUMBNAIL)), - //Player flyout menu items HIDE_QUALITY_MENU("revanced_hide_player_flyout_quality", BOOLEAN, FALSE), HIDE_CAPTIONS_MENU("revanced_hide_player_flyout_captions", BOOLEAN, FALSE), @@ -159,7 +158,7 @@ public enum SettingsEnum { HIDE_HELP_MENU("revanced_hide_player_flyout_help", BOOLEAN, TRUE), HIDE_SPEED_MENU("revanced_hide_player_flyout_speed", BOOLEAN, FALSE), HIDE_MORE_INFO_MENU("revanced_hide_player_flyout_more_info", BOOLEAN, TRUE), - HIDE_AUDIO_TRACK_MENU("revanced_hide_player_flyout_audio_track", BOOLEAN, FALSE), + HIDE_AUDIO_TRACK_MENU("revanced_hide_player_flyout_audio_track", BOOLEAN, TRUE), HIDE_WATCH_IN_VR_MENU("revanced_hide_player_flyout_watch_in_vr", BOOLEAN, TRUE), // Misc @@ -192,7 +191,6 @@ public enum SettingsEnum { // Debugging DEBUG("revanced_debug", BOOLEAN, FALSE), DEBUG_STACKTRACE("revanced_debug_stacktrace", BOOLEAN, FALSE, parents(DEBUG)), - DEBUG_PROTOBUFFER("revanced_debug_protobuffer", BOOLEAN, FALSE, parents(DEBUG)), DEBUG_TOAST_ON_ERROR("revanced_debug_toast_on_error", BOOLEAN, TRUE, "revanced_debug_toast_on_error_user_dialog_message"), // ReturnYoutubeDislike @@ -376,6 +374,14 @@ public enum SettingsEnum { // TODO: delete DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU (When? anytime). migrateOldSettingToNew(DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU, SHOW_OLD_VIDEO_QUALITY_MENU); + // TODO: delete this seekbar color migration code + String oldSeekbarColorValue = DEPRECATED_SEEKBAR_COLOR.getString(); + if (!oldSeekbarColorValue.equalsIgnoreCase((String) DEPRECATED_SEEKBAR_COLOR.defaultValue)) { + SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(oldSeekbarColorValue); + SEEKBAR_CUSTOM_COLOR.saveValue(true); + DEPRECATED_SEEKBAR_COLOR.saveValue(DEPRECATED_SEEKBAR_COLOR.defaultValue); + } + // endregion } diff --git a/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java b/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java index 17799e25..bded5469 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java +++ b/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java @@ -80,24 +80,12 @@ public enum SharedPrefCategory { @NonNull public String getString(@NonNull String key, @NonNull String _default) { Objects.requireNonNull(_default); - try { - return preferences.getString(key, _default); - } catch (ClassCastException ex) { - // Value stored is a completely different type (should never happen). - removeConflictingPreferenceKeyValue(key); - return _default; - } + return preferences.getString(key, _default); } public boolean getBoolean(@NonNull String key, boolean _default) { - try { - return preferences.getBoolean(key, _default); - } catch (ClassCastException ex) { - // Value stored is a completely different type (should never happen). - removeConflictingPreferenceKeyValue(key); - return _default; - } + return preferences.getBoolean(key, _default); } @NonNull diff --git a/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java b/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java index 807f3f8a..e83a921c 100644 --- a/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/utils/ByteTrieSearch.java @@ -30,19 +30,9 @@ public final class ByteTrieSearch extends TrieSearch { super.addPattern(pattern, pattern.length, Objects.requireNonNull(callback)); } - @Override - public boolean matches(@NonNull byte[] textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter) { - return super.matches(textToSearch, textToSearch.length, startIndex, endIndex, callbackParameter); - } - - @Override - public boolean matches(@NonNull byte[] textToSearch, int startIndex) { - return matches(textToSearch, startIndex, textToSearch.length, null); - } - @Override public boolean matches(@NonNull byte[] textToSearch, @Nullable Object callbackParameter) { - return matches(textToSearch,0, textToSearch.length, callbackParameter); + return super.matches(textToSearch, textToSearch.length, callbackParameter); } } diff --git a/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java b/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java index 1a1a0a9e..7afafc60 100644 --- a/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/utils/StringTrieSearch.java @@ -33,18 +33,8 @@ public final class StringTrieSearch extends TrieSearch { super.addPattern(pattern, pattern.length(), Objects.requireNonNull(callback)); } - @Override - public boolean matches(@NonNull String textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter) { - return super.matches(textToSearch, textToSearch.length(), startIndex, endIndex, callbackParameter); - } - @Override public boolean matches(@NonNull String textToSearch, @Nullable Object callbackParameter) { - return matches(textToSearch, 0, textToSearch.length(), callbackParameter); - } - - @Override - public boolean matches(@NonNull String textToSearch, int startIndex) { - return matches(textToSearch, startIndex, textToSearch.length(), null); + return super.matches(textToSearch, textToSearch.length(), callbackParameter); } } diff --git a/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java b/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java index 6458f07c..9d2db167 100644 --- a/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/utils/TrieSearch.java @@ -243,17 +243,12 @@ public abstract class TrieSearch { root.addPattern(pattern, patternLength, 0, callback); } - final boolean matches(@NonNull T textToSearch, int textToSearchLength, int startIndex, int endIndex, - @Nullable Object callbackParameter) { - if (endIndex > textToSearchLength) { - throw new IllegalArgumentException("endIndex: " + endIndex - + " is greater than texToSearchLength: " + textToSearchLength); - } + boolean matches(@NonNull T textToSearch, int textToSearchLength, @Nullable Object callbackParameter) { if (patterns.size() == 0) { return false; // No patterns were added. } - for (int i = startIndex; i < endIndex; i++) { - if (root.matches(textToSearch, endIndex, i, 0, callbackParameter)) return true; + for (int i = 0; i < textToSearchLength; i++) { + if (root.matches(textToSearch, textToSearchLength, i, 0, callbackParameter)) return true; } return false; } @@ -292,27 +287,19 @@ public abstract class TrieSearch { */ public abstract void addPattern(@NonNull T pattern, @NonNull TriePatternMatchedCallback callback); - /** * Searches through text, looking for any substring that matches any pattern in this tree. * * @param textToSearch Text to search through. - * @param startIndex Index to start searching, inclusive value. - * @param endIndex Index to stop matching, exclusive value. * @param callbackParameter Optional parameter passed to the callbacks. * @return If any pattern matched, and it's callback halted searching. */ - public abstract boolean matches(@NonNull T textToSearch, int startIndex, int endIndex, @Nullable Object callbackParameter); - - public abstract boolean matches(@NonNull T textToSearch, int startIndex); - public abstract boolean matches(@NonNull T textToSearch, @Nullable Object callbackParameter); - public final boolean matches(@NonNull T textToSearch, int startIndex, int endIndex) { - return matches(textToSearch, startIndex, endIndex, null); - } - + /** + * Identical to {@link #matches(Object, Object)} but with a null callback parameter. + */ public final boolean matches(@NonNull T textToSearch) { - return matches(textToSearch, 0); + return matches(textToSearch, null); } } diff --git a/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt b/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt index 5c4323e3..9d47bc8f 100644 --- a/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt +++ b/app/src/main/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt @@ -22,7 +22,7 @@ internal object TwiFucker { private fun JSONObject.jsonCheckAndRemoveRecommendedUsers() { if (jsonHasRecommendedUsers()) { - Log.d("ReVanced", "Handle recommended users: $this") + Log.d("revanced", "Handle recommended users: $this") jsonRemoveRecommendedUsers() } } @@ -35,7 +35,7 @@ internal object TwiFucker { private fun JSONObject.jsonCheckAndRemoveThreads() { if (jsonHasThreads()) { - Log.d("ReVanced", "Handle threads: $this") + Log.d("revabced", "Handle threads: $this") jsonRemoveThreads() } } @@ -92,7 +92,7 @@ internal object TwiFucker { val trendRemoveIndex = mutableListOf() forEachIndexed { trendIndex, trend -> if (trend.trendHasPromotedMetadata()) { - Log.d("ReVanced", "Handle trends ads $trendIndex $trend") + Log.d("revanced", "Handle trends ads $trendIndex $trend") trendRemoveIndex.add(trendIndex) } } @@ -119,7 +119,7 @@ internal object TwiFucker { entry.entryGetTrends()?.trendRemoveAds() if (entry.entryHasPromotedMetadata()) { - Log.d("ReVanced", "Handle timeline ads $entryIndex $entry") + Log.d("revanced", "Handle timeline ads $entryIndex $entry") removeIndex.add(entryIndex) } @@ -127,7 +127,7 @@ internal object TwiFucker { val contentItems = entry.entryGetContentItems() contentItems?.forEachIndexed inner@{ itemIndex, item -> if (item.entryHasPromotedMetadata()) { - Log.d("ReVanced", "Handle timeline replies ads $entryIndex $entry") + Log.d("revanced", "Handle timeline replies ads $entryIndex $entry") if (contentItems.length() == 1) { removeIndex.add(entryIndex) } else { @@ -150,7 +150,7 @@ internal object TwiFucker { forEachIndexed { entryIndex, entry -> if (entry.entryIsTweetDetailRelatedTweets()) { - Log.d("ReVanced", "Handle tweet detail related tweets $entryIndex $entry") + Log.d("revanced", "Handle tweet detail related tweets $entryIndex $entry") removeIndex.add(entryIndex) } } @@ -180,7 +180,7 @@ internal object TwiFucker { forEachIndexed { entryIndex, entry -> if (!entry.entryIsWhoToFollow()) return@forEachIndexed - Log.d("ReVanced", "Handle whoToFollow $entryIndex $entry") + Log.d("revanced", "Handle whoToFollow $entryIndex $entry") entryRemoveIndex.add(entryIndex) val items = entry.entryGetContentItems() @@ -188,7 +188,7 @@ internal object TwiFucker { items?.forEachIndexed { index, item -> item.itemContainsPromotedUser().let { if (it) { - Log.d("ReVanced", "Handle whoToFollow promoted user $index $item") + Log.d("revanced", "Handle whoToFollow promoted user $index $item") userRemoveIndex.add(index) } } diff --git a/build.gradle.kts b/build.gradle.kts index 7f2b9a0f..be6fa2c9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:8.0.2") + classpath("com.android.tools.build:gradle:7.4.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") } } diff --git a/gradle.properties b/gradle.properties index 991d90a6..e53b273d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true android.useAndroidX = true -version = 0.116.0 +version = 0.116.1-dev.1