393 lines
14 KiB
Java
393 lines
14 KiB
Java
package app.revanced.integrations.youtube.patches.components;
|
|
|
|
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
|
|
|
import android.os.Build;
|
|
import android.view.View;
|
|
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RequiresApi;
|
|
|
|
import app.revanced.integrations.shared.Logger;
|
|
import app.revanced.integrations.shared.Utils;
|
|
import app.revanced.integrations.youtube.StringTrieSearch;
|
|
import app.revanced.integrations.youtube.settings.Settings;
|
|
import app.revanced.integrations.youtube.shared.NavigationBar;
|
|
import app.revanced.integrations.youtube.shared.PlayerType;
|
|
|
|
@SuppressWarnings("unused")
|
|
public final class LayoutComponentsFilter extends Filter {
|
|
private final StringTrieSearch exceptions = new StringTrieSearch();
|
|
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch();
|
|
private static final ByteArrayFilterGroup mixPlaylistsExceptions2 = new ByteArrayFilterGroup(
|
|
null,
|
|
"cell_description_body"
|
|
);
|
|
|
|
private static final ByteArrayFilterGroup mixPlaylists = new ByteArrayFilterGroup(
|
|
Settings.HIDE_MIX_PLAYLISTS,
|
|
"&list="
|
|
);
|
|
private final StringFilterGroup searchResultShelfHeader;
|
|
private final StringFilterGroup inFeedSurvey;
|
|
private final StringFilterGroup notifyMe;
|
|
private final StringFilterGroup expandableMetadata;
|
|
private final ByteArrayFilterGroup searchResultRecommendations;
|
|
private final StringFilterGroup searchResultVideo;
|
|
private final StringFilterGroup compactChannelBarInner;
|
|
private final StringFilterGroup compactChannelBarInnerButton;
|
|
private final ByteArrayFilterGroup joinMembershipButton;
|
|
private final StringFilterGroup horizontalShelves;
|
|
|
|
static {
|
|
mixPlaylistsExceptions.addPatterns(
|
|
"V.ED", // Playlist browse id.
|
|
"java.lang.ref.WeakReference"
|
|
);
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
public LayoutComponentsFilter() {
|
|
exceptions.addPatterns(
|
|
"home_video_with_context",
|
|
"related_video_with_context",
|
|
"search_video_with_context",
|
|
"comment_thread", // Whitelist comments
|
|
"|comment.", // Whitelist comment replies
|
|
"library_recent_shelf"
|
|
);
|
|
|
|
// Identifiers.
|
|
|
|
final var graySeparator = new StringFilterGroup(
|
|
Settings.HIDE_GRAY_SEPARATOR,
|
|
"cell_divider" // layout residue (gray line above the buttoned ad),
|
|
);
|
|
|
|
final var chipsShelf = new StringFilterGroup(
|
|
Settings.HIDE_CHIPS_SHELF,
|
|
"chips_shelf"
|
|
);
|
|
|
|
addIdentifierCallbacks(
|
|
graySeparator,
|
|
chipsShelf
|
|
);
|
|
|
|
// Paths.
|
|
|
|
final var communityPosts = new StringFilterGroup(
|
|
Settings.HIDE_COMMUNITY_POSTS,
|
|
"post_base_wrapper",
|
|
"image_post_root.eml"
|
|
);
|
|
|
|
final var communityGuidelines = new StringFilterGroup(
|
|
Settings.HIDE_COMMUNITY_GUIDELINES,
|
|
"community_guidelines"
|
|
);
|
|
|
|
final var subscribersCommunityGuidelines = new StringFilterGroup(
|
|
Settings.HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES,
|
|
"sponsorships_comments_upsell"
|
|
);
|
|
|
|
final var channelMemberShelf = new StringFilterGroup(
|
|
Settings.HIDE_CHANNEL_MEMBER_SHELF,
|
|
"member_recognition_shelf"
|
|
);
|
|
|
|
final var compactBanner = new StringFilterGroup(
|
|
Settings.HIDE_COMPACT_BANNER,
|
|
"compact_banner"
|
|
);
|
|
|
|
inFeedSurvey = new StringFilterGroup(
|
|
Settings.HIDE_FEED_SURVEY,
|
|
"in_feed_survey",
|
|
"slimline_survey"
|
|
);
|
|
|
|
final var medicalPanel = new StringFilterGroup(
|
|
Settings.HIDE_MEDICAL_PANELS,
|
|
"medical_panel"
|
|
);
|
|
|
|
final var paidPromotion = new StringFilterGroup(
|
|
Settings.HIDE_PAID_PROMOTION_LABEL,
|
|
"paid_content_overlay"
|
|
);
|
|
|
|
final var infoPanel = new StringFilterGroup(
|
|
Settings.HIDE_HIDE_INFO_PANELS,
|
|
"publisher_transparency_panel",
|
|
"single_item_information_panel"
|
|
);
|
|
|
|
final var latestPosts = new StringFilterGroup(
|
|
Settings.HIDE_HIDE_LATEST_POSTS,
|
|
"post_shelf"
|
|
);
|
|
|
|
final var channelGuidelines = new StringFilterGroup(
|
|
Settings.HIDE_HIDE_CHANNEL_GUIDELINES,
|
|
"channel_guidelines_entry_banner"
|
|
);
|
|
|
|
final var emergencyBox = new StringFilterGroup(
|
|
Settings.HIDE_EMERGENCY_BOX,
|
|
"emergency_onebox"
|
|
);
|
|
|
|
// The player audio track button does the exact same function as the audio track flyout menu option.
|
|
// But if the copy url button is shown, these button clashes and the the audio button does not work.
|
|
// Previously this was a setting to show/hide the player button.
|
|
// But it was decided it's simpler to always hide this button because:
|
|
// - it doesn't work with copy video url feature
|
|
// - the button is rare
|
|
// - always hiding makes the ReVanced settings simpler and easier to understand
|
|
// - nobody is going to notice the redundant button is always hidden
|
|
final var audioTrackButton = new StringFilterGroup(
|
|
null,
|
|
"multi_feed_icon_button"
|
|
);
|
|
|
|
final var artistCard = new StringFilterGroup(
|
|
Settings.HIDE_ARTIST_CARDS,
|
|
"official_card"
|
|
);
|
|
|
|
expandableMetadata = new StringFilterGroup(
|
|
Settings.HIDE_EXPANDABLE_CHIP,
|
|
"inline_expander"
|
|
);
|
|
|
|
final var videoQualityMenuFooter = new StringFilterGroup(
|
|
Settings.HIDE_VIDEO_QUALITY_MENU_FOOTER,
|
|
"quality_sheet_footer"
|
|
);
|
|
|
|
final var channelBar = new StringFilterGroup(
|
|
Settings.HIDE_CHANNEL_BAR,
|
|
"channel_bar"
|
|
);
|
|
|
|
final var relatedVideos = new StringFilterGroup(
|
|
Settings.HIDE_RELATED_VIDEOS,
|
|
"fullscreen_related_videos"
|
|
);
|
|
|
|
final var playables = new StringFilterGroup(
|
|
Settings.HIDE_PLAYABLES,
|
|
"horizontal_gaming_shelf.eml"
|
|
);
|
|
|
|
final var quickActions = new StringFilterGroup(
|
|
Settings.HIDE_QUICK_ACTIONS,
|
|
"quick_actions"
|
|
);
|
|
|
|
final var imageShelf = new StringFilterGroup(
|
|
Settings.HIDE_IMAGE_SHELF,
|
|
"image_shelf"
|
|
);
|
|
|
|
|
|
final var timedReactions = new StringFilterGroup(
|
|
Settings.HIDE_TIMED_REACTIONS,
|
|
"emoji_control_panel",
|
|
"timed_reaction"
|
|
);
|
|
|
|
searchResultShelfHeader = new StringFilterGroup(
|
|
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
|
|
"shelf_header.eml"
|
|
);
|
|
|
|
notifyMe = new StringFilterGroup(
|
|
Settings.HIDE_NOTIFY_ME_BUTTON,
|
|
"set_reminder_button"
|
|
);
|
|
|
|
compactChannelBarInner = new StringFilterGroup(
|
|
Settings.HIDE_JOIN_MEMBERSHIP_BUTTON,
|
|
"compact_channel_bar_inner"
|
|
);
|
|
|
|
compactChannelBarInnerButton = new StringFilterGroup(
|
|
null,
|
|
"|button.eml|"
|
|
);
|
|
|
|
joinMembershipButton = new ByteArrayFilterGroup(
|
|
null,
|
|
"sponsorships"
|
|
);
|
|
|
|
final var channelWatermark = new StringFilterGroup(
|
|
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
|
|
"featured_channel_watermark_overlay"
|
|
);
|
|
|
|
final var forYouShelf = new StringFilterGroup(
|
|
Settings.HIDE_FOR_YOU_SHELF,
|
|
"mixed_content_shelf"
|
|
);
|
|
|
|
searchResultVideo = new StringFilterGroup(
|
|
Settings.HIDE_SEARCH_RESULT_RECOMMENDATIONS,
|
|
"search_video_with_context.eml"
|
|
);
|
|
|
|
searchResultRecommendations = new ByteArrayFilterGroup(
|
|
Settings.HIDE_SEARCH_RESULT_RECOMMENDATIONS,
|
|
"endorsement_header_footer"
|
|
);
|
|
|
|
horizontalShelves = new StringFilterGroup(
|
|
Settings.HIDE_HORIZONTAL_SHELVES,
|
|
"horizontal_video_shelf.eml",
|
|
"horizontal_shelf.eml",
|
|
"horizontal_tile_shelf.eml"
|
|
);
|
|
|
|
addPathCallbacks(
|
|
expandableMetadata,
|
|
inFeedSurvey,
|
|
notifyMe,
|
|
channelBar,
|
|
communityPosts,
|
|
paidPromotion,
|
|
searchResultVideo,
|
|
latestPosts,
|
|
channelWatermark,
|
|
communityGuidelines,
|
|
playables,
|
|
quickActions,
|
|
relatedVideos,
|
|
compactBanner,
|
|
compactChannelBarInner,
|
|
medicalPanel,
|
|
videoQualityMenuFooter,
|
|
infoPanel,
|
|
emergencyBox,
|
|
subscribersCommunityGuidelines,
|
|
channelGuidelines,
|
|
audioTrackButton,
|
|
artistCard,
|
|
timedReactions,
|
|
imageShelf,
|
|
channelMemberShelf,
|
|
forYouShelf,
|
|
horizontalShelves
|
|
);
|
|
}
|
|
|
|
@Override
|
|
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
|
if (matchedGroup == searchResultVideo) {
|
|
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// The groups are excluded from the filter due to the exceptions list below.
|
|
// Filter them separately here.
|
|
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
|
{
|
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
|
}
|
|
|
|
if (exceptions.matches(path)) return false; // Exceptions are not filtered.
|
|
|
|
if (matchedGroup == compactChannelBarInner) {
|
|
if (compactChannelBarInnerButton.check(path).isFiltered()) {
|
|
// The filter may be broad, but in the context of a compactChannelBarInnerButton,
|
|
// it's safe to assume that the button is the only thing that should be hidden.
|
|
if (joinMembershipButton.check(protobufBufferArray).isFiltered()) {
|
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// TODO: This also hides the feed Shorts shelf header
|
|
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
|
|
|
|
if (matchedGroup == horizontalShelves) {
|
|
if (contentIndex == 0 && hideShelves()) {
|
|
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
|
}
|
|
|
|
/**
|
|
* Injection point.
|
|
* Called from a different place then the other filters.
|
|
*/
|
|
public static boolean filterMixPlaylists(final Object conversionContext, @Nullable final byte[] bytes) {
|
|
if (bytes == null) {
|
|
Logger.printDebug(() -> "bytes is null");
|
|
return false;
|
|
}
|
|
|
|
// Prevent playlist items being hidden, if a mix playlist is present in it.
|
|
if (mixPlaylistsExceptions.matches(conversionContext.toString()))
|
|
return false;
|
|
|
|
if (!mixPlaylists.check(bytes).isFiltered())
|
|
return false;
|
|
|
|
// Prevent hiding the description of some videos accidentally.
|
|
if (mixPlaylistsExceptions2.check(bytes).isFiltered())
|
|
return false;
|
|
|
|
Logger.printDebug(() -> "Filtered mix playlist");
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Injection point.
|
|
*/
|
|
public static boolean showWatermark() {
|
|
return !Settings.HIDE_VIDEO_CHANNEL_WATERMARK.get();
|
|
}
|
|
|
|
private static final boolean HIDE_SHOW_MORE_BUTTON_ENABLED = Settings.HIDE_SHOW_MORE_BUTTON.get();
|
|
|
|
/**
|
|
* Injection point.
|
|
*/
|
|
public static void hideShowMoreButton(View view) {
|
|
if (HIDE_SHOW_MORE_BUTTON_ENABLED
|
|
&& NavigationBar.isSearchBarActive()
|
|
// Search bar can be active but behind the player.
|
|
&& !PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
|
Utils.hideViewByLayoutParams(view);
|
|
}
|
|
}
|
|
|
|
private static boolean hideShelves() {
|
|
// If the player is opened while library is selected,
|
|
// then filter any recommendations below the player.
|
|
if (PlayerType.getCurrent().isMaximizedOrFullscreen()
|
|
// Or if the search is active while library is selected, then also filter.
|
|
|| NavigationBar.isSearchBarActive()) {
|
|
return true;
|
|
}
|
|
|
|
// Check navigation button last.
|
|
// Only filter if the library tab is not selected.
|
|
// This check is important as the shelf layout is used for the library tab playlists.
|
|
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
|
|
return selectedNavButton != null && !selectedNavButton.isLibraryOrYouTab();
|
|
}
|
|
}
|