5151
5252import android .animation .Animator ;
5353import android .animation .AnimatorListenerAdapter ;
54- import android .animation .ObjectAnimator ;
55- import android .animation .PropertyValuesHolder ;
56- import android .animation .ValueAnimator ;
5754import android .annotation .SuppressLint ;
5855import android .content .BroadcastReceiver ;
5956import android .content .Context ;
154151import org .schabi .newpipe .ktx .AnimationType ;
155152import org .schabi .newpipe .local .history .HistoryRecordManager ;
156153import org .schabi .newpipe .player .MainPlayer .PlayerType ;
154+ import org .schabi .newpipe .player .event .DisplayPortion ;
157155import org .schabi .newpipe .player .event .PlayerEventListener ;
158156import org .schabi .newpipe .player .event .PlayerGestureListener ;
159157import org .schabi .newpipe .player .event .PlayerServiceEventListener ;
188186import org .schabi .newpipe .util .external_communication .KoreUtils ;
189187import org .schabi .newpipe .util .external_communication .ShareUtils ;
190188import org .schabi .newpipe .views .ExpandableSurfaceView ;
189+ import org .schabi .newpipe .views .player .PlayerFastSeekOverlay ;
191190
192191import java .io .IOException ;
193192import java .util .ArrayList ;
@@ -247,6 +246,7 @@ public final class Player implements
247246 public static final int DEFAULT_CONTROLS_DURATION = 300 ; // 300 millis
248247 public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000 ; // 2 Seconds
249248 public static final int DPAD_CONTROLS_HIDE_TIME = 7000 ; // 7 Seconds
249+ public static final int SEEK_OVERLAY_DURATION = 450 ; // 450 millis
250250
251251 /*//////////////////////////////////////////////////////////////////////////
252252 // Other constants
@@ -313,7 +313,6 @@ public final class Player implements
313313
314314 private PlayerBinding binding ;
315315
316- private ValueAnimator controlViewAnimator ;
317316 private final Handler controlsVisibilityHandler = new Handler ();
318317
319318 // fullscreen player
@@ -365,6 +364,7 @@ public final class Player implements
365364
366365 private int maxGestureLength ; // scaled
367366 private GestureDetectorCompat gestureDetector ;
367+ private PlayerGestureListener playerGestureListener ;
368368
369369 /*//////////////////////////////////////////////////////////////////////////
370370 // Listeners and disposables
@@ -449,6 +449,8 @@ public void setupFromView(@NonNull final PlayerBinding playerBinding) {
449449 initPlayer (true );
450450 }
451451 initListeners ();
452+
453+ setupPlayerSeekOverlay ();
452454 }
453455
454456 private void initViews (@ NonNull final PlayerBinding playerBinding ) {
@@ -525,9 +527,9 @@ private void initListeners() {
525527 binding .resizeTextView .setOnClickListener (this );
526528 binding .playbackLiveSync .setOnClickListener (this );
527529
528- final PlayerGestureListener listener = new PlayerGestureListener (this , service );
529- gestureDetector = new GestureDetectorCompat (context , listener );
530- binding .getRoot ().setOnTouchListener (listener );
530+ playerGestureListener = new PlayerGestureListener (this , service );
531+ gestureDetector = new GestureDetectorCompat (context , playerGestureListener );
532+ binding .getRoot ().setOnTouchListener (playerGestureListener );
531533
532534 binding .queueButton .setOnClickListener (this );
533535 binding .segmentsButton .setOnClickListener (this );
@@ -578,6 +580,68 @@ public void onChange(final boolean selfChange) {
578580 v .getPaddingRight (),
579581 v .getPaddingBottom ()));
580582 }
583+
584+ /**
585+ * Initializes the Fast-For/Backward overlay.
586+ */
587+ private void setupPlayerSeekOverlay () {
588+ binding .fastSeekOverlay
589+ .seekSecondsSupplier (
590+ () -> (int ) (retrieveSeekDurationFromPreferences (this ) / 1000.0f ))
591+ .performListener (new PlayerFastSeekOverlay .PerformListener () {
592+
593+ @ Override
594+ public void onDoubleTap () {
595+ animate (binding .fastSeekOverlay , true , SEEK_OVERLAY_DURATION );
596+ }
597+
598+ @ Override
599+ public void onDoubleTapEnd () {
600+ animate (binding .fastSeekOverlay , false , SEEK_OVERLAY_DURATION );
601+ }
602+
603+ @ Override
604+ public FastSeekDirection getFastSeekDirection (
605+ @ NonNull final DisplayPortion portion
606+ ) {
607+ if (exoPlayerIsNull ()) {
608+ // Abort seeking
609+ playerGestureListener .endMultiDoubleTap ();
610+ return FastSeekDirection .NONE ;
611+ }
612+ if (portion == DisplayPortion .LEFT ) {
613+ // Check if it's possible to rewind
614+ // Small puffer to eliminate infinite rewind seeking
615+ if (simpleExoPlayer .getCurrentPosition () < 500L ) {
616+ return FastSeekDirection .NONE ;
617+ }
618+ return FastSeekDirection .BACKWARD ;
619+ } else if (portion == DisplayPortion .RIGHT ) {
620+ // Check if it's possible to fast-forward
621+ if (currentState == STATE_COMPLETED
622+ || simpleExoPlayer .getCurrentPosition ()
623+ >= simpleExoPlayer .getDuration ()) {
624+ return FastSeekDirection .NONE ;
625+ }
626+ return FastSeekDirection .FORWARD ;
627+ }
628+ /* portion == DisplayPortion.MIDDLE */
629+ return FastSeekDirection .NONE ;
630+ }
631+
632+ @ Override
633+ public void seek (final boolean forward ) {
634+ playerGestureListener .keepInDoubleTapMode ();
635+ if (forward ) {
636+ fastForward ();
637+ } else {
638+ fastRewind ();
639+ }
640+ }
641+ });
642+ playerGestureListener .doubleTapControls (binding .fastSeekOverlay );
643+ }
644+
581645 //endregion
582646
583647
@@ -1796,71 +1860,6 @@ public boolean isControlsVisible() {
17961860 return binding != null && binding .playbackControlRoot .getVisibility () == View .VISIBLE ;
17971861 }
17981862
1799- /**
1800- * Show a animation, and depending on goneOnEnd, will stay on the screen or be gone.
1801- *
1802- * @param drawableId the drawable that will be used to animate,
1803- * pass -1 to clear any animation that is visible
1804- * @param goneOnEnd will set the animation view to GONE on the end of the animation
1805- */
1806- public void showAndAnimateControl (final int drawableId , final boolean goneOnEnd ) {
1807- if (DEBUG ) {
1808- Log .d (TAG , "showAndAnimateControl() called with: "
1809- + "drawableId = [" + drawableId + "], goneOnEnd = [" + goneOnEnd + "]" );
1810- }
1811- if (controlViewAnimator != null && controlViewAnimator .isRunning ()) {
1812- if (DEBUG ) {
1813- Log .d (TAG , "showAndAnimateControl: controlViewAnimator.isRunning" );
1814- }
1815- controlViewAnimator .end ();
1816- }
1817-
1818- if (drawableId == -1 ) {
1819- if (binding .controlAnimationView .getVisibility () == View .VISIBLE ) {
1820- controlViewAnimator = ObjectAnimator .ofPropertyValuesHolder (
1821- binding .controlAnimationView ,
1822- PropertyValuesHolder .ofFloat (View .ALPHA , 1.0f , 0.0f ),
1823- PropertyValuesHolder .ofFloat (View .SCALE_X , 1.4f , 1.0f ),
1824- PropertyValuesHolder .ofFloat (View .SCALE_Y , 1.4f , 1.0f )
1825- ).setDuration (DEFAULT_CONTROLS_DURATION );
1826- controlViewAnimator .addListener (new AnimatorListenerAdapter () {
1827- @ Override
1828- public void onAnimationEnd (final Animator animation ) {
1829- binding .controlAnimationView .setVisibility (View .GONE );
1830- }
1831- });
1832- controlViewAnimator .start ();
1833- }
1834- return ;
1835- }
1836-
1837- final float scaleFrom = goneOnEnd ? 1f : 1f ;
1838- final float scaleTo = goneOnEnd ? 1.8f : 1.4f ;
1839- final float alphaFrom = goneOnEnd ? 1f : 0f ;
1840- final float alphaTo = goneOnEnd ? 0f : 1f ;
1841-
1842-
1843- controlViewAnimator = ObjectAnimator .ofPropertyValuesHolder (
1844- binding .controlAnimationView ,
1845- PropertyValuesHolder .ofFloat (View .ALPHA , alphaFrom , alphaTo ),
1846- PropertyValuesHolder .ofFloat (View .SCALE_X , scaleFrom , scaleTo ),
1847- PropertyValuesHolder .ofFloat (View .SCALE_Y , scaleFrom , scaleTo )
1848- );
1849- controlViewAnimator .setDuration (goneOnEnd ? 1000 : 500 );
1850- controlViewAnimator .addListener (new AnimatorListenerAdapter () {
1851- @ Override
1852- public void onAnimationEnd (final Animator animation ) {
1853- binding .controlAnimationView .setVisibility (goneOnEnd ? View .GONE : View .VISIBLE );
1854- }
1855- });
1856-
1857-
1858- binding .controlAnimationView .setVisibility (View .VISIBLE );
1859- binding .controlAnimationView .setImageDrawable (
1860- AppCompatResources .getDrawable (context , drawableId ));
1861- controlViewAnimator .start ();
1862- }
1863-
18641863 public void showControlsThenHide () {
18651864 if (DEBUG ) {
18661865 Log .d (TAG , "showControlsThenHide() called" );
@@ -1905,6 +1904,7 @@ public void hideControls(final long duration, final long delay) {
19051904 }
19061905
19071906 private void showHideShadow (final boolean show , final long duration ) {
1907+ animate (binding .playbackControlsShadow , show , duration , AnimationType .ALPHA , 0 , null );
19081908 animate (binding .playerTopShadow , show , duration , AnimationType .ALPHA , 0 , null );
19091909 animate (binding .playerBottomShadow , show , duration , AnimationType .ALPHA , 0 , null );
19101910 }
@@ -2102,8 +2102,8 @@ private void onBlocked() {
21022102 startProgressLoop ();
21032103 }
21042104
2105- controlsVisibilityHandler . removeCallbacksAndMessages ( null );
2106- animate ( binding . playbackControlRoot , false , DEFAULT_CONTROLS_DURATION );
2105+ // if we are e.g. switching players, hide controls
2106+ hideControls ( DEFAULT_CONTROLS_DURATION , 0 );
21072107
21082108 binding .playbackSeekBar .setEnabled (false );
21092109 binding .playbackSeekBar .getThumb ()
@@ -2130,8 +2130,6 @@ private void onPlaying() {
21302130
21312131 updateStreamRelatedViews ();
21322132
2133- showAndAnimateControl (-1 , true );
2134-
21352133 binding .playbackSeekBar .setEnabled (true );
21362134 binding .playbackSeekBar .getThumb ()
21372135 .setColorFilter (new PorterDuffColorFilter (Color .RED , PorterDuff .Mode .SRC_IN ));
@@ -2179,18 +2177,21 @@ private void onPaused() {
21792177 stopProgressLoop ();
21802178 }
21812179
2182- showControls (400 );
2183- binding .loadingPanel .setVisibility (View .GONE );
2184-
2185- animate (binding .playPauseButton , false , 80 , AnimationType .SCALE_AND_ALPHA , 0 ,
2186- () -> {
2187- binding .playPauseButton .setImageResource (R .drawable .ic_play_arrow );
2188- animatePlayButtons (true , 200 );
2189- if (!isQueueVisible ) {
2190- binding .playPauseButton .requestFocus ();
2191- }
2192- });
2180+ // Don't let UI elements popup during double tap seeking. This state is entered sometimes
2181+ // during seeking/loading. This if-else check ensures that the controls aren't popping up.
2182+ if (!playerGestureListener .isDoubleTapping ()) {
2183+ showControls (400 );
2184+ binding .loadingPanel .setVisibility (View .GONE );
21932185
2186+ animate (binding .playPauseButton , false , 80 , AnimationType .SCALE_AND_ALPHA , 0 ,
2187+ () -> {
2188+ binding .playPauseButton .setImageResource (R .drawable .ic_play_arrow );
2189+ animatePlayButtons (true , 200 );
2190+ if (!isQueueVisible ) {
2191+ binding .playPauseButton .requestFocus ();
2192+ }
2193+ });
2194+ }
21942195 changePopupWindowFlags (IDLE_WINDOW_FLAGS );
21952196
21962197 // Remove running notification when user does not want minimization to background or popup
@@ -2208,7 +2209,6 @@ private void onPausedSeek() {
22082209 if (DEBUG ) {
22092210 Log .d (TAG , "onPausedSeek() called" );
22102211 }
2211- showAndAnimateControl (-1 , true );
22122212
22132213 animatePlayButtons (false , 100 );
22142214 binding .getRoot ().setKeepScreenOn (true );
@@ -2838,7 +2838,6 @@ public void fastForward() {
28382838 }
28392839 seekBy (retrieveSeekDurationFromPreferences (this ));
28402840 triggerProgressUpdate ();
2841- showAndAnimateControl (R .drawable .ic_fast_forward , true );
28422841 }
28432842
28442843 public void fastRewind () {
@@ -2847,7 +2846,6 @@ public void fastRewind() {
28472846 }
28482847 seekBy (-retrieveSeekDurationFromPreferences (this ));
28492848 triggerProgressUpdate ();
2850- showAndAnimateControl (R .drawable .ic_fast_rewind , true );
28512849 }
28522850 //endregion
28532851
@@ -4279,6 +4277,10 @@ public TextView getCurrentDisplaySeek() {
42794277 return binding .currentDisplaySeek ;
42804278 }
42814279
4280+ public PlayerFastSeekOverlay getFastSeekOverlay () {
4281+ return binding .fastSeekOverlay ;
4282+ }
4283+
42824284 @ Nullable
42834285 public WindowManager .LayoutParams getPopupLayoutParams () {
42844286 return popupLayoutParams ;
0 commit comments