@@ -1961,13 +1961,12 @@ private void showOrHideButtons() {
19611961 final boolean showPrev = playQueue .getIndex () != 0 ;
19621962 final boolean showNext = playQueue .getIndex () + 1 != playQueue .getStreams ().size ();
19631963 final boolean showQueue = playQueue .getStreams ().size () > 1 && !popupPlayerSelected ();
1964- boolean showSegment = false ;
1965- showSegment = /*only when stream has segment and playing in fullscreen player*/
1966- !popupPlayerSelected ()
1967- && !getCurrentStreamInfo ()
1968- .map (StreamInfo ::getStreamSegments )
1969- .map (List ::isEmpty )
1970- .orElse (/*no stream info=*/ true );
1964+ /* only when stream has segments and is not playing in popup player */
1965+ final boolean showSegment = !popupPlayerSelected ()
1966+ && !getCurrentStreamInfo ()
1967+ .map (StreamInfo ::getStreamSegments )
1968+ .map (List ::isEmpty )
1969+ .orElse (/*no stream info=*/ true );
19711970
19721971 binding .playPreviousButton .setVisibility (showPrev ? View .VISIBLE : View .INVISIBLE );
19731972 binding .playPreviousButton .setAlpha (showPrev ? 1.0f : 0.0f );
@@ -2014,7 +2013,7 @@ public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason
20142013 + "playWhenReady = [" + playWhenReady + "], "
20152014 + "reason = [" + reason + "]" );
20162015 }
2017- final int playbackState = simpleExoPlayer == null
2016+ final int playbackState = exoPlayerIsNull ()
20182017 ? com .google .android .exoplayer2 .Player .STATE_IDLE
20192018 : simpleExoPlayer .getPlaybackState ();
20202019 updatePlaybackState (playWhenReady , playbackState );
@@ -2026,8 +2025,7 @@ public void onPlaybackStateChanged(final int playbackState) {
20262025 Log .d (TAG , "ExoPlayer - onPlaybackStateChanged() called with: "
20272026 + "playbackState = [" + playbackState + "]" );
20282027 }
2029- final boolean playWhenReady = simpleExoPlayer != null && simpleExoPlayer .getPlayWhenReady ();
2030- updatePlaybackState (playWhenReady , playbackState );
2028+ updatePlaybackState (getPlayWhenReady (), playbackState );
20312029 }
20322030
20332031 private void updatePlaybackState (final boolean playWhenReady , final int playbackState ) {
@@ -2486,6 +2484,19 @@ private void setMuteButton(@NonNull final ImageButton button, final boolean isMu
24862484 //////////////////////////////////////////////////////////////////////////*/
24872485 //region ExoPlayer listeners (that didn't fit in other categories)
24882486
2487+ /**
2488+ * <p>Listens for event or state changes on ExoPlayer. When any event happens, we check for
2489+ * changes in the currently-playing metadata and update the encapsulating
2490+ * {@link Player}. Downstream listeners are also informed.
2491+ *
2492+ * <p>When the renewed metadata contains any error, it is reported as a notification.
2493+ * This is done because not all source resolution errors are {@link PlaybackException}, which
2494+ * are also captured by {@link ExoPlayer} and stops the playback.
2495+ *
2496+ * @param player The {@link com.google.android.exoplayer2.Player} whose state changed.
2497+ * @param events The {@link com.google.android.exoplayer2.Player.Events} that has triggered
2498+ * the player state changes.
2499+ **/
24892500 @ Override
24902501 public void onEvents (@ NonNull final com .google .android .exoplayer2 .Player player ,
24912502 @ NonNull final com .google .android .exoplayer2 .Player .Events events ) {
@@ -2602,11 +2613,12 @@ public void onCues(@NonNull final List<Cue> cues) {
26022613 * <li>{@link PlaybackException#ERROR_CODE_BEHIND_LIVE_WINDOW BEHIND_LIVE_WINDOW}:
26032614 * If the playback on livestreams are lagged too far behind the current playable
26042615 * window. Then we seek to the latest timestamp and restart the playback.
2616+ * This error is <b>catchable</b>.
26052617 * </li>
26062618 * <li>From {@link PlaybackException#ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE BAD_IO} to
26072619 * {@link PlaybackException#ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED UNSUPPORTED_FORMATS}:
26082620 * If the stream source is validated by the extractor but not recognized by the player,
2609- * then we can try to recover playback by signal an error on the {@link PlayQueue}.</li>
2621+ * then we can try to recover playback by signalling an error on the {@link PlayQueue}.</li>
26102622 * <li>For {@link PlaybackException#ERROR_CODE_TIMEOUT PLAYER_TIMEOUT},
26112623 * {@link PlaybackException#ERROR_CODE_IO_UNSPECIFIED MEDIA_SOURCE_RESOLVER_TIMEOUT} and
26122624 * {@link PlaybackException#ERROR_CODE_IO_NETWORK_CONNECTION_FAILED NO_NETWORK}:
@@ -2617,8 +2629,8 @@ public void onCues(@NonNull final List<Cue> cues) {
26172629 * We terminate the playback.</li>
26182630 * <li>For any other unspecified issue internal: We set a recovery and try to restart
26192631 * the playback.</li>
2620- * In the case of decoder/renderer or unspecified errors , the player will create a
2621- * notification so the users are aware.
2632+ * For any error above that is <b>not</b> explicitly <b>catchable</b> , the player will
2633+ * create a notification so users are aware.
26222634 * </ul>
26232635 * @see com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException)
26242636 * */
@@ -2627,7 +2639,6 @@ public void onCues(@NonNull final List<Cue> cues) {
26272639 public void onPlayerError (@ NonNull final PlaybackException error ) {
26282640 Log .e (TAG , "ExoPlayer - onPlayerError() called with:" , error );
26292641
2630- setRecovery ();
26312642 saveStreamProgressState ();
26322643 boolean isCatchableException = false ;
26332644
@@ -2652,19 +2663,13 @@ public void onPlayerError(@NonNull final PlaybackException error) {
26522663 case ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED :
26532664 // Source errors, signal on playQueue and move on:
26542665 if (!exoPlayerIsNull () && playQueue != null ) {
2655- isCatchableException = true ;
26562666 playQueue .error ();
26572667 }
26582668 break ;
26592669 case ERROR_CODE_TIMEOUT :
26602670 case ERROR_CODE_IO_UNSPECIFIED :
26612671 case ERROR_CODE_IO_NETWORK_CONNECTION_FAILED :
26622672 case ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT :
2663- // Don't create notification on timeout/networking errors:
2664- isCatchableException = true ;
2665- setRecovery ();
2666- reloadPlayQueueManager ();
2667- break ;
26682673 case ERROR_CODE_UNSPECIFIED :
26692674 // Reload playback on unexpected errors:
26702675 setRecovery ();
@@ -3010,10 +3015,9 @@ public void saveStreamProgressState() {
30103015 }
30113016
30123017 public void saveStreamProgressStateCompleted () {
3013- getCurrentStreamInfo ().ifPresent (info -> {
3014- // current stream has ended, so the progress is its duration (+1 to overcome rounding)
3015- saveStreamProgressState ((info .getDuration () + 1 ) * 1000 );
3016- });
3018+ // current stream has ended, so the progress is its duration (+1 to overcome rounding)
3019+ getCurrentStreamInfo ().ifPresent (info ->
3020+ saveStreamProgressState ((info .getDuration () + 1 ) * 1000 ));
30173021 }
30183022 //endregion
30193023
@@ -3414,7 +3418,8 @@ private void updateStreamRelatedViews() {
34143418 case VIDEO_STREAM :
34153419 if (currentMetadata == null
34163420 || !currentMetadata .getMaybeQuality ().isPresent ()
3417- || info .getVideoStreams ().size () + info .getVideoOnlyStreams ().size () == 0 ) {
3421+ || (info .getVideoStreams ().isEmpty ()
3422+ && info .getVideoOnlyStreams ().isEmpty ())) {
34183423 break ;
34193424 }
34203425
@@ -3684,10 +3689,8 @@ private void onTextTracksChanged() {
36843689 }
36853690
36863691 // Normalize mismatching language strings
3687- final List <String > preferredLanguages =
3688- trackSelector .getParameters ().preferredTextLanguages ;
3689- final String preferredLanguage =
3690- preferredLanguages .isEmpty () ? null : preferredLanguages .get (0 );
3692+ final String preferredLanguage = trackSelector .getParameters ()
3693+ .preferredTextLanguages .stream ().findFirst ().orElse (null );
36913694 // Build UI
36923695 buildCaptionMenu (availableLanguages );
36933696 if (trackSelector .getParameters ().getRendererDisabled (textRenderer )
0 commit comments