diff --git a/app/src/main/java/app/revanced/integrations/shared/Utils.java b/app/src/main/java/app/revanced/integrations/shared/Utils.java index f440b25394..cc53abf5d3 100644 --- a/app/src/main/java/app/revanced/integrations/shared/Utils.java +++ b/app/src/main/java/app/revanced/integrations/shared/Utils.java @@ -89,6 +89,7 @@ public static String getAppVersionName() { return versionName; } + /** * Hide a view by setting its layout height and width to 1dp. * @@ -96,11 +97,24 @@ public static String getAppVersionName() { * @param view The view to hide. */ public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View view) { - if (!condition.get()) return; + if (hideViewBy0dpUnderCondition(condition.get(), view)) { + Logger.printDebug(() -> "View hidden by setting: " + condition); + } + } - Logger.printDebug(() -> "Hiding view with setting: " + condition); + /** + * Hide a view by setting its layout height and width to 1dp. + * + * @param condition The setting to check for hiding the view. + * @param view The view to hide. + */ + public static boolean hideViewBy0dpUnderCondition(boolean condition, View view) { + if (condition) { + hideViewByLayoutParams(view); + return true; + } - hideViewByLayoutParams(view); + return false; } /** @@ -110,20 +124,42 @@ public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View vi * @param view The view to hide. */ public static void hideViewUnderCondition(BooleanSetting condition, View view) { - if (!condition.get()) return; + if (hideViewUnderCondition(condition.get(), view)) { + Logger.printDebug(() -> "View hidden by setting: " + condition); + } + } - Logger.printDebug(() -> "Hiding view with setting: " + condition); + /** + * Hide a view by setting its visibility to GONE. + * + * @param condition The setting to check for hiding the view. + * @param view The view to hide. + */ + public static boolean hideViewUnderCondition(boolean condition, View view) { + if (condition) { + view.setVisibility(View.GONE); + return true; + } + + return false; + } - view.setVisibility(View.GONE); + public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting condition, View view) { + if (hideViewByRemovingFromParentUnderCondition(condition.get(), view)) { + Logger.printDebug(() -> "View hidden by setting: " + condition); + } } - public static void removeViewFromParentUnderConditions(BooleanSetting setting, View view) { - if (setting.get()) { + public static boolean hideViewByRemovingFromParentUnderCondition(boolean setting, View view) { + if (setting) { ViewParent parent = view.getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(view); + return true; } } + + return false; } /** @@ -236,6 +272,8 @@ public static T getChildView(@NonNull ViewGroup viewGroup, bool @NonNull MatchFilter filter) { for (int i = 0, childCount = viewGroup.getChildCount(); i < childCount; i++) { View childAt = viewGroup.getChildAt(i); + Logger.printDebug(() -> "View id: " + childAt.getId() + " tag: " + childAt.getTag()); + if (filter.matches(childAt)) { //noinspection unchecked return (T) childAt; diff --git a/app/src/main/java/app/revanced/integrations/shared/settings/EnumSetting.java b/app/src/main/java/app/revanced/integrations/shared/settings/EnumSetting.java index a6301def24..fd8edaf4fe 100644 --- a/app/src/main/java/app/revanced/integrations/shared/settings/EnumSetting.java +++ b/app/src/main/java/app/revanced/integrations/shared/settings/EnumSetting.java @@ -2,13 +2,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import app.revanced.integrations.shared.Logger; + import org.json.JSONException; import org.json.JSONObject; import java.util.Locale; import java.util.Objects; +import app.revanced.integrations.shared.Logger; + /** * If an Enum value is removed or changed, any saved or imported data using the * non-existent value will be reverted to the default value @@ -98,4 +100,18 @@ public void save(@NonNull T newValue) { public T get() { return value; } + + /** + * Availability based on if this setting is currently set to any of the provided types. + */ + @SafeVarargs + public final Setting.Availability availability(@NonNull T... types) { + return () -> { + T currentEnumType = get(); + for (T enumType : types) { + if (currentEnumType == enumType) return true; + } + return false; + }; + } } diff --git a/app/src/main/java/app/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch.java b/app/src/main/java/app/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch.java index 264bd89310..d0f110bb06 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch.java @@ -6,7 +6,7 @@ @SuppressWarnings("unused") public class SpoofSimPatch { - private static final Boolean ENABLED = Settings.SIM_SPOOF.get(); + private static final boolean ENABLED = Settings.SIM_SPOOF.get(); public static String getCountryIso(String value) { if (ENABLED) { diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/CustomPlayerOverlayOpacityPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/CustomPlayerOverlayOpacityPatch.java index 26bedbb0d6..2180385782 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/CustomPlayerOverlayOpacityPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/CustomPlayerOverlayOpacityPatch.java @@ -1,22 +1,33 @@ package app.revanced.integrations.youtube.patches; +import static app.revanced.integrations.shared.StringRef.str; + import android.widget.ImageView; -import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Utils; +import app.revanced.integrations.youtube.settings.Settings; @SuppressWarnings("unused") public class CustomPlayerOverlayOpacityPatch { - public static void changeOpacity(ImageView imageView) { + private static final int PLAYER_OVERLAY_OPACITY_LEVEL; + + static { int opacity = Settings.PLAYER_OVERLAY_OPACITY.get(); if (opacity < 0 || opacity > 100) { - Utils.showToastLong("Player overlay opacity must be between 0-100"); + Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast")); Settings.PLAYER_OVERLAY_OPACITY.resetToDefault(); opacity = Settings.PLAYER_OVERLAY_OPACITY.defaultValue; } - imageView.setImageAlpha((opacity * 255) / 100); + PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100; + } + + /** + * Injection point. + */ + public static void changeOpacity(ImageView imageView) { + imageView.setImageAlpha(PLAYER_OVERLAY_OPACITY_LEVEL); } } diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/EnableTabletLayoutPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/EnableTabletLayoutPatch.java deleted file mode 100644 index c7b5364bc5..0000000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/EnableTabletLayoutPatch.java +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.integrations.youtube.patches; - -import app.revanced.integrations.youtube.settings.Settings; - -@SuppressWarnings("unused") -public final class EnableTabletLayoutPatch { - public static boolean enableTabletLayout() { - return Settings.TABLET_LAYOUT.get(); - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/HideAutoplayButtonPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/HideAutoplayButtonPatch.java index 846905c238..d4619857d9 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/HideAutoplayButtonPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/HideAutoplayButtonPatch.java @@ -5,7 +5,7 @@ @SuppressWarnings("unused") public class HideAutoplayButtonPatch { - private static final Boolean HIDE_AUTOPLAY_BUTTON_ENABLED = Settings.HIDE_AUTOPLAY_BUTTON.get(); + private static final boolean HIDE_AUTOPLAY_BUTTON_ENABLED = Settings.HIDE_AUTOPLAY_BUTTON.get(); /** * Injection point. diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/MiniplayerPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/MiniplayerPatch.java new file mode 100644 index 0000000000..22ea9f1017 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/MiniplayerPatch.java @@ -0,0 +1,166 @@ +package app.revanced.integrations.youtube.patches; + +import static app.revanced.integrations.shared.StringRef.str; +import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.*; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import app.revanced.integrations.shared.Logger; +import app.revanced.integrations.shared.Utils; +import app.revanced.integrations.youtube.settings.Settings; + +@SuppressWarnings("unused") +public final class MiniplayerPatch { + + /** + * Mini player type. Null fields indicates to use the original un-patched value. + */ + public enum MiniplayerType { + /** Unmodified type, and same as un-patched. */ + ORIGINAL(null, null), + PHONE(false, null), + TABLET(true, null), + MODERN_1(null, 1), + MODERN_2(null, 2), + MODERN_3(null, 3); + + /** + * Legacy tablet hook value. + */ + @Nullable + final Boolean legacyTabletOverride; + + /** + * Modern player type used by YT. + */ + @Nullable + final Integer modernPlayerType; + + MiniplayerType(@Nullable Boolean legacyTabletOverride, @Nullable Integer modernPlayerType) { + this.legacyTabletOverride = legacyTabletOverride; + this.modernPlayerType = modernPlayerType; + } + + public boolean isModern() { + return modernPlayerType != null; + } + } + + /** + * Modern subtitle overlay for {@link MiniplayerType#MODERN_2}. + * Resource is not present in older targets, and this field will be zero. + */ + private static final int MODERN_OVERLAY_SUBTITLE_TEXT + = Utils.getResourceIdentifier("modern_miniplayer_subtitle_text", "id"); + + private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get(); + + private static final boolean HIDE_EXPAND_CLOSE_ENABLED = + (CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.get(); + + private static final boolean HIDE_SUBTEXT_ENABLED = + (CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get(); + + private static final boolean HIDE_REWIND_FORWARD_ENABLED = + CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get(); + + private static final int OPACITY_LEVEL; + + static { + int opacity = Settings.MINIPLAYER_OPACITY.get(); + + if (opacity < 0 || opacity > 100) { + Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast")); + Settings.MINIPLAYER_OPACITY.resetToDefault(); + opacity = Settings.MINIPLAYER_OPACITY.defaultValue; + } + + OPACITY_LEVEL = (opacity * 255) / 100; + } + + /** + * Injection point. + */ + public static boolean getLegacyTabletMiniplayerOverride(boolean original) { + Boolean isTablet = CURRENT_TYPE.legacyTabletOverride; + return isTablet == null + ? original + : isTablet; + } + + /** + * Injection point. + */ + public static boolean getModernMiniplayerOverride(boolean original) { + return CURRENT_TYPE == ORIGINAL + ? original + : CURRENT_TYPE.isModern(); + } + + /** + * Injection point. + */ + public static int getModernMiniplayerOverrideType(int original) { + Integer modernValue = CURRENT_TYPE.modernPlayerType; + return modernValue == null + ? original + : modernValue; + } + + /** + * Injection point. + */ + public static void adjustMiniplayerOpacity(ImageView view) { + if (CURRENT_TYPE == MODERN_1) { + view.setImageAlpha(OPACITY_LEVEL); + } + } + + /** + * Injection point. + */ + public static void hideMiniplayerExpandClose(ImageView view) { + Utils.hideViewByRemovingFromParentUnderCondition(HIDE_EXPAND_CLOSE_ENABLED, view); + } + + /** + * Injection point. + */ + public static void hideMiniplayerRewindForward(ImageView view) { + Utils.hideViewByRemovingFromParentUnderCondition(HIDE_REWIND_FORWARD_ENABLED, view); + } + + /** + * Injection point. + */ + public static void hideMiniplayerSubTexts(View view) { + // Different subviews are passed in, but only TextView and layouts are of interest here. + final boolean hideView = HIDE_SUBTEXT_ENABLED && (view instanceof TextView || view instanceof LinearLayout); + Utils.hideViewByRemovingFromParentUnderCondition(hideView, view); + } + + /** + * Injection point. + */ + public static void playerOverlayGroupCreated(View group) { + // Modern 2 has an half broken subtitle that is always present. + // Always hide it to make the miniplayer mostly usable. + if (CURRENT_TYPE == MODERN_2 && MODERN_OVERLAY_SUBTITLE_TEXT != 0) { + if (group instanceof ViewGroup) { + View subtitleText = Utils.getChildView((ViewGroup) group, true, + view -> view.getId() == MODERN_OVERLAY_SUBTITLE_TEXT); + + if (subtitleText != null) { + subtitleText.setVisibility(View.GONE); + Logger.printDebug(() -> "Modern overlay subtitle view set to hidden"); + } + } + } + } +} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/NavigationButtonsPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/NavigationButtonsPatch.java index 33e6bc3abb..bdbeb893d2 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/NavigationButtonsPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/NavigationButtonsPatch.java @@ -23,7 +23,7 @@ public final class NavigationButtonsPatch { } }; - private static final Boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON + private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get(); /** diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/TabletLayoutPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/TabletLayoutPatch.java new file mode 100644 index 0000000000..baea21a67c --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/TabletLayoutPatch.java @@ -0,0 +1,16 @@ +package app.revanced.integrations.youtube.patches; + +import app.revanced.integrations.youtube.settings.Settings; + +@SuppressWarnings("unused") +public final class TabletLayoutPatch { + + private static final boolean TABLET_LAYOUT_ENABLED = Settings.TABLET_LAYOUT.get(); + + /** + * Injection point. + */ + public static boolean getTabletLayoutEnabled() { + return TABLET_LAYOUT_ENABLED; + } +} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/TabletMiniPlayerOverridePatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/TabletMiniPlayerOverridePatch.java deleted file mode 100644 index 1a0515ae97..0000000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/TabletMiniPlayerOverridePatch.java +++ /dev/null @@ -1,13 +0,0 @@ -package app.revanced.integrations.youtube.patches; - -import app.revanced.integrations.youtube.settings.Settings; - -@SuppressWarnings("unused") -public class TabletMiniPlayerOverridePatch { - - public static boolean getTabletMiniPlayerOverride(boolean original) { - if (Settings.USE_TABLET_MINIPLAYER.get()) - return true; - return original; - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/components/ShortsFilter.java b/app/src/main/java/app/revanced/integrations/youtube/patches/components/ShortsFilter.java index a4d6178823..8ba6c46b3c 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/components/ShortsFilter.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/components/ShortsFilter.java @@ -1,7 +1,6 @@ package app.revanced.integrations.youtube.patches.components; import static app.revanced.integrations.shared.Utils.hideViewUnderCondition; -import static app.revanced.integrations.shared.Utils.removeViewFromParentUnderConditions; import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton; import android.view.View; @@ -326,11 +325,11 @@ public static void hideLikeButton(final View likeButtonView) { // the button was (only relevant for dislikes button). // // Instead remove the view from the parent. - removeViewFromParentUnderConditions(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView); + Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView); } public static void hideDislikeButton(final View dislikeButtonView) { - removeViewFromParentUnderConditions(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView); + Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView); } public static void hideShortsCommentsButton(final View commentsButtonView) { diff --git a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java index dd5eec20cb..c64b6820d0 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java +++ b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java @@ -1,5 +1,17 @@ package app.revanced.integrations.youtube.settings; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static app.revanced.integrations.shared.settings.Setting.*; +import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType; +import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_1; +import static app.revanced.integrations.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3; +import static app.revanced.integrations.youtube.sponsorblock.objects.CategoryBehaviour.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import app.revanced.integrations.shared.Logger; import app.revanced.integrations.shared.settings.*; import app.revanced.integrations.shared.settings.preference.SharedPrefCategory; @@ -10,15 +22,7 @@ import app.revanced.integrations.youtube.patches.spoof.SpoofAppVersionPatch; import app.revanced.integrations.youtube.sponsorblock.SponsorBlockSettings; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import static app.revanced.integrations.shared.settings.Setting.*; -import static app.revanced.integrations.youtube.sponsorblock.objects.CategoryBehaviour.*; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; - +@SuppressWarnings("deprecation") public class Settings extends BaseSettings { // Video public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE); @@ -129,6 +133,13 @@ public class Settings extends BaseSettings { public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE); public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE); + // Miniplayer + public static final EnumSetting MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true); + public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3)); + public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3)); + public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1)); + public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1)); + // External downloader public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE); public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE); @@ -175,7 +186,6 @@ public class Settings extends BaseSettings { public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message"); public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "17.33.42", true, parent(SPOOF_APP_VERSION)); public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message"); - public static final BooleanSetting USE_TABLET_MINIPLAYER = new BooleanSetting("revanced_tablet_miniplayer", FALSE, true); public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true); public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE, "revanced_remove_viewer_discretion_dialog_user_dialog_message");