diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 679084bdf..1fb6b5859 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -255,11 +255,10 @@ public final class VideoDetailFragment playerUi.ifPresent(MainPlayerUi::toggleFullscreen); } - //noinspection SimplifyOptionalCallChains if (playAfterConnect || (currentInfo != null && isAutoplayEnabled() - && !playerUi.isPresent())) { + && playerUi.isEmpty())) { autoPlayEnabled = true; // forcefully start playing openVideoPlayerAutoFullscreen(); } @@ -1174,16 +1173,15 @@ public final class VideoDetailFragment * be reused in a few milliseconds and the flickering would be annoying. */ private void hideMainPlayerOnLoadingNewStream() { - //noinspection SimplifyOptionalCallChains - if (!isPlayerServiceAvailable() || !getRoot().isPresent() - || !player.videoPlayerSelected()) { + final var root = getRoot(); + if (!isPlayerServiceAvailable() || root.isEmpty() || !player.videoPlayerSelected()) { return; } removeVideoPlayerView(); if (isAutoplayEnabled()) { playerService.stopForImmediateReusing(); - getRoot().ifPresent(view -> view.setVisibility(View.GONE)); + root.ifPresent(view -> view.setVisibility(View.GONE)); } else { playerHolder.stopService(); } @@ -1887,10 +1885,9 @@ public final class VideoDetailFragment @Override public void onFullscreenStateChanged(final boolean fullscreen) { setupBrightness(); - //noinspection SimplifyOptionalCallChains if (!isPlayerAndPlayerServiceAvailable() - || !player.UIs().get(MainPlayerUi.class).isPresent() - || getRoot().map(View::getParent).orElse(null) == null) { + || player.UIs().get(MainPlayerUi.class).isEmpty() + || getRoot().flatMap(v -> Optional.ofNullable(v.getParent())).isEmpty()) { return; } @@ -2429,23 +2426,20 @@ public final class VideoDetailFragment // helpers to check the state of player and playerService boolean isPlayerAvailable() { - return (player != null); + return player != null; } boolean isPlayerServiceAvailable() { - return (playerService != null); + return playerService != null; } boolean isPlayerAndPlayerServiceAvailable() { - return (player != null && playerService != null); + return player != null && playerService != null; } public Optional getRoot() { - if (player == null) { - return Optional.empty(); - } - - return player.UIs().get(VideoPlayerUi.class) + return Optional.ofNullable(player) + .flatMap(player1 -> player1.UIs().get(VideoPlayerUi.class)) .map(playerUi -> playerUi.getBinding().getRoot()); } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 95520ba1e..b6098a1ef 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1695,26 +1695,25 @@ public final class Player implements PlaybackListener, Listener { } private void saveStreamProgressState(final long progressMillis) { - //noinspection SimplifyOptionalCallChains - if (!getCurrentStreamInfo().isPresent() - || !prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true)) { - return; - } - if (DEBUG) { - Log.d(TAG, "saveStreamProgressState() called with: progressMillis=" + progressMillis - + ", currentMetadata=[" + getCurrentStreamInfo().get().getName() + "]"); - } + getCurrentStreamInfo().ifPresent(info -> { + if (!prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true)) { + return; + } + if (DEBUG) { + Log.d(TAG, "saveStreamProgressState() called with: progressMillis=" + progressMillis + + ", currentMetadata=[" + info.getName() + "]"); + } - databaseUpdateDisposable - .add(recordManager.saveStreamState(getCurrentStreamInfo().get(), progressMillis) - .observeOn(AndroidSchedulers.mainThread()) - .doOnError(e -> { - if (DEBUG) { - e.printStackTrace(); - } - }) - .onErrorComplete() - .subscribe()); + databaseUpdateDisposable.add(recordManager.saveStreamState(info, progressMillis) + .observeOn(AndroidSchedulers.mainThread()) + .doOnError(e -> { + if (DEBUG) { + e.printStackTrace(); + } + }) + .onErrorComplete() + .subscribe()); + }); } public void saveStreamProgressState() { @@ -1876,23 +1875,16 @@ public final class Player implements PlaybackListener, Listener { loadController.disablePreloadingOfCurrentTrack(); } - @Nullable - public VideoStream getSelectedVideoStream() { - @Nullable final MediaItemTag.Quality quality = Optional.ofNullable(currentMetadata) + public Optional getSelectedVideoStream() { + return Optional.ofNullable(currentMetadata) .flatMap(MediaItemTag::getMaybeQuality) - .orElse(null); - if (quality == null) { - return null; - } - - final List availableStreams = quality.getSortedVideoStreams(); - final int selectedStreamIndex = quality.getSelectedVideoStreamIndex(); - - if (selectedStreamIndex >= 0 && availableStreams.size() > selectedStreamIndex) { - return availableStreams.get(selectedStreamIndex); - } else { - return null; - } + .filter(quality -> { + final int selectedStreamIndex = quality.getSelectedVideoStreamIndex(); + return selectedStreamIndex >= 0 + && selectedStreamIndex < quality.getSortedVideoStreams().size(); + }) + .map(quality -> quality.getSortedVideoStreams() + .get(quality.getSelectedVideoStreamIndex())); } //endregion @@ -2036,40 +2028,36 @@ public final class Player implements PlaybackListener, Listener { // in livestreams) so we will be not able to execute the block below. // Reload the play queue manager in this case, which is the behavior when we don't know the // index of the video renderer or playQueueManagerReloadingNeeded returns true. - final Optional optCurrentStreamInfo = getCurrentStreamInfo(); - if (!optCurrentStreamInfo.isPresent()) { - reloadPlayQueueManager(); - setRecovery(); - return; - } + getCurrentStreamInfo().ifPresentOrElse(info -> { + // In the case we don't know the source type, fallback to the one with video with audio + // or audio-only source. + final SourceType sourceType = videoResolver.getStreamSourceType() + .orElse(SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY); - final StreamInfo info = optCurrentStreamInfo.get(); + if (playQueueManagerReloadingNeeded(sourceType, info, getVideoRendererIndex())) { + reloadPlayQueueManager(); + } else { + if (StreamTypeUtil.isAudio(info.getStreamType())) { + // Nothing to do more than setting the recovery position + setRecovery(); + return; + } - // In the case we don't know the source type, fallback to the one with video with audio or - // audio-only source. - final SourceType sourceType = videoResolver.getStreamSourceType().orElse( - SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY); + final var parametersBuilder = trackSelector.buildUponParameters(); - if (playQueueManagerReloadingNeeded(sourceType, info, getVideoRendererIndex())) { - reloadPlayQueueManager(); - } else { - if (StreamTypeUtil.isAudio(info.getStreamType())) { - // Nothing to do more than setting the recovery position - setRecovery(); - return; + // Enable/disable the video track and the ability to select subtitles + parametersBuilder.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoEnabled); + parametersBuilder.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoEnabled); + + trackSelector.setParameters(parametersBuilder); } - final DefaultTrackSelector.Parameters.Builder parametersBuilder = - trackSelector.buildUponParameters(); - - // Enable/disable the video track and the ability to select subtitles - parametersBuilder.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoEnabled); - parametersBuilder.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoEnabled); - - trackSelector.setParameters(parametersBuilder); - } - - setRecovery(); + setRecovery(); + }, () -> { + // This is executed when the current stream info is not available. + reloadPlayQueueManager(); + setRecovery(); + }); } /** diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java index d23dd4574..4f808caac 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java @@ -61,12 +61,11 @@ public interface MediaItemTag { @NonNull static Optional from(@Nullable final MediaItem mediaItem) { - if (mediaItem == null || mediaItem.localConfiguration == null - || !(mediaItem.localConfiguration.tag instanceof MediaItemTag)) { - return Optional.empty(); - } - - return Optional.of((MediaItemTag) mediaItem.localConfiguration.tag); + return Optional.ofNullable(mediaItem) + .flatMap(item -> Optional.ofNullable(item.localConfiguration)) + .flatMap(localConfiguration -> Optional.ofNullable(localConfiguration.tag)) + .filter(MediaItemTag.class::isInstance) + .map(MediaItemTag.class::cast); } @NonNull diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index 9b13bb3d7..88d7145bc 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -7,8 +7,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.ArraySet; -import com.google.android.exoplayer2.source.MediaSource; - import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -23,10 +21,10 @@ import org.schabi.newpipe.player.playqueue.events.MoveEvent; import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent; import org.schabi.newpipe.player.playqueue.events.RemoveEvent; import org.schabi.newpipe.player.playqueue.events.ReorderEvent; -import org.schabi.newpipe.util.ServiceHelper; import java.util.Collection; import java.util.Collections; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -43,6 +41,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException; import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException; import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG; +import static org.schabi.newpipe.util.ServiceHelper.getCacheExpirationMillis; public class MediaSourceManager { @NonNull @@ -421,31 +420,39 @@ public class MediaSourceManager { } private Single getLoadedMediaSource(@NonNull final PlayQueueItem stream) { - return stream.getStream().map(streamInfo -> { - final MediaSource source = playbackListener.sourceOf(stream, streamInfo); - if (source == null || !MediaItemTag.from(source.getMediaItem()).isPresent()) { - final String message = "Unable to resolve source from stream info. " - + "URL: " + stream.getUrl() + ", " - + "audio count: " + streamInfo.getAudioStreams().size() + ", " - + "video count: " + streamInfo.getVideoOnlyStreams().size() + ", " - + streamInfo.getVideoStreams().size(); - return (ManagedMediaSource) - FailedMediaSource.of(stream, new MediaSourceResolutionException(message)); - } - - final MediaItemTag tag = MediaItemTag.from(source.getMediaItem()).get(); - final long expiration = System.currentTimeMillis() - + ServiceHelper.getCacheExpirationMillis(streamInfo.getServiceId()); - return new LoadedMediaSource(source, tag, stream, expiration); - }).onErrorReturn(throwable -> { - if (throwable instanceof ExtractionException) { - return FailedMediaSource.of(stream, new StreamInfoLoadException(throwable)); - } - // Non-source related error expected here (e.g. network), - // should allow retry shortly after the error. - return FailedMediaSource.of(stream, new Exception(throwable), - /*allowRetryIn=*/TimeUnit.MILLISECONDS.convert(3, TimeUnit.SECONDS)); - }); + return stream.getStream() + .map(streamInfo -> Optional + .ofNullable(playbackListener.sourceOf(stream, streamInfo)) + .flatMap(source -> + MediaItemTag.from(source.getMediaItem()) + .map(tag -> { + final int serviceId = streamInfo.getServiceId(); + final long expiration = System.currentTimeMillis() + + getCacheExpirationMillis(serviceId); + return new LoadedMediaSource(source, tag, stream, + expiration); + }) + ) + .orElseGet(() -> { + final String message = "Unable to resolve source from stream info. " + + "URL: " + stream.getUrl() + + ", audio count: " + streamInfo.getAudioStreams().size() + + ", video count: " + streamInfo.getVideoOnlyStreams().size() + + ", " + streamInfo.getVideoStreams().size(); + return FailedMediaSource.of(stream, + new MediaSourceResolutionException(message)); + }) + ) + .onErrorReturn(throwable -> { + if (throwable instanceof ExtractionException) { + return FailedMediaSource.of(stream, new StreamInfoLoadException(throwable)); + } + // Non-source related error expected here (e.g. network), + // should allow retry shortly after the error. + final long allowRetryIn = TimeUnit.MILLISECONDS.convert(3, + TimeUnit.SECONDS); + return FailedMediaSource.of(stream, new Exception(throwable), allowRetryIn); + }); } private void onMediaSourceReceived(@NonNull final PlayQueueItem item, diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java index 9eea89e78..28856d606 100644 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java @@ -8,6 +8,7 @@ import android.widget.ImageView; import androidx.annotation.IntDef; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.graphics.BitmapCompat; import androidx.core.math.MathUtils; import androidx.preference.PreferenceManager; @@ -16,7 +17,6 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.util.DeviceUtils; import java.lang.annotation.Retention; -import java.util.Optional; import java.util.function.IntSupplier; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -66,21 +66,19 @@ public final class SeekbarPreviewThumbnailHelper { public static void tryResizeAndSetSeekbarPreviewThumbnail( @NonNull final Context context, - @NonNull final Optional optPreviewThumbnail, + @Nullable final Bitmap previewThumbnail, @NonNull final ImageView currentSeekbarPreviewThumbnail, @NonNull final IntSupplier baseViewWidthSupplier) { - - if (!optPreviewThumbnail.isPresent()) { + if (previewThumbnail == null) { currentSeekbarPreviewThumbnail.setVisibility(View.GONE); return; } currentSeekbarPreviewThumbnail.setVisibility(View.VISIBLE); - final Bitmap srcBitmap = optPreviewThumbnail.get(); // Resize original bitmap try { - final int srcWidth = srcBitmap.getWidth() > 0 ? srcBitmap.getWidth() : 1; + final int srcWidth = previewThumbnail.getWidth() > 0 ? previewThumbnail.getWidth() : 1; final int newWidth = MathUtils.clamp( // Use 1/4 of the width for the preview Math.round(baseViewWidthSupplier.getAsInt() / 4f), @@ -90,15 +88,15 @@ public final class SeekbarPreviewThumbnailHelper { Math.round(srcWidth * 2.5f)); final float scaleFactor = (float) newWidth / srcWidth; - final int newHeight = (int) (srcBitmap.getHeight() * scaleFactor); + final int newHeight = (int) (previewThumbnail.getHeight() * scaleFactor); - currentSeekbarPreviewThumbnail.setImageBitmap(BitmapCompat.createScaledBitmap(srcBitmap, - newWidth, newHeight, null, true)); + currentSeekbarPreviewThumbnail.setImageBitmap(BitmapCompat + .createScaledBitmap(previewThumbnail, newWidth, newHeight, null, true)); } catch (final Exception ex) { Log.e(TAG, "Failed to resize and set seekbar preview thumbnail", ex); currentSeekbarPreviewThumbnail.setVisibility(View.GONE); } finally { - srcBitmap.recycle(); + previewThumbnail.recycle(); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java index 6226900f6..683629c25 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java @@ -74,6 +74,7 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -746,15 +747,10 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh } private int getNearestStreamSegmentPosition(final long playbackPosition) { - //noinspection SimplifyOptionalCallChains - if (!player.getCurrentStreamInfo().isPresent()) { - return 0; - } - int nearestPosition = 0; final List segments = player.getCurrentStreamInfo() - .get() - .getStreamSegments(); + .map(StreamInfo::getStreamSegments) + .orElse(Collections.emptyList()); for (int i = 0; i < segments.size(); i++) { if (segments.get(i).getStartTimeSeconds() * 1000L > playbackPosition) { @@ -866,14 +862,11 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh @Override protected void onPlaybackSpeedClicked() { - final AppCompatActivity activity = getParentActivity().orElse(null); - if (activity == null) { - return; - } - - PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), player.getPlaybackPitch(), - player.getPlaybackSkipSilence(), player::setPlaybackParameters) - .show(activity.getSupportFragmentManager(), null); + getParentActivity().ifPresent(activity -> + PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), + player.getPlaybackPitch(), player.getPlaybackSkipSilence(), + player::setPlaybackParameters) + .show(activity.getSupportFragmentManager(), null)); } @Override @@ -973,22 +966,22 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh //////////////////////////////////////////////////////////////////////////*/ //region Getters + private Optional getParentContext() { + return Optional.ofNullable(binding.getRoot().getParent()) + .filter(ViewGroup.class::isInstance) + .map(parent -> ((ViewGroup) parent).getContext()); + } + public Optional getParentActivity() { - final ViewParent rootParent = binding.getRoot().getParent(); - if (rootParent instanceof ViewGroup) { - final Context activity = ((ViewGroup) rootParent).getContext(); - if (activity instanceof AppCompatActivity) { - return Optional.of((AppCompatActivity) activity); - } - } - return Optional.empty(); + return getParentContext() + .filter(AppCompatActivity.class::isInstance) + .map(AppCompatActivity.class::cast); } public boolean isLandscape() { // DisplayMetrics from activity context knows about MultiWindow feature // while DisplayMetrics from app context doesn't - return DeviceUtils.isLandscape( - getParentActivity().map(Context.class::cast).orElse(player.getService())); + return DeviceUtils.isLandscape(getParentContext().orElse(player.getService())); } //endregion } diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java index bce75d77f..e4f5b05e1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java @@ -566,7 +566,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa SeekbarPreviewThumbnailHelper .tryResizeAndSetSeekbarPreviewThumbnail( player.getContext(), - seekbarPreviewThumbnailHolder.getBitmapAt(progress), + seekbarPreviewThumbnailHolder.getBitmapAt(progress).orElse(null), binding.currentSeekbarPreviewThumbnail, binding.subtitleView::getWidth); @@ -982,61 +982,56 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa } private void updateStreamRelatedViews() { - //noinspection SimplifyOptionalCallChains - if (!player.getCurrentStreamInfo().isPresent()) { - return; - } - final StreamInfo info = player.getCurrentStreamInfo().get(); + player.getCurrentStreamInfo().ifPresent(info -> { + binding.qualityTextView.setVisibility(View.GONE); + binding.playbackSpeed.setVisibility(View.GONE); - binding.qualityTextView.setVisibility(View.GONE); - binding.playbackSpeed.setVisibility(View.GONE); + binding.playbackEndTime.setVisibility(View.GONE); + binding.playbackLiveSync.setVisibility(View.GONE); - binding.playbackEndTime.setVisibility(View.GONE); - binding.playbackLiveSync.setVisibility(View.GONE); - - switch (info.getStreamType()) { - case AUDIO_STREAM: - case POST_LIVE_AUDIO_STREAM: - binding.surfaceView.setVisibility(View.GONE); - binding.endScreen.setVisibility(View.VISIBLE); - binding.playbackEndTime.setVisibility(View.VISIBLE); - break; - - case AUDIO_LIVE_STREAM: - binding.surfaceView.setVisibility(View.GONE); - binding.endScreen.setVisibility(View.VISIBLE); - binding.playbackLiveSync.setVisibility(View.VISIBLE); - break; - - case LIVE_STREAM: - binding.surfaceView.setVisibility(View.VISIBLE); - binding.endScreen.setVisibility(View.GONE); - binding.playbackLiveSync.setVisibility(View.VISIBLE); - break; - - case VIDEO_STREAM: - case POST_LIVE_STREAM: - //noinspection SimplifyOptionalCallChains - if (player.getCurrentMetadata() != null - && !player.getCurrentMetadata().getMaybeQuality().isPresent() - || (info.getVideoStreams().isEmpty() - && info.getVideoOnlyStreams().isEmpty())) { + switch (info.getStreamType()) { + case AUDIO_STREAM: + case POST_LIVE_AUDIO_STREAM: + binding.surfaceView.setVisibility(View.GONE); + binding.endScreen.setVisibility(View.VISIBLE); + binding.playbackEndTime.setVisibility(View.VISIBLE); break; - } - buildQualityMenu(); + case AUDIO_LIVE_STREAM: + binding.surfaceView.setVisibility(View.GONE); + binding.endScreen.setVisibility(View.VISIBLE); + binding.playbackLiveSync.setVisibility(View.VISIBLE); + break; - binding.qualityTextView.setVisibility(View.VISIBLE); - binding.surfaceView.setVisibility(View.VISIBLE); - // fallthrough - default: - binding.endScreen.setVisibility(View.GONE); - binding.playbackEndTime.setVisibility(View.VISIBLE); - break; - } + case LIVE_STREAM: + binding.surfaceView.setVisibility(View.VISIBLE); + binding.endScreen.setVisibility(View.GONE); + binding.playbackLiveSync.setVisibility(View.VISIBLE); + break; - buildPlaybackSpeedMenu(); - binding.playbackSpeed.setVisibility(View.VISIBLE); + case VIDEO_STREAM: + case POST_LIVE_STREAM: + if (player.getCurrentMetadata() != null + && player.getCurrentMetadata().getMaybeQuality().isEmpty() + || (info.getVideoStreams().isEmpty() + && info.getVideoOnlyStreams().isEmpty())) { + break; + } + + buildQualityMenu(); + + binding.qualityTextView.setVisibility(View.VISIBLE); + binding.surfaceView.setVisibility(View.VISIBLE); + // fallthrough + default: + binding.endScreen.setVisibility(View.GONE); + binding.playbackEndTime.setVisibility(View.VISIBLE); + break; + } + + buildPlaybackSpeedMenu(); + binding.playbackSpeed.setVisibility(View.VISIBLE); + }); } //endregion @@ -1065,12 +1060,11 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa qualityPopupMenu.getMenu().add(POPUP_MENU_ID_QUALITY, i, Menu.NONE, MediaFormat .getNameById(videoStream.getFormatId()) + " " + videoStream.getResolution()); } - final VideoStream selectedVideoStream = player.getSelectedVideoStream(); - if (selectedVideoStream != null) { - binding.qualityTextView.setText(selectedVideoStream.getResolution()); - } qualityPopupMenu.setOnMenuItemClickListener(this); qualityPopupMenu.setOnDismissListener(this); + + player.getSelectedVideoStream() + .ifPresent(s -> binding.qualityTextView.setText(s.getResolution())); } private void buildPlaybackSpeedMenu() { @@ -1176,12 +1170,9 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa qualityPopupMenu.show(); isSomePopupMenuVisible = true; - final VideoStream videoStream = player.getSelectedVideoStream(); - if (videoStream != null) { - //noinspection SetTextI18n - binding.qualityTextView.setText(MediaFormat.getNameById(videoStream.getFormatId()) - + " " + videoStream.getResolution()); - } + player.getSelectedVideoStream() + .map(s -> MediaFormat.getNameById(s.getFormatId()) + " " + s.getResolution()) + .ifPresent(binding.qualityTextView::setText); } /** @@ -1198,8 +1189,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa if (menuItem.getGroupId() == POPUP_MENU_ID_QUALITY) { final int menuItemIndex = menuItem.getItemId(); @Nullable final MediaItemTag currentMetadata = player.getCurrentMetadata(); - //noinspection SimplifyOptionalCallChains - if (currentMetadata == null || !currentMetadata.getMaybeQuality().isPresent()) { + if (currentMetadata == null || currentMetadata.getMaybeQuality().isEmpty()) { return true; } @@ -1238,10 +1228,9 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]"); } isSomePopupMenuVisible = false; //TODO check if this works - final VideoStream selectedVideoStream = player.getSelectedVideoStream(); - if (selectedVideoStream != null) { - binding.qualityTextView.setText(selectedVideoStream.getResolution()); - } + player.getSelectedVideoStream() + .ifPresent(s -> binding.qualityTextView.setText(s.getResolution())); + if (player.isPlaying()) { hideControls(DEFAULT_CONTROLS_DURATION, 0); hideSystemUIIfNeeded(); @@ -1300,9 +1289,8 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa // Build UI buildCaptionMenu(availableLanguages); - //noinspection SimplifyOptionalCallChains if (player.getTrackSelector().getParameters().getRendererDisabled( - player.getCaptionRendererIndex()) || !selectedTracks.isPresent()) { + player.getCaptionRendererIndex()) || selectedTracks.isEmpty()) { binding.captionTextView.setText(R.string.caption_none); } else { binding.captionTextView.setText(selectedTracks.get().language);