diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java index 3d4f1d888..57d7caec8 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java @@ -26,7 +26,6 @@ public class CreateSegmentButton { controlsView, "revanced_sb_create_segment_button", null, - null, CreateSegmentButton::shouldBeShown, v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(), null diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java index e8f793d05..32af03272 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java @@ -28,7 +28,6 @@ public class VotingButton { controlsView, "revanced_sb_voting_button", null, - null, VotingButton::shouldBeShown, v -> SponsorBlockUtils.onVotingClicked(v.getContext()), null diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java index 9b9d46cce..b0dc22e76 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java @@ -21,7 +21,6 @@ public class CopyVideoUrlButton { instance = new PlayerControlButton( controlsView, "revanced_copy_video_url_button", - "revanced_copy_video_url_button_placeholder", null, Settings.COPY_VIDEO_URL::get, view -> CopyVideoUrlPatch.copyUrl(false), diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java index 55ec75543..c702756ed 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java @@ -21,7 +21,6 @@ public class CopyVideoUrlTimestampButton { instance = new PlayerControlButton( controlsView, "revanced_copy_video_url_timestamp_button", - "revanced_copy_video_url_timestamp_button_placeholder", null, Settings.COPY_VIDEO_URL_TIMESTAMP::get, view -> CopyVideoUrlPatch.copyUrl(true), diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java index af158ca9e..12e465ec2 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java @@ -22,7 +22,6 @@ public class ExternalDownloadButton { instance = new PlayerControlButton( controlsView, "revanced_external_download_button", - "revanced_external_download_button_placeholder", null, Settings.EXTERNAL_DOWNLOADER::get, ExternalDownloadButton::onDownloadClick, diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java index 57cb5912b..d7c4fc346 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java @@ -33,7 +33,6 @@ public class PlaybackSpeedDialogButton { controlsView, "revanced_playback_speed_dialog_button_container", "revanced_playback_speed_dialog_button", - "revanced_playback_speed_dialog_button_placeholder", "revanced_playback_speed_dialog_button_text", Settings.PLAYBACK_SPEED_DIALOG_BUTTON::get, view -> { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java index 64551d327..c9d61a8d3 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java @@ -1,7 +1,7 @@ package app.revanced.extension.youtube.videoplayer; import android.view.View; -import android.view.animation.Animation; +import android.view.ViewPropertyAnimator; import android.widget.ImageView; import android.widget.TextView; @@ -24,53 +24,29 @@ public class PlayerControlButton { boolean buttonEnabled(); } - private static final int fadeInDuration; - private static final int fadeOutDuration; - - private static final Animation fadeInAnimation; - private static final Animation fadeOutAnimation; - private static final Animation fadeOutImmediate; - - static { - fadeInDuration = Utils.getResourceInteger("fade_duration_fast"); - fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled"); - - fadeInAnimation = Utils.getResourceAnimation("fade_in"); - fadeInAnimation.setDuration(fadeInDuration); - - fadeOutAnimation = Utils.getResourceAnimation("fade_out"); - fadeOutAnimation.setDuration(fadeOutDuration); - - fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out"); - fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast")); - } + private static final int fadeInDuration = Utils.getResourceInteger("fade_duration_fast"); + private static final int fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled"); private final WeakReference containerRef; private final WeakReference buttonRef; - /** - * Empty view with the same layout size as the button. Used to fill empty space while the - * fade out animation runs. Without this the chapter titles overlapping the button when fading out. - */ - private final WeakReference placeHolderRef; private final WeakReference textOverlayRef; private final PlayerControlButtonStatus enabledStatus; private boolean isVisible; + private long lastTimeSetVisible; public PlayerControlButton(View controlsViewGroup, String buttonId, - @Nullable String placeholderId, @Nullable String textOverlayId, PlayerControlButtonStatus enabledStatus, View.OnClickListener onClickListener, @Nullable View.OnLongClickListener longClickListener) { - this(controlsViewGroup, buttonId, buttonId, placeholderId, textOverlayId, + this(controlsViewGroup, buttonId, buttonId, textOverlayId, enabledStatus, onClickListener, longClickListener); } public PlayerControlButton(View controlsViewGroup, String viewToHide, String buttonId, - @Nullable String placeholderId, @Nullable String textOverlayId, PlayerControlButtonStatus enabledStatus, View.OnClickListener onClickListener, @@ -86,13 +62,6 @@ public class PlayerControlButton { } buttonRef = new WeakReference<>(button); - View tempPlaceholder = null; - if (placeholderId != null) { - tempPlaceholder = Utils.getChildViewByResourceName(controlsViewGroup, placeholderId); - tempPlaceholder.setVisibility(View.GONE); - } - placeHolderRef = new WeakReference<>(tempPlaceholder); - TextView tempTextOverlay = null; if (textOverlayId != null) { tempTextOverlay = Utils.getChildViewByResourceName(controlsViewGroup, textOverlayId); @@ -114,12 +83,117 @@ public class PlayerControlButton { } public void setVisibilityNegatedImmediate() { - if (PlayerControlsVisibility.getCurrent() != PlayerControlsVisibility.PLAYER_CONTROLS_VISIBILITY_HIDDEN) { - return; - } + try { + Utils.verifyOnMainThread(); + if (PlayerControlsVisibility.getCurrent() != PlayerControlsVisibility.PLAYER_CONTROLS_VISIBILITY_HIDDEN) { + return; + } - final boolean buttonEnabled = enabledStatus.buttonEnabled(); - if (!buttonEnabled) { + final boolean buttonEnabled = enabledStatus.buttonEnabled(); + if (!buttonEnabled) { + return; + } + + View container = containerRef.get(); + if (container == null) { + return; + } + + isVisible = false; + + ViewPropertyAnimator animate = container.animate(); + animate.cancel(); + + // If the overlay is tapped to display then immediately tapped to dismiss + // before the fade in animation finishes, then the fade out animation is + // the time between when the fade in started and now. + final long animationDuration = Math.min(fadeInDuration, + System.currentTimeMillis() - lastTimeSetVisible); + if (animationDuration <= 0) { + // Should never happen, but handle just in case. + container.setVisibility(View.GONE); + return; + } + + animate.alpha(0) + .setDuration(animationDuration) + .withEndAction(() -> container.setVisibility(View.GONE)) + .start(); + } catch (Exception ex) { + Logger.printException(() -> "setVisibilityNegatedImmediate failure", ex); + } + } + + 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(() -> privateSetVisibility(true, false)); + } else { + privateSetVisibility(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; + + privateSetVisibility(visible, animated); + } + + private void privateSetVisibility(boolean visible, boolean animated) { + try { + Utils.verifyOnMainThread(); + + if (isVisible == visible) return; + isVisible = visible; + + if (visible) { + lastTimeSetVisible = System.currentTimeMillis(); + } + + View container = containerRef.get(); + if (container == null) { + return; + } + + if (visible && enabledStatus.buttonEnabled()) { + ViewPropertyAnimator animate = container.animate(); + animate.cancel(); + container.setVisibility(View.VISIBLE); + + if (animated) { + container.setAlpha(0); + animate.alpha(1) + .setDuration(fadeInDuration) + .start(); + } else { + container.setAlpha(1); + } + } else if (container.getVisibility() == View.VISIBLE) { + ViewPropertyAnimator animate = container.animate(); + animate.cancel(); + + if (animated) { + animate.alpha(0) + .setDuration(fadeOutDuration) + .withEndAction(() -> container.setVisibility(View.GONE)) + .start(); + } else { + container.setVisibility(View.GONE); + } + } + } catch (Exception ex) { + Logger.printException(() -> "privateSetVisibility failure", ex); + } + } + + /** + * Synchronizes the button state after the player state changes. + */ + private void playerTypeChanged(PlayerType newType) { + Utils.verifyOnMainThread(); + if (newType != PlayerType.WATCH_WHILE_MINIMIZED && !newType.isMaximizedOrFullscreen()) { return; } @@ -128,116 +202,26 @@ public class PlayerControlButton { return; } - isVisible = false; + container.animate().cancel(); - container.clearAnimation(); - container.startAnimation(fadeOutImmediate); - container.setVisibility(View.GONE); - - View placeholder = placeHolderRef.get(); - if (placeholder != null) { + if (isVisible && enabledStatus.buttonEnabled()) { container.setVisibility(View.VISIBLE); - } - } - - 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; - - View container = containerRef.get(); - if (container == null) return; - - View placeholder = placeHolderRef.get(); - final boolean buttonEnabled = enabledStatus.buttonEnabled(); - - if (visible && buttonEnabled) { - container.clearAnimation(); - if (animated) { - container.startAnimation(fadeInAnimation); - } - container.setVisibility(View.VISIBLE); - - if (placeholder != null) { - placeholder.setVisibility(View.GONE); - } - } else { - if (container.getVisibility() == View.VISIBLE) { - container.clearAnimation(); - if (animated) { - container.startAnimation(fadeOutAnimation); - } - container.setVisibility(View.GONE); - } - - if (placeholder != null) { - placeholder.setVisibility(buttonEnabled - ? View.VISIBLE - : View.GONE); - } - } - } catch (Exception ex) { - Logger.printException(() -> "private_setVisibility failure", ex); - } - } - - /** - * Synchronizes the button state after the player state changes. - */ - private void playerTypeChanged(PlayerType newType) { - if (newType != PlayerType.WATCH_WHILE_MINIMIZED && !newType.isMaximizedOrFullscreen()) { - return; - } - - View container = containerRef.get(); - if (container == null) return; - - container.clearAnimation(); - View placeholder = placeHolderRef.get(); - - if (enabledStatus.buttonEnabled()) { - if (isVisible) { - container.setVisibility(View.VISIBLE); - if (placeholder != null) placeholder.setVisibility(View.GONE); - } else { - container.setVisibility(View.GONE); - if (placeholder != null) placeholder.setVisibility(View.VISIBLE); - } + container.setAlpha(1); } else { container.setVisibility(View.GONE); - if (placeholder != null) placeholder.setVisibility(View.GONE); } } public void hide() { Utils.verifyOnMainThread(); - if (!isVisible) return; + if (!isVisible) { + return; + } + isVisible = false; View view = containerRef.get(); if (view == null) return; view.setVisibility(View.GONE); - - View placeHolder = placeHolderRef.get(); - if (placeHolder != null) view.setVisibility(View.GONE); - - isVisible = false; } /** @@ -245,6 +229,8 @@ public class PlayerControlButton { * @param resourceId Drawable identifier, or zero to hide the icon. */ public void setIcon(int resourceId) { + Utils.verifyOnMainThread(); + View button = buttonRef.get(); if (button instanceof ImageView imageButton) { imageButton.setImageResource(resourceId); @@ -256,6 +242,8 @@ public class PlayerControlButton { * @param text The text to set on the overlay, or null to clear the text. */ public void setTextOverlay(CharSequence text) { + Utils.verifyOnMainThread(); + TextView textOverlay = textOverlayRef.get(); if (textOverlay != null) { textOverlay.setText(text); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java index 109eaadd2..0c878616c 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java @@ -69,7 +69,6 @@ public class VideoQualityDialogButton { controlsView, "revanced_video_quality_dialog_button_container", "revanced_video_quality_dialog_button", - "revanced_video_quality_dialog_button_placeholder", "revanced_video_quality_dialog_button_text", Settings.VIDEO_QUALITY_DIALOG_BUTTON::get, view -> { diff --git a/patches/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml index 34093f120..1dd0e3dc1 100644 --- a/patches/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml +++ b/patches/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml @@ -18,14 +18,6 @@ yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" /> - - - - diff --git a/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml index eb9c2d779..a243eea71 100644 --- a/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml +++ b/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml @@ -18,12 +18,4 @@ android:src="@drawable/revanced_yt_download_button" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" /> - - diff --git a/patches/src/main/resources/qualitybutton/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/qualitybutton/host/layout/youtube_controls_bottom_ui_container.xml index 6a8772d8a..ab5da7e36 100644 --- a/patches/src/main/resources/qualitybutton/host/layout/youtube_controls_bottom_ui_container.xml +++ b/patches/src/main/resources/qualitybutton/host/layout/youtube_controls_bottom_ui_container.xml @@ -33,11 +33,5 @@ android:paddingTop="5.5dp" android:textColor="@android:color/white" android:textSize="10dp" /> - - diff --git a/patches/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml index c7532ecd6..2541113c5 100644 --- a/patches/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml +++ b/patches/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml @@ -33,11 +33,5 @@ android:paddingTop="5.5dp" android:textColor="@android:color/white" android:textSize="10dp" /> - -