-Fixed memory leak due to permanent remote view bitmap references.
-Removed redundant code in popup player.
This commit is contained in:
parent
eb15c04254
commit
150c3b413a
10 changed files with 204 additions and 289 deletions
|
@ -146,7 +146,7 @@ public class BackgroundPlayer extends Service {
|
|||
private void onScreenOnOff(boolean on) {
|
||||
if (DEBUG) Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]");
|
||||
if (on) {
|
||||
if (basePlayerImpl.isPlaying() && !basePlayerImpl.isProgressLoopRunning.get()) basePlayerImpl.startProgressLoop();
|
||||
if (basePlayerImpl.isPlaying() && !basePlayerImpl.isProgressLoopRunning()) basePlayerImpl.startProgressLoop();
|
||||
} else basePlayerImpl.stopProgressLoop();
|
||||
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ public class BackgroundPlayer extends Service {
|
|||
*
|
||||
* @param drawableId if != -1, sets the drawable with that id on the play/pause button
|
||||
*/
|
||||
private void updateNotification(int drawableId) {
|
||||
private synchronized void updateNotification(int drawableId) {
|
||||
if (DEBUG) Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]");
|
||||
if (notBuilder == null) return;
|
||||
if (drawableId != -1) {
|
||||
|
@ -275,19 +275,27 @@ public class BackgroundPlayer extends Service {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initThumbnail() {
|
||||
public void initThumbnail(final String url) {
|
||||
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();
|
||||
super.initThumbnail(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||
super.onThumbnailReceived(thumbnail);
|
||||
|
||||
if (thumbnail != null) {
|
||||
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, thumbnail);
|
||||
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, thumbnail);
|
||||
videoThumbnail = thumbnail;
|
||||
|
||||
// rebuild notification here since remote view does not release bitmaps, causing memory leaks
|
||||
// remove this line to see for yourself
|
||||
notBuilder = createNotification();
|
||||
|
||||
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, videoThumbnail);
|
||||
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, videoThumbnail);
|
||||
|
||||
updateNotification(-1);
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +311,7 @@ public class BackgroundPlayer extends Service {
|
|||
FAST_FORWARD_REWIND_AMOUNT = 10000;
|
||||
}
|
||||
PROGRESS_LOOP_INTERVAL = 1000;
|
||||
basePlayerImpl.getPlayer().setVolume(1f);
|
||||
simpleExoPlayer.setVolume(1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -382,13 +390,13 @@ public class BackgroundPlayer extends Service {
|
|||
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
|
||||
super.sync(info, sortedStreamsIndex);
|
||||
|
||||
basePlayerImpl.setVideoTitle(info.name);
|
||||
basePlayerImpl.setUploaderName(info.uploader_name);
|
||||
setVideoTitle(info.name);
|
||||
setUploaderName(info.uploader_name);
|
||||
|
||||
notRemoteView.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
|
||||
notRemoteView.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
|
||||
bigNotRemoteView.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
|
||||
bigNotRemoteView.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
|
||||
notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||
notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
|
||||
bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle());
|
||||
bigNotRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName());
|
||||
updateNotification(-1);
|
||||
}
|
||||
|
||||
|
@ -436,7 +444,7 @@ public class BackgroundPlayer extends Service {
|
|||
onVideoPlayPause();
|
||||
break;
|
||||
case ACTION_OPEN_DETAIL:
|
||||
onOpenDetail(BackgroundPlayer.this, basePlayerImpl.getVideoUrl(), basePlayerImpl.getVideoTitle());
|
||||
onOpenDetail(BackgroundPlayer.this, getVideoUrl(), getVideoTitle());
|
||||
break;
|
||||
case ACTION_REPEAT:
|
||||
onRepeatClicked();
|
||||
|
@ -483,7 +491,7 @@ public class BackgroundPlayer extends Service {
|
|||
super.onPaused();
|
||||
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||
if (isProgressLoopRunning()) stopProgressLoop();
|
||||
|
||||
releaseWifiAndCpu();
|
||||
}
|
||||
|
|
|
@ -30,12 +30,9 @@ import android.content.SharedPreferences;
|
|||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
|
@ -67,15 +64,14 @@ import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvicto
|
|||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||
|
||||
import org.schabi.newpipe.Downloader;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.player.playback.PlaybackManager;
|
||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||
import org.schabi.newpipe.player.playback.PlaybackManager;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
|
@ -89,7 +85,19 @@ import java.util.ArrayList;
|
|||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.disposables.SerialDisposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.functions.Predicate;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Base for the players, joining the common properties
|
||||
|
@ -101,7 +109,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
AudioManager.OnAudioFocusChangeListener, PlaybackListener {
|
||||
// TODO: Check api version for deprecated audio manager methods
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
public static final boolean DEBUG = true;
|
||||
public static final String TAG = "BasePlayer";
|
||||
|
||||
protected Context context;
|
||||
|
@ -134,7 +142,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
protected String videoUrl = "";
|
||||
protected String videoTitle = "";
|
||||
protected String videoThumbnailUrl = "";
|
||||
protected long videoStartPos = -1;
|
||||
protected String uploaderName = "";
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -144,8 +151,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
protected PlaybackManager playbackManager;
|
||||
protected PlayQueue playQueue;
|
||||
|
||||
protected int restoreQueueIndex;
|
||||
protected long restoreWindowPos;
|
||||
protected int queueStartPos = 0;
|
||||
protected long videoStartPos = -1;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
|
@ -157,21 +164,19 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
protected SimpleExoPlayer simpleExoPlayer;
|
||||
protected boolean isPrepared = false;
|
||||
|
||||
protected MediaSource mediaSource;
|
||||
protected CacheDataSourceFactory cacheDataSourceFactory;
|
||||
protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
|
||||
protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||
|
||||
protected int PROGRESS_LOOP_INTERVAL = 100;
|
||||
protected AtomicBoolean isProgressLoopRunning = new AtomicBoolean();
|
||||
protected Handler progressLoop;
|
||||
protected Runnable progressUpdate;
|
||||
protected Disposable progressUpdateReactor;
|
||||
|
||||
protected SerialDisposable thumbnailReactor;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public BasePlayer(Context context) {
|
||||
this.context = context;
|
||||
this.progressLoop = new Handler();
|
||||
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
this.audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
|
||||
|
||||
|
@ -184,6 +189,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
this.intentFilter = new IntentFilter();
|
||||
setupBroadcastReceiver(intentFilter);
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
|
||||
this.thumbnailReactor = new SerialDisposable();
|
||||
}
|
||||
|
||||
public void setup() {
|
||||
|
@ -223,23 +230,31 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
simpleExoPlayer.addListener(this);
|
||||
}
|
||||
|
||||
public void initListeners() {
|
||||
progressUpdate = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//if(DEBUG) Log.d(TAG, "progressUpdate run() called");
|
||||
onUpdateProgress((int) simpleExoPlayer.getCurrentPosition(), (int) simpleExoPlayer.getDuration(), simpleExoPlayer.getBufferedPercentage());
|
||||
if (isProgressLoopRunning.get()) progressLoop.postDelayed(this, PROGRESS_LOOP_INTERVAL);
|
||||
}
|
||||
};
|
||||
public void initListeners() {}
|
||||
|
||||
protected Disposable getProgressReactor() {
|
||||
return Observable.interval(PROGRESS_LOOP_INTERVAL, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.filter(new Predicate<Long>() {
|
||||
@Override
|
||||
public boolean test(@NonNull Long aLong) throws Exception {
|
||||
return isProgressLoopRunning();
|
||||
}
|
||||
})
|
||||
.subscribe(new Consumer<Long>() {
|
||||
@Override
|
||||
public void accept(Long aLong) throws Exception {
|
||||
triggerProgressUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void handleIntent(Intent intent) {
|
||||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||
if (intent == null) return;
|
||||
|
||||
restoreQueueIndex = intent.getIntExtra(RESTORE_QUEUE_INDEX, 0);
|
||||
restoreWindowPos = intent.getLongExtra(START_POSITION, 0);
|
||||
queueStartPos = intent.getIntExtra(RESTORE_QUEUE_INDEX, 0);
|
||||
videoStartPos = intent.getLongExtra(START_POSITION, 0);
|
||||
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
|
||||
|
||||
switch (intent.getStringExtra(INTENT_TYPE)) {
|
||||
|
@ -254,7 +269,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleExternalPlaylistIntent(Intent intent) {
|
||||
final int serviceId = intent.getIntExtra(ExternalPlayQueue.SERVICE_ID, -1);
|
||||
|
@ -286,21 +300,37 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
playbackManager = new PlaybackManager(this, playQueue);
|
||||
}
|
||||
|
||||
public void initThumbnail() {
|
||||
if (DEBUG) Log.d(TAG, "initThumbnail() called");
|
||||
videoThumbnail = null;
|
||||
if (videoThumbnailUrl == null || videoThumbnailUrl.isEmpty()) return;
|
||||
ImageLoader.getInstance().resume();
|
||||
ImageLoader.getInstance().loadImage(videoThumbnailUrl, new SimpleImageLoadingListener() {
|
||||
public void initThumbnail(final String url) {
|
||||
final Callable<Bitmap> bitmapCallable = new Callable<Bitmap>() {
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
if (simpleExoPlayer == null) return;
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onLoadingComplete() called with: imageUri = [" + imageUri + "], view = [" + view + "], loadedImage = [" + loadedImage + "]");
|
||||
videoThumbnail = loadedImage;
|
||||
onThumbnailReceived(loadedImage);
|
||||
public Bitmap call() throws Exception {
|
||||
return ImageLoader.getInstance().loadImageSync(url);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Single.fromCallable(bitmapCallable)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new SingleObserver<Bitmap>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
thumbnailReactor.set(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull Bitmap bitmap) {
|
||||
onThumbnailReceived(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(TAG, "Thumbnail Fetch Failed.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||
if (DEBUG) Log.d(TAG, "onThumbnailReceived() called with: thumbnail = [" + thumbnail + "]");
|
||||
}
|
||||
|
||||
public void destroyPlayer() {
|
||||
|
@ -309,7 +339,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
simpleExoPlayer.stop();
|
||||
simpleExoPlayer.release();
|
||||
}
|
||||
if (progressLoop != null && isProgressLoopRunning.get()) stopProgressLoop();
|
||||
if (isProgressLoopRunning()) stopProgressLoop();
|
||||
if (audioManager != null) {
|
||||
audioManager.abandonAudioFocus(this);
|
||||
audioManager = null;
|
||||
|
@ -320,7 +350,11 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
if (DEBUG) Log.d(TAG, "destroy() called");
|
||||
destroyPlayer();
|
||||
unregisterBroadcastReceiver();
|
||||
|
||||
thumbnailReactor.dispose();
|
||||
thumbnailReactor = null;
|
||||
videoThumbnail = null;
|
||||
|
||||
simpleExoPlayer = null;
|
||||
}
|
||||
|
||||
|
@ -469,19 +503,19 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
|
||||
public void onLoading() {
|
||||
if (DEBUG) Log.d(TAG, "onLoading() called");
|
||||
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||
if (!isProgressLoopRunning()) startProgressLoop();
|
||||
}
|
||||
|
||||
public void onPlaying() {
|
||||
if (DEBUG) Log.d(TAG, "onPlaying() called");
|
||||
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||
if (!isProgressLoopRunning()) startProgressLoop();
|
||||
}
|
||||
|
||||
public void onBuffering() {
|
||||
}
|
||||
|
||||
public void onPaused() {
|
||||
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||
if (isProgressLoopRunning()) stopProgressLoop();
|
||||
}
|
||||
|
||||
public void onPausedSeek() {
|
||||
|
@ -489,7 +523,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
|
||||
public void onCompleted() {
|
||||
if (DEBUG) Log.d(TAG, "onCompleted() called");
|
||||
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||
if (isProgressLoopRunning()) stopProgressLoop();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -524,22 +558,25 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
Log.w(TAG, "onTracksChanged() called, unsupported operation. Is this expected?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
if (DEBUG) Log.d(TAG, "playbackParameters(), speed: " + playbackParameters.speed + ", pitch: " + playbackParameters.pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
if (DEBUG) Log.d(TAG, "onLoadingChanged() called with: isLoading = [" + isLoading + "]");
|
||||
|
||||
if (!isLoading && getCurrentState() == STATE_PAUSED && isProgressLoopRunning.get()) stopProgressLoop();
|
||||
else if (isLoading && !isProgressLoopRunning.get()) startProgressLoop();
|
||||
if (!isLoading && getCurrentState() == STATE_PAUSED && isProgressLoopRunning()) stopProgressLoop();
|
||||
else if (isLoading && !isProgressLoopRunning()) startProgressLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -595,6 +632,11 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
playbackManager.refresh(newIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int i) {
|
||||
if (DEBUG) Log.d(TAG, "onRepeatModeChanged() called with: mode = [" + i + "]");
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -614,13 +656,13 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
if (simpleExoPlayer == null) return;
|
||||
if (DEBUG) Log.d(TAG, "Unblocking...");
|
||||
|
||||
if (restoreQueueIndex != playQueue.getIndex()) {
|
||||
restoreQueueIndex = playQueue.getIndex();
|
||||
restoreWindowPos = 0;
|
||||
if (queueStartPos != playQueue.getIndex()) {
|
||||
queueStartPos = playQueue.getIndex();
|
||||
videoStartPos = 0;
|
||||
}
|
||||
|
||||
simpleExoPlayer.prepare(playbackManager.getMediaSource());
|
||||
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), restoreWindowPos);
|
||||
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), videoStartPos);
|
||||
simpleExoPlayer.setPlayWhenReady(false);
|
||||
}
|
||||
|
||||
|
@ -633,15 +675,16 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
videoThumbnailUrl = info.thumbnail_url;
|
||||
videoTitle = info.name;
|
||||
|
||||
initThumbnail();
|
||||
initThumbnail(videoThumbnailUrl);
|
||||
|
||||
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) {
|
||||
if (DEBUG) Log.w(TAG, "Rewinding to correct window");
|
||||
if (simpleExoPlayer.getCurrentTimeline().getWindowCount() > playbackManager.getCurrentSourceIndex()) {
|
||||
simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex());
|
||||
} else {
|
||||
Toast.makeText(context, "Play Queue out of sync", Toast.LENGTH_SHORT).show();
|
||||
simpleExoPlayer.seekToDefaultPosition();
|
||||
if (DEBUG) Log.w(TAG, "Play Queue out of sync");
|
||||
playbackManager.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -674,26 +717,12 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
public void onVideoPlayPause() {
|
||||
if (DEBUG) Log.d(TAG, "onVideoPlayPause() called");
|
||||
|
||||
if (currentState == STATE_COMPLETED) {
|
||||
onVideoPlayPauseRepeat();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlaying()) audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||
else audioManager.abandonAudioFocus(this);
|
||||
|
||||
simpleExoPlayer.setPlayWhenReady(!isPlaying());
|
||||
}
|
||||
|
||||
public void onVideoPlayPauseRepeat() {
|
||||
if (DEBUG) Log.d(TAG, "onVideoPlayPauseRepeat() called");
|
||||
changeState(STATE_LOADING);
|
||||
setVideoStartPos(0);
|
||||
simpleExoPlayer.seekTo(0);
|
||||
simpleExoPlayer.setPlayWhenReady(true);
|
||||
audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||
}
|
||||
|
||||
public void onFastRewind() {
|
||||
if (DEBUG) Log.d(TAG, "onFastRewind() called");
|
||||
seekBy(-FAST_FORWARD_REWIND_AMOUNT);
|
||||
|
@ -704,10 +733,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
seekBy(FAST_FORWARD_REWIND_AMOUNT);
|
||||
}
|
||||
|
||||
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||
if (DEBUG) Log.d(TAG, "onThumbnailReceived() called with: thumbnail = [" + thumbnail + "]");
|
||||
}
|
||||
|
||||
public void seekBy(int milliSeconds) {
|
||||
if (DEBUG) Log.d(TAG, "seekBy() called with: milliSeconds = [" + milliSeconds + "]");
|
||||
if (simpleExoPlayer == null || (isCompleted() && milliSeconds > 0) || ((milliSeconds < 0 && simpleExoPlayer.getCurrentPosition() == 0)))
|
||||
|
@ -746,14 +771,13 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
}
|
||||
|
||||
protected void startProgressLoop() {
|
||||
progressLoop.removeCallbacksAndMessages(null);
|
||||
isProgressLoopRunning.set(true);
|
||||
progressLoop.post(progressUpdate);
|
||||
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
|
||||
progressUpdateReactor = getProgressReactor();
|
||||
}
|
||||
|
||||
protected void stopProgressLoop() {
|
||||
isProgressLoopRunning.set(false);
|
||||
progressLoop.removeCallbacksAndMessages(null);
|
||||
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
|
||||
progressUpdateReactor = null;
|
||||
}
|
||||
|
||||
protected void tryDeleteCacheFiles(Context context) {
|
||||
|
@ -902,4 +926,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
|||
public boolean isPlayerReady() {
|
||||
return currentState == STATE_PLAYING || currentState == STATE_COMPLETED || currentState == STATE_PAUSED;
|
||||
}
|
||||
|
||||
public boolean isProgressLoopRunning() {
|
||||
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,7 +217,7 @@ public class MainVideoPlayer extends Activity {
|
|||
MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
|
||||
gestureDetector = new GestureDetector(context, listener);
|
||||
gestureDetector.setIsLongpressEnabled(false);
|
||||
playerImpl.getRootView().setOnTouchListener(listener);
|
||||
getRootView().setOnTouchListener(listener);
|
||||
|
||||
repeatButton.setOnClickListener(this);
|
||||
playPauseButton.setOnClickListener(this);
|
||||
|
@ -252,8 +252,10 @@ public class MainVideoPlayer extends Activity {
|
|||
|
||||
@Override
|
||||
public void onFullScreenButtonClicked() {
|
||||
super.onFullScreenButtonClicked();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||
if (playerImpl.getPlayer() == null) return;
|
||||
if (simpleExoPlayer == null) return;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !PermissionHelper.checkSystemAlertWindowPermission(MainVideoPlayer.this)) {
|
||||
|
@ -261,11 +263,11 @@ public class MainVideoPlayer extends Activity {
|
|||
return;
|
||||
}
|
||||
|
||||
context.startService(NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, playerImpl));
|
||||
if (playerImpl != null) playerImpl.destroyPlayer();
|
||||
context.startService(NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, this));
|
||||
destroyPlayer();
|
||||
|
||||
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
||||
MainVideoPlayer.this.finish();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -302,10 +304,10 @@ public class MainVideoPlayer extends Activity {
|
|||
|
||||
if (getCurrentState() != STATE_COMPLETED) {
|
||||
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
||||
animateView(playerImpl.getControlsRoot(), true, 300, 0, new Runnable() {
|
||||
animateView(getControlsRoot(), true, 300, 0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (getCurrentState() == STATE_PLAYING && !playerImpl.isSomePopupMenuVisible()) {
|
||||
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
|
||||
hideControls(300, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
}
|
||||
}
|
||||
|
@ -321,7 +323,7 @@ public class MainVideoPlayer extends Activity {
|
|||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
super.onStopTrackingTouch(seekBar);
|
||||
if (playerImpl.wasPlaying()) {
|
||||
if (wasPlaying()) {
|
||||
hideControls(100, 0);
|
||||
}
|
||||
}
|
||||
|
@ -457,11 +459,6 @@ public class MainVideoPlayer extends Activity {
|
|||
public ImageButton getPlayPauseButton() {
|
||||
return playPauseButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int i) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
|
||||
|
|
|
@ -67,6 +67,10 @@ import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||
import org.schabi.newpipe.player.playback.PlaybackManager;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
|
@ -115,14 +119,7 @@ public class PopupVideoPlayer extends Service {
|
|||
private float minimumWidth, minimumHeight;
|
||||
private float maximumWidth, maximumHeight;
|
||||
|
||||
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder notBuilder;
|
||||
private RemoteViews notRemoteView;
|
||||
|
||||
|
||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||
|
||||
private VideoPlayerImpl playerImpl;
|
||||
private Disposable currentWorker;
|
||||
|
@ -148,7 +145,6 @@ public class PopupVideoPlayer extends Service {
|
|||
if (playerImpl.getPlayer() == null) initPopup();
|
||||
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
|
||||
|
||||
if (imageLoader != null) imageLoader.clearMemoryCache();
|
||||
if (intent.getStringExtra(Constants.KEY_URL) != null) {
|
||||
final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
|
||||
final String url = intent.getStringExtra(Constants.KEY_URL);
|
||||
|
@ -245,61 +241,6 @@ public class PopupVideoPlayer extends Service {
|
|||
windowManager.addView(rootView, windowLayoutParams);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Notification
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private NotificationCompat.Builder createNotification() {
|
||||
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_popup_notification);
|
||||
|
||||
if (playerImpl.getVideoThumbnail() == null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
|
||||
else notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getVideoThumbnail());
|
||||
|
||||
notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle());
|
||||
notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName());
|
||||
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationStop,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationContent,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_OPEN_DETAIL), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
notRemoteView.setOnClickPendingIntent(R.id.notificationRepeat,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
switch (playerImpl.simpleExoPlayer.getRepeatMode()) {
|
||||
case Player.REPEAT_MODE_OFF:
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 77);
|
||||
break;
|
||||
case Player.REPEAT_MODE_ONE:
|
||||
//todo change image
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 168);
|
||||
break;
|
||||
case Player.REPEAT_MODE_ALL:
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 255);
|
||||
break;
|
||||
}
|
||||
|
||||
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.ic_play_arrow_white)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContent(notRemoteView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the notification, and the play/pause button in it.
|
||||
* Used for changes on the remoteView
|
||||
*
|
||||
* @param drawableId if != -1, sets the drawable with that id on the play/pause button
|
||||
*/
|
||||
private void updateNotification(int drawableId) {
|
||||
if (DEBUG) Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]");
|
||||
if (notBuilder == null || notRemoteView == null) return;
|
||||
if (drawableId != -1) notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
|
||||
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Misc
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -400,25 +341,22 @@ public class PopupVideoPlayer extends Service {
|
|||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||
super.onThumbnailReceived(thumbnail);
|
||||
if (thumbnail != null) {
|
||||
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, thumbnail);
|
||||
updateNotification(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFullScreenButtonClicked() {
|
||||
super.onFullScreenButtonClicked();
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||
Intent intent;
|
||||
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
|
||||
intent = NavigationHelper.getOpenVideoPlayerIntent(context, MainVideoPlayer.class, playerImpl);
|
||||
if (!playerImpl.isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
||||
intent = NavigationHelper.getOpenVideoPlayerIntent(context, MainVideoPlayer.class, this);
|
||||
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
} else {
|
||||
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
|
||||
|
@ -429,31 +367,10 @@ public class PopupVideoPlayer extends Service {
|
|||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
if (playerImpl != null) playerImpl.destroyPlayer();
|
||||
destroyPlayer();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatClicked() {
|
||||
super.onRepeatClicked();
|
||||
switch (simpleExoPlayer.getRepeatMode()) {
|
||||
case Player.REPEAT_MODE_OFF:
|
||||
// Drawable didn't work on low API :/
|
||||
//notRemoteView.setImageViewResource(R.id.notificationRepeat, R.drawable.ic_repeat_disabled_white);
|
||||
// Set the icon to 30% opacity - 255 (max) * .3
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 77);
|
||||
break;
|
||||
case Player.REPEAT_MODE_ONE:
|
||||
// todo change image
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 168);
|
||||
break;
|
||||
case Player.REPEAT_MODE_ALL:
|
||||
notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, 255);
|
||||
break;
|
||||
}
|
||||
updateNotification(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(PopupMenu menu) {
|
||||
super.onDismiss(menu);
|
||||
|
@ -469,7 +386,7 @@ public class PopupVideoPlayer extends Service {
|
|||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
super.onStopTrackingTouch(seekBar);
|
||||
if (playerImpl.wasPlaying()) {
|
||||
if (wasPlaying()) {
|
||||
hideControls(100, 0);
|
||||
}
|
||||
}
|
||||
|
@ -507,13 +424,13 @@ public class PopupVideoPlayer extends Service {
|
|||
onVideoClose();
|
||||
break;
|
||||
case ACTION_PLAY_PAUSE:
|
||||
playerImpl.onVideoPlayPause();
|
||||
onVideoPlayPause();
|
||||
break;
|
||||
case ACTION_OPEN_DETAIL:
|
||||
onOpenDetail(PopupVideoPlayer.this, playerImpl.getVideoUrl(), playerImpl.getVideoTitle());
|
||||
onOpenDetail(PopupVideoPlayer.this, getVideoUrl(), getVideoTitle());
|
||||
break;
|
||||
case ACTION_REPEAT:
|
||||
playerImpl.onRepeatClicked();
|
||||
onRepeatClicked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -524,38 +441,32 @@ public class PopupVideoPlayer extends Service {
|
|||
@Override
|
||||
public void onLoading() {
|
||||
super.onLoading();
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaying() {
|
||||
super.onPlaying();
|
||||
updateNotification(R.drawable.ic_pause_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuffering() {
|
||||
super.onBuffering();
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPaused() {
|
||||
super.onPaused();
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
showAndAnimateControl(R.drawable.ic_play_arrow_white, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPausedSeek() {
|
||||
super.onPausedSeek();
|
||||
updateNotification(R.drawable.ic_play_arrow_white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
super.onCompleted();
|
||||
updateNotification(R.drawable.ic_replay_white);
|
||||
showAndAnimateControl(R.drawable.ic_replay_white, false);
|
||||
}
|
||||
|
||||
|
@ -564,10 +475,6 @@ public class PopupVideoPlayer extends Service {
|
|||
public TextView getResizingIndicator() {
|
||||
return resizingIndicator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int i) {
|
||||
}
|
||||
}
|
||||
|
||||
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
|
||||
|
@ -746,49 +653,16 @@ public class PopupVideoPlayer extends Service {
|
|||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public void onReceive(StreamInfo info) {
|
||||
playerImpl.setVideoTitle(info.name);
|
||||
playerImpl.setVideoUrl(info.url);
|
||||
playerImpl.setVideoThumbnailUrl(info.thumbnail_url);
|
||||
playerImpl.setUploaderName(info.uploader_name);
|
||||
|
||||
playerImpl.setVideoStreamsList(new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false)));
|
||||
playerImpl.setAudioStream(ListHelper.getHighestQualityAudio(info.audio_streams));
|
||||
|
||||
int defaultResolution = ListHelper.getPopupDefaultResolutionIndex(context, playerImpl.getVideoStreamsList());
|
||||
playerImpl.setSelectedIndexStream(defaultResolution);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "FetcherHandler.StreamExtractor: chosen = "
|
||||
+ MediaFormat.getNameById(info.video_streams.get(defaultResolution).format) + " "
|
||||
+ info.video_streams.get(defaultResolution).resolution + " > "
|
||||
+ info.video_streams.get(defaultResolution).url);
|
||||
}
|
||||
|
||||
public void onReceive(final StreamInfo info) {
|
||||
if (info.start_position > 0) playerImpl.setVideoStartPos(info.start_position * 1000);
|
||||
else playerImpl.setVideoStartPos(-1);
|
||||
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
playerImpl.playQueue = new SinglePlayQueue(info, PlayQueueItem.DEFAULT_QUALITY);
|
||||
playerImpl.playQueue.init();
|
||||
}
|
||||
});
|
||||
|
||||
imageLoader.resume();
|
||||
imageLoader.loadImage(info.thumbnail_url, displayImageOptions, new SimpleImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingComplete(final String imageUri, View view, final Bitmap loadedImage) {
|
||||
if (playerImpl == null || playerImpl.getPlayer() == null) return;
|
||||
if (DEBUG) Log.d(TAG, "FetcherHandler.imageLoader.onLoadingComplete() called with: imageUri = [" + imageUri + "]");
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
playerImpl.setVideoThumbnail(loadedImage);
|
||||
if (loadedImage != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
|
||||
updateNotification(-1);
|
||||
}
|
||||
});
|
||||
playerImpl.playbackManager = new PlaybackManager(playerImpl, playerImpl.playQueue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
public void onLoading() {
|
||||
if (DEBUG) Log.d(TAG, "onLoading() called");
|
||||
|
||||
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||
if (!isProgressLoopRunning()) startProgressLoop();
|
||||
|
||||
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
||||
animateView(controlsRoot, false, 300);
|
||||
|
@ -331,7 +331,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
@Override
|
||||
public void onPlaying() {
|
||||
if (DEBUG) Log.d(TAG, "onPlaying() called");
|
||||
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||
if (!isProgressLoopRunning()) startProgressLoop();
|
||||
showAndAnimateControl(-1, true);
|
||||
loadingPanel.setVisibility(View.GONE);
|
||||
showControlsThenHide();
|
||||
|
@ -362,7 +362,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
public void onCompleted() {
|
||||
if (DEBUG) Log.d(TAG, "onCompleted() called");
|
||||
|
||||
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||
if (isProgressLoopRunning()) stopProgressLoop();
|
||||
|
||||
showControls(500);
|
||||
animateView(endScreen, true, 800);
|
||||
|
@ -445,22 +445,15 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoPlayPauseRepeat() {
|
||||
if (DEBUG) Log.d(TAG, "onVideoPlayPauseRepeat() called");
|
||||
if (qualityChanged) {
|
||||
setVideoStartPos(0);
|
||||
//play(true);
|
||||
} else super.onVideoPlayPauseRepeat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||
super.onThumbnailReceived(thumbnail);
|
||||
if (thumbnail != null) endScreen.setImageBitmap(thumbnail);
|
||||
}
|
||||
|
||||
protected abstract void onFullScreenButtonClicked();
|
||||
protected void onFullScreenButtonClicked() {
|
||||
if (!isPlayerReady()) return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFastRewind() {
|
||||
|
@ -501,8 +494,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
|
||||
if (selectedIndexStream == menuItem.getItemId()) return true;
|
||||
|
||||
restoreQueueIndex = playQueue.getIndex();
|
||||
restoreWindowPos = simpleExoPlayer.getCurrentPosition();
|
||||
queueStartPos = playQueue.getIndex();
|
||||
videoStartPos = simpleExoPlayer.getCurrentPosition();
|
||||
playbackManager.updateCurrent(menuItem.getItemId());
|
||||
|
||||
qualityTextView.setText(menuItem.getTitle());
|
||||
|
@ -580,7 +573,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
|||
animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200);
|
||||
|
||||
if (getCurrentState() == STATE_PAUSED_SEEK) changeState(STATE_BUFFERING);
|
||||
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||
if (!isProgressLoopRunning()) startProgressLoop();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.schabi.newpipe.playlist.PlayQueue;
|
|||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.SwapEvent;
|
||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -91,7 +91,9 @@ public class PlaybackManager {
|
|||
|
||||
public void report(final Exception error) {
|
||||
// ignore error checking for now, just remove the current index
|
||||
if (error == null || !tryBlock()) return;
|
||||
if (error == null) return;
|
||||
|
||||
tryBlock();
|
||||
|
||||
final int index = playQueue.getIndex();
|
||||
playQueue.remove(index);
|
||||
|
@ -101,7 +103,7 @@ public class PlaybackManager {
|
|||
}
|
||||
|
||||
public void updateCurrent(final int newSortedStreamsIndex) {
|
||||
if (!tryBlock()) return;
|
||||
tryBlock();
|
||||
|
||||
PlayQueueItem item = playQueue.getCurrent();
|
||||
item.setSortedQualityIndex(newSortedStreamsIndex);
|
||||
|
@ -110,6 +112,13 @@ public class PlaybackManager {
|
|||
load();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
tryBlock();
|
||||
|
||||
resetSources();
|
||||
load();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||
if (disposables != null) disposables.dispose();
|
||||
|
@ -143,8 +152,8 @@ public class PlaybackManager {
|
|||
switch (event.type()) {
|
||||
case INIT:
|
||||
isBlocked = true;
|
||||
break;
|
||||
case APPEND:
|
||||
load();
|
||||
break;
|
||||
case SELECT:
|
||||
onSelect();
|
||||
|
@ -153,10 +162,9 @@ public class PlaybackManager {
|
|||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||
remove(removeEvent.index());
|
||||
break;
|
||||
case SWAP:
|
||||
final SwapEvent swapEvent = (SwapEvent) event;
|
||||
swap(swapEvent.getFrom(), swapEvent.getTo());
|
||||
load();
|
||||
case MOVE:
|
||||
final MoveEvent moveEvent = (MoveEvent) event;
|
||||
move(moveEvent.getFrom(), moveEvent.getTo());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -167,6 +175,8 @@ public class PlaybackManager {
|
|||
playQueue.fetch();
|
||||
} else if (playQueue.isEmpty()) {
|
||||
playbackListener.shutdown();
|
||||
} else {
|
||||
load(); // All event warrants a load
|
||||
}
|
||||
|
||||
if (playQueueReactor != null) playQueueReactor.request(1);
|
||||
|
@ -176,9 +186,7 @@ public class PlaybackManager {
|
|||
public void onError(@NonNull Throwable e) {}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
dispose();
|
||||
}
|
||||
public void onComplete() {}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -214,21 +222,26 @@ public class PlaybackManager {
|
|||
|
||||
/*
|
||||
* Responds to a SELECT event.
|
||||
* If the selected item is already loaded, then we simply synchronize and
|
||||
*
|
||||
* If the player is being blocked, then nothing should happen.
|
||||
*
|
||||
* Otherwise:
|
||||
*
|
||||
* When the selected item is already loaded, then we simply synchronize and
|
||||
* start loading some more items.
|
||||
*
|
||||
* If the current item has not been fully loaded, then the player will be
|
||||
* When the current item has not been fully loaded, then the player will be
|
||||
* blocked. The sources will be reset and reloaded, to conserve memory.
|
||||
* */
|
||||
private void onSelect() {
|
||||
if (isCurrentIndexLoaded() && !isBlocked) {
|
||||
if (isBlocked) return;
|
||||
|
||||
if (isCurrentIndexLoaded()) {
|
||||
sync();
|
||||
} else {
|
||||
tryBlock();
|
||||
resetSources();
|
||||
}
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
private void sync() {
|
||||
|
@ -249,6 +262,7 @@ public class PlaybackManager {
|
|||
final int currentIndex = playQueue.getIndex();
|
||||
final PlayQueueItem currentItem = playQueue.get(currentIndex);
|
||||
if (currentItem != null) load(currentItem);
|
||||
else return;
|
||||
|
||||
// The rest are just for seamless playback
|
||||
final int leftBound = Math.max(0, currentIndex - WINDOW_SIZE);
|
||||
|
@ -270,7 +284,6 @@ public class PlaybackManager {
|
|||
return;
|
||||
}
|
||||
|
||||
if (disposables.size() > 8) disposables.clear();
|
||||
disposables.add(d);
|
||||
}
|
||||
|
||||
|
@ -328,7 +341,7 @@ public class PlaybackManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void swap(final int source, final int target) {
|
||||
private void move(final int source, final int target) {
|
||||
final int sourceIndex = sourceToQueueIndex.indexOf(source);
|
||||
final int targetIndex = sourceToQueueIndex.indexOf(target);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import org.schabi.newpipe.playlist.events.InitEvent;
|
|||
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
|
||||
import org.schabi.newpipe.playlist.events.RemoveEvent;
|
||||
import org.schabi.newpipe.playlist.events.SelectEvent;
|
||||
import org.schabi.newpipe.playlist.events.SwapEvent;
|
||||
import org.schabi.newpipe.playlist.events.MoveEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
@ -28,7 +28,7 @@ public abstract class PlayQueue implements Serializable {
|
|||
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
|
||||
private final int INDEX_CHANGE_DEBOUNCE = 350;
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
public static final boolean DEBUG = true;
|
||||
|
||||
private final ArrayList<PlayQueueItem> streams;
|
||||
private final AtomicInteger queueIndex;
|
||||
|
@ -178,7 +178,7 @@ public abstract class PlayQueue implements Serializable {
|
|||
queueIndex.set(newIndex);
|
||||
}
|
||||
|
||||
broadcast(new SwapEvent(source, target));
|
||||
broadcast(new MoveEvent(source, target));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package org.schabi.newpipe.playlist.events;
|
||||
|
||||
|
||||
public class SwapEvent implements PlayQueueMessage {
|
||||
public class MoveEvent implements PlayQueueMessage {
|
||||
final private int from;
|
||||
final private int to;
|
||||
|
||||
@Override
|
||||
public PlayQueueEvent type() {
|
||||
return PlayQueueEvent.SWAP;
|
||||
return PlayQueueEvent.MOVE;
|
||||
}
|
||||
|
||||
public SwapEvent(final int from, final int to) {
|
||||
public MoveEvent(final int from, final int to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
|
@ -16,6 +16,6 @@ public enum PlayQueueEvent {
|
|||
REMOVE,
|
||||
|
||||
// sent when two streams swap place in the play queue
|
||||
SWAP
|
||||
MOVE
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.schabi.newpipe.playlist.events;
|
||||
|
||||
public interface PlayQueueMessage {
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface PlayQueueMessage extends Serializable {
|
||||
PlayQueueEvent type();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue