diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/PlayerControlsPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/PlayerControlsPatch.java new file mode 100644 index 0000000000..51dcc022c0 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/PlayerControlsPatch.java @@ -0,0 +1,44 @@ +package app.revanced.integrations.youtube.patches; + +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.ImageView; + +import app.revanced.integrations.shared.Logger; + +@SuppressWarnings("unused") +public class PlayerControlsPatch { + /** + * Injection point. + */ + public static void setFullscreenCloseButton(ImageView imageButton) { + // Add a global listener, since the protected method + // View#onVisibilityChanged() does not have any call backs. + imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + int lastVisibility = View.VISIBLE; + + @Override + public void onGlobalLayout() { + try { + final int visibility = imageButton.getVisibility(); + if (lastVisibility != visibility) { + lastVisibility = visibility; + + Logger.printDebug(() -> "fullscreen button visibility: " + + (visibility == View.VISIBLE ? "VISIBLE" : + visibility == View.GONE ? "GONE" : "INVISIBLE")); + + fullscreenButtonVisibilityChanged(visibility == View.VISIBLE); + } + } catch (Exception ex) { + Logger.printDebug(() -> "OnGlobalLayoutListener failure", ex); + } + } + }); + } + + // noinspection EmptyMethod + public static void fullscreenButtonVisibilityChanged(boolean isVisible) { + // Code added during patching. + } +} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/components/LayoutComponentsFilter.java b/app/src/main/java/app/revanced/integrations/youtube/patches/components/LayoutComponentsFilter.java index 050e6ce664..073505c8be 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/components/LayoutComponentsFilter.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/components/LayoutComponentsFilter.java @@ -145,10 +145,8 @@ public LayoutComponentsFilter() { ); // 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 diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java index dffdf9ff48..9b5c7376d8 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java @@ -12,8 +12,9 @@ import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Logger; import app.revanced.integrations.shared.Utils; -import app.revanced.integrations.youtube.videoplayer.BottomControlButton; +import app.revanced.integrations.youtube.videoplayer.PlayerControlButton; +// Edit: This should be a subclass of PlayerControlButton public class CreateSegmentButtonController { private static WeakReference buttonReference = new WeakReference<>(null); private static boolean isShowing; @@ -27,9 +28,7 @@ public static void initialize(View youtubeControlsLayout) { ImageView imageView = Objects.requireNonNull(youtubeControlsLayout.findViewById( getResourceIdentifier("revanced_sb_create_segment_button", "id"))); imageView.setVisibility(View.GONE); - imageView.setOnClickListener(v -> { - SponsorBlockViewController.toggleNewSegmentLayoutVisibility(); - }); + imageView.setOnClickListener(v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility()); buttonReference = new WeakReference<>(imageView); } catch (Exception ex) { @@ -37,25 +36,30 @@ public static void initialize(View youtubeControlsLayout) { } } - public static void changeVisibilityImmediate(boolean visible) { - changeVisibility(visible, true); - } - /** * injection point */ - public static void changeVisibilityNegatedImmediate(boolean visible) { - changeVisibility(!visible, true); + public static void changeVisibilityImmediate(boolean visible) { + if (visible) { + // Fix button flickering, by pushing this call to the back of + // the main thread and letting other layout code run first. + Utils.runOnMainThread(() -> setVisibility(true, false)); + } else { + setVisibility(false, false); + } } /** * injection point */ - public static void changeVisibility(boolean visible) { - changeVisibility(visible, false); + public static void changeVisibility(boolean visible, boolean animated) { + // Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking. + if (visible && !animated) return; + + setVisibility(visible, animated); } - public static void changeVisibility(boolean visible, boolean immediate) { + private static void setVisibility(boolean visible, boolean animated) { try { if (isShowing == visible) return; isShowing = visible; @@ -68,8 +72,8 @@ public static void changeVisibility(boolean visible, boolean immediate) { if (!shouldBeShown()) { return; } - if (!immediate) { - iView.startAnimation(BottomControlButton.getButtonFadeIn()); + if (animated) { + iView.startAnimation(PlayerControlButton.getButtonFadeIn()); } iView.setVisibility(View.VISIBLE); return; @@ -77,8 +81,8 @@ public static void changeVisibility(boolean visible, boolean immediate) { if (iView.getVisibility() == View.VISIBLE) { iView.clearAnimation(); - if (!immediate) { - iView.startAnimation(BottomControlButton.getButtonFadeOut()); + if (animated) { + iView.startAnimation(PlayerControlButton.getButtonFadeOut()); } iView.setVisibility(View.GONE); } diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java index 581ba1ada5..52a4660d85 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java @@ -14,8 +14,9 @@ import app.revanced.integrations.youtube.sponsorblock.SponsorBlockUtils; import app.revanced.integrations.shared.Logger; import app.revanced.integrations.shared.Utils; -import app.revanced.integrations.youtube.videoplayer.BottomControlButton; +import app.revanced.integrations.youtube.videoplayer.PlayerControlButton; +// Edit: This should be a subclass of PlayerControlButton public class VotingButtonController { private static WeakReference buttonReference = new WeakReference<>(null); private static boolean isShowing; @@ -29,35 +30,41 @@ public static void initialize(View youtubeControlsLayout) { ImageView imageView = Objects.requireNonNull(youtubeControlsLayout.findViewById( getResourceIdentifier("revanced_sb_voting_button", "id"))); imageView.setVisibility(View.GONE); - imageView.setOnClickListener(v -> { - SponsorBlockUtils.onVotingClicked(v.getContext()); - }); + imageView.setOnClickListener(v -> SponsorBlockUtils.onVotingClicked(v.getContext())); buttonReference = new WeakReference<>(imageView); } catch (Exception ex) { - Logger.printException(() -> "Unable to set RelativeLayout", ex); + Logger.printException(() -> "initialize failure", ex); } } + /** + * injection point + */ public static void changeVisibilityImmediate(boolean visible) { - changeVisibility(visible, true); + if (visible) { + // Fix button flickering, by pushing this call to the back of + // the main thread and letting other layout code run first. + Utils.runOnMainThread(() -> setVisibility(true, false)); + } else { + setVisibility(false, false); + } } /** * injection point */ - public static void changeVisibilityNegatedImmediate(boolean visible) { - changeVisibility(!visible, true); + public static void changeVisibility(boolean visible, boolean animated) { + // Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking. + if (visible && !animated) return; + + setVisibility(visible, animated); } /** * injection point */ - public static void changeVisibility(boolean visible) { - changeVisibility(visible, false); - } - - public static void changeVisibility(boolean visible, boolean immediate) { + private static void setVisibility(boolean visible, boolean animated) { try { if (isShowing == visible) return; isShowing = visible; @@ -70,8 +77,8 @@ public static void changeVisibility(boolean visible, boolean immediate) { if (!shouldBeShown()) { return; } - if (!immediate) { - iView.startAnimation(BottomControlButton.getButtonFadeIn()); + if (animated) { + iView.startAnimation(PlayerControlButton.getButtonFadeIn()); } iView.setVisibility(View.VISIBLE); return; @@ -79,8 +86,8 @@ public static void changeVisibility(boolean visible, boolean immediate) { if (iView.getVisibility() == View.VISIBLE) { iView.clearAnimation(); - if (!immediate) { - iView.startAnimation(BottomControlButton.getButtonFadeOut()); + if (animated) { + iView.startAnimation(PlayerControlButton.getButtonFadeOut()); } iView.setVisibility(View.GONE); } diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/BottomControlButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/BottomControlButton.java deleted file mode 100644 index aacd93677a..0000000000 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/BottomControlButton.java +++ /dev/null @@ -1,83 +0,0 @@ -package app.revanced.integrations.youtube.videoplayer; - -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.widget.ImageView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.lang.ref.WeakReference; -import java.util.Objects; - -import app.revanced.integrations.shared.Logger; -import app.revanced.integrations.shared.Utils; -import app.revanced.integrations.shared.settings.BooleanSetting; - -public abstract class BottomControlButton { - private static final Animation fadeIn; - private static final Animation fadeOut; - - private final WeakReference buttonRef; - private final BooleanSetting setting; - protected boolean isVisible; - - static { - // TODO: check if these durations are correct. - fadeIn = Utils.getResourceAnimation("fade_in"); - fadeIn.setDuration(Utils.getResourceInteger("fade_duration_fast")); - - fadeOut = Utils.getResourceAnimation("fade_out"); - fadeOut.setDuration(Utils.getResourceInteger("fade_duration_scheduled")); - } - - @NonNull - public static Animation getButtonFadeIn() { - return fadeIn; - } - - @NonNull - public static Animation getButtonFadeOut() { - return fadeOut; - } - - public BottomControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId, - @NonNull BooleanSetting booleanSetting, @NonNull View.OnClickListener onClickListener, - @Nullable View.OnLongClickListener longClickListener) { - Logger.printDebug(() -> "Initializing button: " + imageViewButtonId); - - setting = booleanSetting; - - // Create the button. - ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById( - Utils.getResourceIdentifier(imageViewButtonId, "id") - )); - imageView.setOnClickListener(onClickListener); - if (longClickListener != null) { - imageView.setOnLongClickListener(longClickListener); - } - imageView.setVisibility(View.GONE); - - buttonRef = new WeakReference<>(imageView); - } - - public void setVisibility(boolean visible) { - if (isVisible == visible) return; - isVisible = visible; - - ImageView imageView = buttonRef.get(); - if (imageView == null) { - return; - } - - imageView.clearAnimation(); - if (visible && setting.get()) { - imageView.startAnimation(fadeIn); - imageView.setVisibility(View.VISIBLE); - } else if (imageView.getVisibility() == View.VISIBLE) { - imageView.startAnimation(fadeOut); - imageView.setVisibility(View.GONE); - } - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java index 5995da96b0..e77fd908c0 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java @@ -9,7 +9,8 @@ import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Logger; -public class CopyVideoUrlButton extends BottomControlButton { +@SuppressWarnings("unused") +public class CopyVideoUrlButton extends PlayerControlButton { @Nullable private static CopyVideoUrlButton instance; @@ -38,9 +39,16 @@ public static void initializeButton(View view) { } /** - * Injection point. + * injection point + */ + public static void changeVisibilityImmediate(boolean visible) { + if (instance != null) instance.setVisibilityImmediate(visible); + } + + /** + * injection point */ - public static void changeVisibility(boolean showing) { - if (instance != null) instance.setVisibility(showing); + public static void changeVisibility(boolean visible, boolean animated) { + if (instance != null) instance.setVisibility(visible, animated); } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java index 41af50ac95..3e80d1f24a 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java @@ -9,7 +9,8 @@ import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Logger; -public class CopyVideoUrlTimestampButton extends BottomControlButton { +@SuppressWarnings("unused") +public class CopyVideoUrlTimestampButton extends PlayerControlButton { @Nullable private static CopyVideoUrlTimestampButton instance; @@ -38,10 +39,16 @@ public static void initializeButton(View bottomControlsViewGroup) { } /** - * Injection point. + * injection point */ - public static void changeVisibility(boolean showing) { - if (instance != null) instance.setVisibility(showing); + public static void changeVisibilityImmediate(boolean visible) { + if (instance != null) instance.setVisibilityImmediate(visible); } + /** + * injection point + */ + public static void changeVisibility(boolean visible, boolean animated) { + if (instance != null) instance.setVisibility(visible, animated); + } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/ExternalDownloadButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/ExternalDownloadButton.java index 2a902b025b..1be8423434 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/ExternalDownloadButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/ExternalDownloadButton.java @@ -11,7 +11,7 @@ import app.revanced.integrations.youtube.settings.Settings; @SuppressWarnings("unused") -public class ExternalDownloadButton extends BottomControlButton { +public class ExternalDownloadButton extends PlayerControlButton { @Nullable private static ExternalDownloadButton instance; @@ -37,10 +37,17 @@ public static void initializeButton(View view) { } /** - * Injection point. + * injection point + */ + public static void changeVisibilityImmediate(boolean visible) { + if (instance != null) instance.setVisibilityImmediate(visible); + } + + /** + * injection point */ - public static void changeVisibility(boolean showing) { - if (instance != null) instance.setVisibility(showing); + public static void changeVisibility(boolean visible, boolean animated) { + if (instance != null) instance.setVisibility(visible, animated); } private static void onDownloadClick(View view) { diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton.java index 0acf184a06..eca69e92a8 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton.java @@ -9,7 +9,8 @@ import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Logger; -public class PlaybackSpeedDialogButton extends BottomControlButton { +@SuppressWarnings("unused") +public class PlaybackSpeedDialogButton extends PlayerControlButton { @Nullable private static PlaybackSpeedDialogButton instance; @@ -35,9 +36,16 @@ public static void initializeButton(View view) { } /** - * Injection point. + * injection point + */ + public static void changeVisibilityImmediate(boolean visible) { + if (instance != null) instance.setVisibilityImmediate(visible); + } + + /** + * injection point */ - public static void changeVisibility(boolean showing) { - if (instance != null) instance.setVisibility(showing); + public static void changeVisibility(boolean visible, boolean animated) { + if (instance != null) instance.setVisibility(visible, animated); } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlayerControlButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlayerControlButton.java new file mode 100644 index 0000000000..09684bcc11 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/PlayerControlButton.java @@ -0,0 +1,117 @@ +package app.revanced.integrations.youtube.videoplayer; + +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +import app.revanced.integrations.shared.Logger; +import app.revanced.integrations.shared.Utils; +import app.revanced.integrations.shared.settings.BooleanSetting; + +public abstract class PlayerControlButton { + private static final Animation fadeIn; + private static final Animation fadeOut; + private static final Animation fadeOutImmediate; + + private final WeakReference buttonRef; + protected final BooleanSetting setting; + protected boolean isVisible; + + static { + // TODO: check if these durations are correct. + fadeIn = Utils.getResourceAnimation("fade_in"); + fadeIn.setDuration(Utils.getResourceInteger("fade_duration_fast")); + + fadeOut = Utils.getResourceAnimation("fade_out"); + fadeOut.setDuration(Utils.getResourceInteger("fade_duration_scheduled")); + + fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out"); + fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast")); + } + + @NonNull + public static Animation getButtonFadeIn() { + return fadeIn; + } + + @NonNull + public static Animation getButtonFadeOut() { + return fadeOut; + } + + @NonNull + public static Animation getButtonFadeOutImmediately() { + return fadeOutImmediate; + } + + public PlayerControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId, + @NonNull BooleanSetting booleanSetting, @NonNull View.OnClickListener onClickListener, + @Nullable View.OnLongClickListener longClickListener) { + Logger.printDebug(() -> "Initializing button: " + imageViewButtonId); + + ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById( + Utils.getResourceIdentifier(imageViewButtonId, "id") + )); + imageView.setVisibility(View.GONE); + + imageView.setOnClickListener(onClickListener); + if (longClickListener != null) { + imageView.setOnLongClickListener(longClickListener); + } + + setting = booleanSetting; + buttonRef = new WeakReference<>(imageView); + } + + public void setVisibilityImmediate(boolean visible) { + if (visible) { + // Fix button flickering, by pushing this call to the back of + // the main thread and letting other layout code run first. + Utils.runOnMainThread(() -> private_setVisibility(true, false)); + } else { + private_setVisibility(false, false); + } + } + + public void setVisibility(boolean visible, boolean animated) { + // Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking. + if (visible && !animated) return; + + private_setVisibility(visible, animated); + } + + private void private_setVisibility(boolean visible, boolean animated) { + try { + if (isVisible == visible) return; + isVisible = visible; + + ImageView iView = buttonRef.get(); + if (iView == null) { + return; + } + + if (visible && setting.get()) { + iView.clearAnimation(); + if (animated) { + iView.startAnimation(PlayerControlButton.getButtonFadeIn()); + } + iView.setVisibility(View.VISIBLE); + } else if (iView.getVisibility() == View.VISIBLE) { + iView.clearAnimation(); + if (animated) { + iView.startAnimation(PlayerControlButton.getButtonFadeOut()); + } + iView.setVisibility(View.GONE); + } + } catch (Exception ex) { + Logger.printException(() -> "setVisibility failure", ex); + } + } +}