From f1f5996975cb3a5315c98dbeaa5e108105a2a4b8 Mon Sep 17 00:00:00 2001 From: John Zhen Mo Date: Wed, 16 May 2018 18:18:32 -0700 Subject: [PATCH] -Refactored playback resolvers and other persistent player objects to instantiate once only during player creation to enforce non-nullity. -Fixed background and popup player service staying in foreground when playback is paused or completed. -Fixed player metadata not updating on new stream. -Fixed player intent playback quality not applied. -Fixed player auto-queue not applied after stream transition or swapping. --- .../newpipe/player/BackgroundPlayer.java | 101 +++++++++++++---- .../org/schabi/newpipe/player/BasePlayer.java | 103 ++++++++++-------- .../newpipe/player/MainVideoPlayer.java | 8 +- .../newpipe/player/PopupVideoPlayer.java | 85 +++++++++++---- .../schabi/newpipe/player/VideoPlayer.java | 18 ++- .../resolver/AudioPlaybackResolver.java | 2 + .../newpipe/player/resolver/Resolver.java | 3 +- .../resolver/VideoPlaybackResolver.java | 1 + 8 files changed, 219 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 37b7e4c64..c5c9f0615 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -37,6 +37,7 @@ import android.widget.RemoteViews; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.MediaSource; +import com.nostra13.universalimageloader.core.assist.FailReason; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; @@ -264,16 +265,16 @@ public final class BackgroundPlayer extends Service { protected class BasePlayerImpl extends BasePlayer { - @Nullable private AudioPlaybackResolver resolver; + @NonNull final private AudioPlaybackResolver resolver; BasePlayerImpl(Context context) { super(context); + this.resolver = new AudioPlaybackResolver(context, dataSource); } @Override public void initPlayer(boolean playOnReady) { super.initPlayer(playOnReady); - resolver = new AudioPlaybackResolver(context, dataSource); } @Override @@ -286,30 +287,65 @@ public final class BackgroundPlayer extends Service { startForeground(NOTIFICATION_ID, notBuilder.build()); } - @Override - public void initThumbnail(final String url) { + /*////////////////////////////////////////////////////////////////////////// + // Thumbnail Loading + //////////////////////////////////////////////////////////////////////////*/ + + private void setDummyRemoteViewThumbnail() { resetNotification(); - if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail); - if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail); + if (notRemoteView != null) { + notRemoteView.setImageViewResource(R.id.notificationCover, + R.drawable.dummy_thumbnail); + } + if (bigNotRemoteView != null) { + bigNotRemoteView.setImageViewResource(R.id.notificationCover, + R.drawable.dummy_thumbnail); + } updateNotification(-1); - super.initThumbnail(url); + } + + @Override + public void onLoadingStarted(String imageUri, View view) { + super.onLoadingStarted(imageUri, view); + setDummyRemoteViewThumbnail(); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); - - if (loadedImage != null) { - // rebuild notification here since remote view does not release bitmaps, causing memory leaks - resetNotification(); - - if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage); - if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage); - - updateNotification(-1); + if (loadedImage == null) { + setDummyRemoteViewThumbnail(); + return; } + + // rebuild notification here since remote view does not release bitmaps, + // causing memory leaks + resetNotification(); + if (notRemoteView != null) { + notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage); + } + if (bigNotRemoteView != null) { + bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage); + } + updateNotification(-1); } + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason) { + super.onLoadingFailed(imageUri, view, failReason); + setDummyRemoteViewThumbnail(); + } + + @Override + public void onLoadingCancelled(String imageUri, View view) { + super.onLoadingCancelled(imageUri, view); + setDummyRemoteViewThumbnail(); + } + + /*////////////////////////////////////////////////////////////////////////// + // States Implementation + //////////////////////////////////////////////////////////////////////////*/ + @Override public void onPrepared(boolean playWhenReady) { super.onPrepared(playWhenReady); @@ -385,17 +421,15 @@ public final class BackgroundPlayer extends Service { protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); - if (shouldUpdateOnProgress) { - resetNotification(); - updateNotification(-1); - updateMetadata(); - } + resetNotification(); + updateNotification(-1); + updateMetadata(); } @Override @Nullable public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) { - return resolver == null ? null : resolver.resolve(info); + return resolver.resolve(info); } @Override @@ -515,18 +549,40 @@ public final class BackgroundPlayer extends Service { updateNotification(-1); } + @Override + public void onBlocked() { + super.onBlocked(); + startForeground(NOTIFICATION_ID, notBuilder.build()); + } + + @Override + public void onBuffering() { + super.onBuffering(); + startForeground(NOTIFICATION_ID, notBuilder.build()); + } + + @Override + public void onPausedSeek() { + super.onPausedSeek(); + startForeground(NOTIFICATION_ID, notBuilder.build()); + } + @Override public void onPlaying() { super.onPlaying(); updateNotification(R.drawable.ic_pause_white); + lockManager.acquireWifiAndCpu(); + startForeground(NOTIFICATION_ID, notBuilder.build()); } @Override public void onPaused() { super.onPaused(); updateNotification(R.drawable.ic_play_arrow_white); + lockManager.releaseWifiAndCpu(); + stopForeground(false); } @Override @@ -539,6 +595,7 @@ public final class BackgroundPlayer extends Service { updateNotification(R.drawable.ic_replay_white); lockManager.releaseWifiAndCpu(); + stopForeground(false); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index a0607621e..3f50cf149 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -68,6 +68,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.resolver.MediaSourceTag; +import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.SerializedCache; import java.io.IOException; @@ -78,6 +79,7 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; +import io.reactivex.disposables.SerialDisposable; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION; @@ -104,6 +106,14 @@ public abstract class BasePlayer implements @NonNull final protected HistoryRecordManager recordManager; + @NonNull final protected CustomTrackSelector trackSelector; + @NonNull final protected PlayerDataSource dataSource; + + @NonNull final private LoadControl loadControl; + @NonNull final private RenderersFactory renderFactory; + + @NonNull final private SerialDisposable progressUpdateReactor; + @NonNull final private CompositeDisposable databaseUpdateReactor; /*////////////////////////////////////////////////////////////////////////// // Intent //////////////////////////////////////////////////////////////////////////*/ @@ -142,9 +152,6 @@ public abstract class BasePlayer implements protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500; protected final static int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds - protected CustomTrackSelector trackSelector; - protected PlayerDataSource dataSource; - protected SimpleExoPlayer simpleExoPlayer; protected AudioReactor audioReactor; protected MediaSessionManager mediaSessionManager; @@ -152,9 +159,6 @@ public abstract class BasePlayer implements private boolean isPrepared = false; private boolean isSynchronizing = false; - protected Disposable progressUpdateReactor; - protected CompositeDisposable databaseUpdateReactor; - //////////////////////////////////////////////////////////////////////////*/ public BasePlayer(@NonNull final Context context) { @@ -171,29 +175,32 @@ public abstract class BasePlayer implements context.registerReceiver(broadcastReceiver, intentFilter); this.recordManager = new HistoryRecordManager(context); + + this.progressUpdateReactor = new SerialDisposable(); + this.databaseUpdateReactor = new CompositeDisposable(); + + final String userAgent = Downloader.USER_AGENT; + final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); + this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); + + final TrackSelection.Factory trackSelectionFactory = + PlayerHelper.getQualitySelector(context, bandwidthMeter); + this.trackSelector = new CustomTrackSelector(trackSelectionFactory); + + this.loadControl = new LoadController(context); + this.renderFactory = new DefaultRenderersFactory(context); } public void setup() { - if (simpleExoPlayer == null) initPlayer(/*playOnInit=*/true); + if (simpleExoPlayer == null) { + initPlayer(/*playOnInit=*/true); + } initListeners(); } public void initPlayer(final boolean playOnReady) { if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]"); - if (databaseUpdateReactor != null) databaseUpdateReactor.dispose(); - databaseUpdateReactor = new CompositeDisposable(); - - final String userAgent = Downloader.USER_AGENT; - final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); - dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); - - final TrackSelection.Factory trackSelectionFactory = - PlayerHelper.getQualitySelector(context, bandwidthMeter); - trackSelector = new CustomTrackSelector(trackSelectionFactory); - - final LoadControl loadControl = new LoadController(context); - final RenderersFactory renderFactory = new DefaultRenderersFactory(context); simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, trackSelector, loadControl); simpleExoPlayer.addListener(this); simpleExoPlayer.setPlayWhenReady(playOnReady); @@ -287,7 +294,6 @@ public abstract class BasePlayer implements if (mediaSessionManager != null) mediaSessionManager.dispose(); - trackSelector = null; mediaSessionManager = null; simpleExoPlayer = null; } @@ -296,11 +302,12 @@ public abstract class BasePlayer implements // Thumbnail Loading //////////////////////////////////////////////////////////////////////////*/ - public void initThumbnail(final String url) { + private void initThumbnail(final String url) { if (DEBUG) Log.d(TAG, "Thumbnail - initThumbnail() called"); if (url == null || url.isEmpty()) return; ImageLoader.getInstance().resume(); - ImageLoader.getInstance().loadImage(url, this); + ImageLoader.getInstance().loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, + this); } @Override @@ -461,13 +468,11 @@ public abstract class BasePlayer implements public abstract void onUpdateProgress(int currentProgress, int duration, int bufferPercent); protected void startProgressLoop() { - if (progressUpdateReactor != null) progressUpdateReactor.dispose(); - progressUpdateReactor = getProgressReactor(); + progressUpdateReactor.set(getProgressReactor()); } protected void stopProgressLoop() { - if (progressUpdateReactor != null) progressUpdateReactor.dispose(); - progressUpdateReactor = null; + progressUpdateReactor.set(null); } public void triggerProgressUpdate() { @@ -482,7 +487,8 @@ public abstract class BasePlayer implements private Disposable getProgressReactor() { return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(ignored -> triggerProgressUpdate()); + .subscribe(ignored -> triggerProgressUpdate(), + error -> Log.e(TAG, "Progress update failure: ", error)); } /*////////////////////////////////////////////////////////////////////////// @@ -496,12 +502,16 @@ public abstract class BasePlayer implements (manifest == null ? "no manifest" : "available manifest") + ", " + "timeline size = [" + timeline.getWindowCount() + "], " + "reason = [" + reason + "]"); + + maybeUpdateCurrentMetadata(); } @Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { if (DEBUG) Log.d(TAG, "ExoPlayer - onTracksChanged(), " + "track group size = " + trackGroups.length); + + maybeUpdateCurrentMetadata(); } @Override @@ -521,6 +531,8 @@ public abstract class BasePlayer implements } else if (isLoading && !isProgressLoopRunning()) { startProgressLoop(); } + + maybeUpdateCurrentMetadata(); } @Override @@ -665,24 +677,22 @@ public abstract class BasePlayer implements if (DEBUG) Log.d(TAG, "ExoPlayer - onPositionDiscontinuity() called with " + "reason = [" + reason + "]"); - maybeUpdateCurrentMetadata(); - // Refresh the playback if there is a transition to the next video final int newPeriodIndex = simpleExoPlayer.getCurrentPeriodIndex(); - - /* Discontinuity reasons!! Thank you ExoPlayer lords */ switch (reason) { case DISCONTINUITY_REASON_PERIOD_TRANSITION: if (newPeriodIndex == playQueue.getIndex()) { registerView(); } else { - playQueue.offsetIndex(+1); + playQueue.setIndex(newPeriodIndex); } case DISCONTINUITY_REASON_SEEK: case DISCONTINUITY_REASON_SEEK_ADJUSTMENT: case DISCONTINUITY_REASON_INTERNAL: break; } + + maybeUpdateCurrentMetadata(); } @Override @@ -794,14 +804,6 @@ public abstract class BasePlayer implements initThumbnail(info.getThumbnailUrl()); registerView(); - - // when starting playback on the last item when not repeating, maybe auto queue - if (playQueue.getIndex() == playQueue.size() - 1 && - getRepeatMode() == Player.REPEAT_MODE_OFF && - PlayerHelper.isAutoQueueEnabled(context)) { - final PlayQueue autoQueue = PlayerHelper.autoQueueOf(info, playQueue.getStreams()); - if (autoQueue != null) playQueue.append(autoQueue.getStreams()); - } } @Override @@ -960,7 +962,7 @@ public abstract class BasePlayer implements //////////////////////////////////////////////////////////////////////////*/ private void registerView() { - if (databaseUpdateReactor == null || currentMetadata == null) return; + if (currentMetadata == null) return; final StreamInfo currentInfo = currentMetadata.getMetadata(); databaseUpdateReactor.add(recordManager.onViewed(currentInfo).onErrorComplete() .subscribe( @@ -980,7 +982,7 @@ public abstract class BasePlayer implements } protected void savePlaybackState(final StreamInfo info, final long progress) { - if (info == null || databaseUpdateReactor == null) return; + if (info == null) return; final Disposable stateSaver = recordManager.saveStreamState(info, progress) .observeOn(AndroidSchedulers.mainThread()) .onErrorComplete() @@ -1012,12 +1014,23 @@ public abstract class BasePlayer implements return; } - if (metadata == null || currentMetadata == metadata) return; + if (metadata == null) return; + maybeAutoQueueNextStream(metadata); + if (currentMetadata == metadata) return; currentMetadata = metadata; onMetadataChanged(metadata); } + private void maybeAutoQueueNextStream(@NonNull final MediaSourceTag currentMetadata) { + if (playQueue == null || playQueue.getIndex() != playQueue.size() - 1 || + getRepeatMode() != Player.REPEAT_MODE_OFF || + !PlayerHelper.isAutoQueueEnabled(context)) return; + // auto queue when starting playback on the last item when not repeating + final PlayQueue autoQueue = PlayerHelper.autoQueueOf(currentMetadata.getMetadata(), + playQueue.getStreams()); + if (autoQueue != null) playQueue.append(autoQueue.getStreams()); + } /*////////////////////////////////////////////////////////////////////////// // Getters and Setters //////////////////////////////////////////////////////////////////////////*/ @@ -1135,7 +1148,7 @@ public abstract class BasePlayer implements } public boolean isProgressLoopRunning() { - return progressUpdateReactor != null && !progressUpdateReactor.isDisposed(); + return progressUpdateReactor.get() != null; } public void setRecovery() { diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index a66906c73..9130ea009 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -127,7 +127,7 @@ public final class MainVideoPlayer extends AppCompatActivity hideSystemUi(); setContentView(R.layout.activity_main_player); - playerImpl = new VideoPlayerImpl(this); + playerImpl = new VideoPlayerImpl(this); playerImpl.setup(findViewById(android.R.id.content)); if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) { @@ -498,11 +498,11 @@ public final class MainVideoPlayer extends AppCompatActivity // Playback Listener //////////////////////////////////////////////////////////////////////////*/ - protected void onMetadataChanged(@Nullable final MediaSourceTag tag) { + protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); - titleTextView.setText(getVideoTitle()); - channelTextView.setText(getUploaderName()); + titleTextView.setText(tag.getMetadata().getName()); + channelTextView.setText(tag.getMetadata().getUploaderName()); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index 476ad73ef..0426525a4 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -56,6 +56,7 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.text.CaptionStyleCompat; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.SubtitleView; +import com.nostra13.universalimageloader.core.assist.FailReason; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; @@ -428,21 +429,6 @@ public final class PopupVideoPlayer extends Service { super.destroy(); } - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { - super.onLoadingComplete(imageUri, view, loadedImage); - if (loadedImage != null) { - // rebuild notification here since remote view does not release bitmaps, causing memory leaks - notBuilder = createNotification(); - - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage); - } - - updateNotification(-1); - } - } - @Override public void onFullScreenButtonClicked() { super.onFullScreenButtonClicked(); @@ -527,6 +513,54 @@ public final class PopupVideoPlayer extends Service { }; } + /*////////////////////////////////////////////////////////////////////////// + // Thumbnail Loading + //////////////////////////////////////////////////////////////////////////*/ + + private void setDummyRemoteViewThumbnail() { + resetNotification(); + if (notRemoteView != null) { + notRemoteView.setImageViewResource(R.id.notificationCover, + R.drawable.dummy_thumbnail); + } + updateNotification(-1); + } + + @Override + public void onLoadingStarted(String imageUri, View view) { + super.onLoadingStarted(imageUri, view); + setDummyRemoteViewThumbnail(); + } + + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + super.onLoadingComplete(imageUri, view, loadedImage); + if (loadedImage == null) { + setDummyRemoteViewThumbnail(); + return; + } + + // rebuild notification here since remote view does not release bitmaps, + // causing memory leaks + resetNotification(); + if (notRemoteView != null) { + notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage); + } + updateNotification(-1); + } + + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason) { + super.onLoadingFailed(imageUri, view, failReason); + setDummyRemoteViewThumbnail(); + } + + @Override + public void onLoadingCancelled(String imageUri, View view) { + super.onLoadingCancelled(imageUri, view); + setDummyRemoteViewThumbnail(); + } + /*////////////////////////////////////////////////////////////////////////// // Activity Event Listener //////////////////////////////////////////////////////////////////////////*/ @@ -578,8 +612,8 @@ public final class PopupVideoPlayer extends Service { public void onRepeatModeChanged(int i) { super.onRepeatModeChanged(i); setRepeatModeRemote(notRemoteView, i); - updateNotification(-1); updatePlayback(); + updateNotification(-1); } @Override @@ -592,7 +626,7 @@ public final class PopupVideoPlayer extends Service { // Playback Listener //////////////////////////////////////////////////////////////////////////*/ - protected void onMetadataChanged(@Nullable final MediaSourceTag tag) { + protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); updateMetadata(); } @@ -651,12 +685,14 @@ public final class PopupVideoPlayer extends Service { public void changeState(int state) { super.changeState(state); updatePlayback(); + updateNotification(-1); } @Override public void onBlocked() { super.onBlocked(); updateNotification(R.drawable.ic_play_arrow_white); + startForeground(NOTIFICATION_ID, notBuilder.build()); } @Override @@ -664,19 +700,21 @@ public final class PopupVideoPlayer extends Service { super.onPlaying(); updateNotification(R.drawable.ic_pause_white); videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white); - lockManager.acquireWifiAndCpu(); - hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams); + + startForeground(NOTIFICATION_ID, notBuilder.build()); + lockManager.acquireWifiAndCpu(); } @Override public void onBuffering() { super.onBuffering(); updateNotification(R.drawable.ic_play_arrow_white); + startForeground(NOTIFICATION_ID, notBuilder.build()); } @Override @@ -684,10 +722,13 @@ public final class PopupVideoPlayer extends Service { super.onPaused(); updateNotification(R.drawable.ic_play_arrow_white); videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white); + lockManager.releaseWifiAndCpu(); windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams); + + stopForeground(false); } @Override @@ -695,6 +736,7 @@ public final class PopupVideoPlayer extends Service { super.onPausedSeek(); videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white); updateNotification(R.drawable.ic_play_arrow_white); + startForeground(NOTIFICATION_ID, notBuilder.build()); } @Override @@ -702,10 +744,13 @@ public final class PopupVideoPlayer extends Service { super.onCompleted(); updateNotification(R.drawable.ic_replay_white); videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white); + lockManager.releaseWifiAndCpu(); windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams); + + stopForeground(false); } @Override @@ -731,7 +776,7 @@ public final class PopupVideoPlayer extends Service { /*package-private*/ void enableVideoRenderer(final boolean enable) { final int videoRendererIndex = getRendererIndex(C.TRACK_TYPE_VIDEO); - if (trackSelector != null && videoRendererIndex != RENDERER_UNAVAILABLE) { + if (videoRendererIndex != RENDERER_UNAVAILABLE) { trackSelector.setParameters(trackSelector.buildUponParameters() .setRendererDisabled(videoRendererIndex, !enable)); } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index eaec59f65..1ca0ff4ee 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -103,7 +103,7 @@ public abstract class VideoPlayer extends BasePlayer protected boolean wasPlaying = false; - @Nullable private VideoPlaybackResolver resolver; + @NonNull final private VideoPlaybackResolver resolver; /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ @@ -154,6 +154,7 @@ public abstract class VideoPlayer extends BasePlayer public VideoPlayer(String debugTag, Context context) { super(context); this.TAG = debugTag; + this.resolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver()); } public void setup(View rootView) { @@ -236,8 +237,6 @@ public abstract class VideoPlayer extends BasePlayer trackSelector.setParameters(trackSelector.buildUponParameters() .setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context))); } - - resolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver()); } @Override @@ -292,7 +291,7 @@ public abstract class VideoPlayer extends BasePlayer 0, Menu.NONE, R.string.caption_none); captionOffItem.setOnMenuItemClickListener(menuItem -> { final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT); - if (trackSelector != null && textRendererIndex != RENDERER_UNAVAILABLE) { + if (textRendererIndex != RENDERER_UNAVAILABLE) { trackSelector.setParameters(trackSelector.buildUponParameters() .setRendererDisabled(textRendererIndex, true)); } @@ -306,7 +305,7 @@ public abstract class VideoPlayer extends BasePlayer i + 1, Menu.NONE, captionLanguage); captionItem.setOnMenuItemClickListener(menuItem -> { final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT); - if (trackSelector != null && textRendererIndex != RENDERER_UNAVAILABLE) { + if (textRendererIndex != RENDERER_UNAVAILABLE) { trackSelector.setPreferredTextLanguage(captionLanguage); trackSelector.setParameters(trackSelector.buildUponParameters() .setRendererDisabled(textRendererIndex, false)); @@ -369,7 +368,7 @@ public abstract class VideoPlayer extends BasePlayer @Override @Nullable public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) { - return resolver == null ? null : resolver.resolve(info); + return resolver.resolve(info); } /*////////////////////////////////////////////////////////////////////////// @@ -480,8 +479,7 @@ public abstract class VideoPlayer extends BasePlayer final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT); if (captionTextView == null) return; - if (trackSelector == null || trackSelector.getCurrentMappedTrackInfo() == null || - textRenderer == RENDERER_UNAVAILABLE) { + if (trackSelector.getCurrentMappedTrackInfo() == null || textRenderer == RENDERER_UNAVAILABLE) { captionTextView.setVisibility(View.GONE); return; } @@ -833,12 +831,12 @@ public abstract class VideoPlayer extends BasePlayer //////////////////////////////////////////////////////////////////////////*/ public void setPlaybackQuality(final String quality) { - if (resolver != null) resolver.setPlaybackQuality(quality); + this.resolver.setPlaybackQuality(quality); } @Nullable public String getPlaybackQuality() { - return resolver == null ? null : resolver.getPlaybackQuality(); + return resolver.getPlaybackQuality(); } public AspectRatioFrameLayout getAspectRatioFrameLayout() { diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java index f95f0e3bb..6bb556850 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.player.resolver; import android.content.Context; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.source.MediaSource; @@ -24,6 +25,7 @@ public class AudioPlaybackResolver implements PlaybackResolver { } @Override + @Nullable public MediaSource resolve(@NonNull StreamInfo info) { final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info); if (liveSource != null) return liveSource; diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java index a7d4448e4..4bd795574 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/Resolver.java @@ -1,7 +1,8 @@ package org.schabi.newpipe.player.resolver; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; public interface Resolver { - Product resolve(@NonNull Source source); + @Nullable Product resolve(@NonNull Source source); } diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java index 5aa42ce3c..8f91f4886 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java @@ -47,6 +47,7 @@ public class VideoPlaybackResolver implements PlaybackResolver { } @Override + @Nullable public MediaSource resolve(@NonNull StreamInfo info) { final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info); if (liveSource != null) return liveSource;