mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 16:40:32 +00:00
-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.
This commit is contained in:
parent
0a2dbc4688
commit
f1f5996975
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.schabi.newpipe.player.resolver;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public interface Resolver<Source, Product> {
|
||||
Product resolve(@NonNull Source source);
|
||||
@Nullable Product resolve(@NonNull Source source);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user