-Fixed popup player not playing in foreground.

-Fixed activity binder memory leak in popup and background players.
-Fixed out of bound window after removing last item on play queue.
-Fixed MediaSourceManager continues to process update after disposed.
-Changed play queue append to shuffle if queue is already shuffled.
This commit is contained in:
John Zhen Mo 2017-10-26 13:42:50 -07:00
parent 052c9a9869
commit c6e759a94c
13 changed files with 149 additions and 105 deletions

View File

@ -27,7 +27,6 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager; import android.os.PowerManager;
@ -84,12 +83,6 @@ public final class BackgroundPlayer extends Service {
private PlayerEventListener activityListener; private PlayerEventListener activityListener;
private IBinder mBinder; private IBinder mBinder;
class LocalBinder extends Binder {
BasePlayerImpl getBackgroundPlayerInstance() {
return BackgroundPlayer.this.basePlayerImpl;
}
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Notification // Notification
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -116,10 +109,10 @@ public final class BackgroundPlayer extends Service {
wifiManager = ((WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE)); wifiManager = ((WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE));
ThemeHelper.setTheme(this); ThemeHelper.setTheme(this);
basePlayerImpl = new BasePlayerImpl(this); basePlayerImpl = new BasePlayerImpl(getApplicationContext());
basePlayerImpl.setup(); basePlayerImpl.setup();
mBinder = new LocalBinder(); mBinder = new PlayerServiceBinder(basePlayerImpl);
shouldUpdateOnProgress = true; shouldUpdateOnProgress = true;
} }
@ -155,16 +148,19 @@ public final class BackgroundPlayer extends Service {
} }
private void onClose() { private void onClose() {
stopForeground(true); if (DEBUG) Log.d(TAG, "onClose() called");
releaseWifiAndCpu(); releaseWifiAndCpu();
if (basePlayerImpl != null) { if (basePlayerImpl != null) {
basePlayerImpl.stopActivityBinding(); basePlayerImpl.stopActivityBinding();
basePlayerImpl.destroy(); basePlayerImpl.destroy();
} }
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
basePlayerImpl = null;
mBinder = null; mBinder = null;
basePlayerImpl = null;
stopForeground(true);
stopSelf(); stopSelf();
} }
@ -322,6 +318,7 @@ public final class BackgroundPlayer extends Service {
updateNotification(-1); updateNotification(-1);
} }
clearThumbnailCache();
} }
@Override @Override
@ -460,8 +457,7 @@ public final class BackgroundPlayer extends Service {
@Override @Override
public void shutdown() { public void shutdown() {
super.shutdown(); super.shutdown();
stopActivityBinding(); onClose();
stopSelf();
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -538,7 +534,7 @@ public final class BackgroundPlayer extends Service {
onVideoPlayPause(); onVideoPlayPause();
break; break;
case ACTION_OPEN_DETAIL: case ACTION_OPEN_DETAIL:
openControl(BackgroundPlayer.this); openControl(getApplicationContext());
break; break;
case ACTION_REPEAT: case ACTION_REPEAT:
onRepeatClicked(); onRepeatClicked();

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.player; package org.schabi.newpipe.player;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -19,12 +18,6 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity {
return getResources().getString(R.string.title_activity_background_player); return getResources().getString(R.string.title_activity_background_player);
} }
@Override
public BasePlayer playerFrom(IBinder binder) {
final BackgroundPlayer.LocalBinder mLocalBinder = (BackgroundPlayer.LocalBinder) binder;
return mLocalBinder.getBackgroundPlayerInstance();
}
@Override @Override
public Intent getBindIntent() { public Intent getBindIntent() {
return new Intent(this, BackgroundPlayer.class); return new Intent(this, BackgroundPlayer.class);

View File

@ -315,6 +315,8 @@ public abstract class BasePlayer implements Player.EventListener,
public void destroy() { public void destroy() {
if (DEBUG) Log.d(TAG, "destroy() called"); if (DEBUG) Log.d(TAG, "destroy() called");
destroyPlayer(); destroyPlayer();
clearThumbnailCache();
unregisterBroadcastReceiver();
if (playQueue != null) { if (playQueue != null) {
playQueue.dispose(); playQueue.dispose();
@ -325,8 +327,6 @@ public abstract class BasePlayer implements Player.EventListener,
playbackManager = null; playbackManager = null;
} }
unregisterBroadcastReceiver();
trackSelector = null; trackSelector = null;
simpleExoPlayer = null; simpleExoPlayer = null;
} }
@ -902,6 +902,10 @@ public abstract class BasePlayer implements Player.EventListener,
} }
} }
protected void clearThumbnailCache() {
ImageLoader.getInstance().clearMemoryCache();
}
protected void startProgressLoop() { protected void startProgressLoop() {
if (progressUpdateReactor != null) progressUpdateReactor.dispose(); if (progressUpdateReactor != null) progressUpdateReactor.dispose();
progressUpdateReactor = getProgressReactor(); progressUpdateReactor = getProgressReactor();

View File

@ -20,8 +20,10 @@
package org.schabi.newpipe.player; package org.schabi.newpipe.player;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
@ -44,6 +46,7 @@ import android.widget.Toast;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
@ -97,7 +100,7 @@ public final class MainVideoPlayer extends Activity {
showSystemUi(); showSystemUi();
setContentView(R.layout.activity_main_player); setContentView(R.layout.activity_main_player);
playerImpl = new VideoPlayerImpl(); playerImpl = new VideoPlayerImpl(getApplicationContext());
playerImpl.setup(findViewById(android.R.id.content)); playerImpl.setup(findViewById(android.R.id.content));
playerImpl.handleIntent(getIntent()); playerImpl.handleIntent(getIntent());
} }
@ -243,8 +246,8 @@ public final class MainVideoPlayer extends Activity {
private boolean queueVisible; private boolean queueVisible;
VideoPlayerImpl() { VideoPlayerImpl(final Context context) {
super("VideoPlayerImpl" + MainVideoPlayer.TAG, MainVideoPlayer.this); super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
} }
@Override @Override
@ -285,6 +288,12 @@ public final class MainVideoPlayer extends Activity {
screenRotationButton.setOnClickListener(this); screenRotationButton.setOnClickListener(this);
} }
@Override
public void onThumbnailReceived(Bitmap thumbnail) {
super.onThumbnailReceived(thumbnail);
clearThumbnailCache();
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener // ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View File

@ -0,0 +1,16 @@
package org.schabi.newpipe.player;
import android.os.Binder;
import android.support.annotation.NonNull;
class PlayerServiceBinder extends Binder {
private final BasePlayer basePlayer;
PlayerServiceBinder(@NonNull final BasePlayer basePlayer) {
this.basePlayer = basePlayer;
}
BasePlayer getPlayerInstance() {
return basePlayer;
}
}

View File

@ -30,7 +30,6 @@ import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
@ -133,12 +132,6 @@ public final class PopupVideoPlayer extends Service {
private PlayerEventListener activityListener; private PlayerEventListener activityListener;
private IBinder mBinder; private IBinder mBinder;
class LocalBinder extends Binder {
VideoPlayerImpl getPopupPlayerInstance() {
return PopupVideoPlayer.this.playerImpl;
}
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Service LifeCycle // Service LifeCycle
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -148,21 +141,20 @@ public final class PopupVideoPlayer extends Service {
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
playerImpl = new VideoPlayerImpl(); playerImpl = new VideoPlayerImpl(getApplicationContext());
ThemeHelper.setTheme(this); ThemeHelper.setTheme(this);
mBinder = new LocalBinder(); mBinder = new PlayerServiceBinder(playerImpl);
} }
@Override @Override
@SuppressWarnings("unchecked")
public int onStartCommand(final Intent intent, int flags, int startId) { public int onStartCommand(final Intent intent, int flags, int startId) {
if (DEBUG) if (DEBUG)
Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]"); Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
if (playerImpl.getPlayer() == null) initPopup(); if (playerImpl.getPlayer() == null) initPopup();
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true); if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
if (intent.getStringExtra(Constants.KEY_URL) != null) { if (intent != null && intent.getStringExtra(Constants.KEY_URL) != null) {
final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
final String url = intent.getStringExtra(Constants.KEY_URL); final String url = intent.getStringExtra(Constants.KEY_URL);
@ -200,14 +192,7 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy() called"); if (DEBUG) Log.d(TAG, "onDestroy() called");
stopForeground(true); onClose();
if (playerImpl != null) {
playerImpl.destroy();
if (playerImpl.getRootView() != null) windowManager.removeView(playerImpl.getRootView());
}
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
if (currentWorker != null) currentWorker.dispose();
savePositionAndSize();
} }
@Override @Override
@ -251,7 +236,6 @@ public final class PopupVideoPlayer extends Service {
MySimpleOnGestureListener listener = new MySimpleOnGestureListener(); MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
gestureDetector = new GestureDetector(this, listener); gestureDetector = new GestureDetector(this, listener);
//gestureDetector.setIsLongpressEnabled(false);
rootView.setOnTouchListener(listener); rootView.setOnTouchListener(listener);
playerImpl.getLoadingPanel().setMinimumWidth(windowLayoutParams.width); playerImpl.getLoadingPanel().setMinimumWidth(windowLayoutParams.width);
playerImpl.getLoadingPanel().setMinimumHeight(windowLayoutParams.height); playerImpl.getLoadingPanel().setMinimumHeight(windowLayoutParams.height);
@ -262,6 +246,10 @@ public final class PopupVideoPlayer extends Service {
// Notification // Notification
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private void resetNotification() {
notBuilder = createNotification();
}
private NotificationCompat.Builder createNotification() { private NotificationCompat.Builder createNotification() {
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_popup_notification); notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_popup_notification);
@ -303,9 +291,23 @@ public final class PopupVideoPlayer extends Service {
// Misc // Misc
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public void onVideoClose() { public void onClose() {
if (DEBUG) Log.d(TAG, "onVideoClose() called"); if (DEBUG) Log.d(TAG, "onClose() called");
playerImpl.stopActivityBinding();
if (playerImpl != null) {
if (playerImpl.getRootView() != null) {
windowManager.removeView(playerImpl.getRootView());
playerImpl.setRootView(null);
}
playerImpl.stopActivityBinding();
playerImpl.destroy();
}
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
if (currentWorker != null) currentWorker.dispose();
mBinder = null;
playerImpl = null;
stopForeground(true);
stopSelf(); stopSelf();
} }
@ -399,8 +401,16 @@ public final class PopupVideoPlayer extends Service {
protected class VideoPlayerImpl extends VideoPlayer { protected class VideoPlayerImpl extends VideoPlayer {
private TextView resizingIndicator; private TextView resizingIndicator;
VideoPlayerImpl() { @Override
super("VideoPlayerImpl" + PopupVideoPlayer.TAG, PopupVideoPlayer.this); public void handleIntent(Intent intent) {
super.handleIntent(intent);
resetNotification();
startForeground(NOTIFICATION_ID, notBuilder.build());
}
VideoPlayerImpl(final Context context) {
super("VideoPlayerImpl" + PopupVideoPlayer.TAG, context);
} }
@Override @Override
@ -411,8 +421,8 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public void destroy() { public void destroy() {
super.destroy();
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, null); if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, null);
super.destroy();
} }
@Override @Override
@ -426,6 +436,7 @@ public final class PopupVideoPlayer extends Service {
updateNotification(-1); updateNotification(-1);
} }
clearThumbnailCache();
} }
@Override @Override
@ -434,7 +445,7 @@ public final class PopupVideoPlayer extends Service {
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called"); if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
playerImpl.setRecovery(); setRecovery();
Intent intent; Intent intent;
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) { if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
intent = NavigationHelper.getPlayerIntent( intent = NavigationHelper.getPlayerIntent(
@ -457,9 +468,7 @@ public final class PopupVideoPlayer extends Service {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} }
context.startActivity(intent); context.startActivity(intent);
playerImpl.stopActivityBinding(); onClose();
destroyPlayer();
stopSelf();
} }
@Override @Override
@ -595,8 +604,7 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public void shutdown() { public void shutdown() {
super.shutdown(); super.shutdown();
stopActivityBinding(); onClose();
stopSelf();
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -619,16 +627,17 @@ public final class PopupVideoPlayer extends Service {
@Override @Override
public void onBroadcastReceived(Intent intent) { public void onBroadcastReceived(Intent intent) {
super.onBroadcastReceived(intent); super.onBroadcastReceived(intent);
if (intent == null || intent.getAction() == null) return;
if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]"); if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
switch (intent.getAction()) { switch (intent.getAction()) {
case ACTION_CLOSE: case ACTION_CLOSE:
onVideoClose(); onClose();
break; break;
case ACTION_PLAY_PAUSE: case ACTION_PLAY_PAUSE:
onVideoPlayPause(); onVideoPlayPause();
break; break;
case ACTION_OPEN_DETAIL: case ACTION_OPEN_DETAIL:
openControl(PopupVideoPlayer.this); openControl(getApplicationContext());
break; break;
case ACTION_REPEAT: case ACTION_REPEAT:
onRepeatClicked(); onRepeatClicked();
@ -800,7 +809,7 @@ public final class PopupVideoPlayer extends Service {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (Math.abs(velocityX) > SHUTDOWN_FLING_VELOCITY) { if (Math.abs(velocityX) > SHUTDOWN_FLING_VELOCITY) {
if (DEBUG) Log.d(TAG, "Popup close fling velocity= " + velocityX); if (DEBUG) Log.d(TAG, "Popup close fling velocity= " + velocityX);
onVideoClose(); onClose();
return true; return true;
} }
return false; return false;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.player; package org.schabi.newpipe.player;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -19,12 +18,6 @@ public final class PopupVideoPlayerActivity extends ServicePlayerActivity {
return getResources().getString(R.string.title_activity_popup_player); return getResources().getString(R.string.title_activity_popup_player);
} }
@Override
public BasePlayer playerFrom(IBinder binder) {
final PopupVideoPlayer.LocalBinder mLocalBinder = (PopupVideoPlayer.LocalBinder) binder;
return mLocalBinder.getPopupPlayerInstance();
}
@Override @Override
public Intent getBindIntent() { public Intent getBindIntent() {
return new Intent(this, PopupVideoPlayer.class); return new Intent(this, PopupVideoPlayer.class);

View File

@ -91,8 +91,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
public abstract void stopPlayerListener(); public abstract void stopPlayerListener();
public abstract BasePlayer playerFrom(final IBinder binder);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Activity Lifecycle // Activity Lifecycle
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -182,7 +180,11 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(getTag(), "Player service is connected"); Log.d(getTag(), "Player service is connected");
player = playerFrom(service);
if (service instanceof PlayerServiceBinder) {
player = ((PlayerServiceBinder) service).getPlayerInstance();
}
if (player == null || player.playQueue == null || player.playQueueAdapter == null || player.simpleExoPlayer == null) { if (player == null || player.playQueue == null || player.playQueueAdapter == null || player.simpleExoPlayer == null) {
unbind(); unbind();
finish(); finish();

View File

@ -139,7 +139,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
@Override @Override
public void onNext(@NonNull PlayQueueEvent playQueueMessage) { public void onNext(@NonNull PlayQueueEvent playQueueMessage) {
onPlayQueueChanged(playQueueMessage); if (playQueueReactor != null) onPlayQueueChanged(playQueueMessage);
} }
@Override @Override
@ -153,6 +153,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
private void onPlayQueueChanged(final PlayQueueEvent event) { private void onPlayQueueChanged(final PlayQueueEvent event) {
if (playQueue.isEmpty()) { if (playQueue.isEmpty()) {
playbackListener.shutdown(); playbackListener.shutdown();
return;
} }
// why no pattern matching in Java =( // why no pattern matching in Java =(
@ -170,7 +171,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
break; break;
case REMOVE: case REMOVE:
final RemoveEvent removeEvent = (RemoveEvent) event; final RemoveEvent removeEvent = (RemoveEvent) event;
remove(removeEvent.index()); remove(removeEvent.getRemoveIndex());
sync(); sync();
break; break;
case MOVE: case MOVE:

View File

@ -18,7 +18,6 @@ import org.schabi.newpipe.playlist.events.SelectEvent;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -210,25 +209,30 @@ public abstract class PlayQueue implements Serializable {
/** /**
* Appends the given {@link PlayQueueItem}s to the current play queue. * Appends the given {@link PlayQueueItem}s to the current play queue.
* *
* Will emit a {@link AppendEvent} on any given context. * @see #append(List items)
* */ * */
public synchronized void append(final PlayQueueItem... items) { public synchronized void append(final PlayQueueItem... items) {
streams.addAll(Arrays.asList(items)); append(Arrays.asList(items));
if (backup != null) backup.addAll(Arrays.asList(items));
broadcast(new AppendEvent(items.length));
} }
/** /**
* Appends the given {@link PlayQueueItem}s to the current play queue. * Appends the given {@link PlayQueueItem}s to the current play queue.
* *
* If the play queue is shuffled, then append the items to the backup queue as is and
* append the shuffle items to the play queue.
*
* Will emit a {@link AppendEvent} on any given context. * Will emit a {@link AppendEvent} on any given context.
* */ * */
public synchronized void append(final Collection<PlayQueueItem> items) { public synchronized void append(final List<PlayQueueItem> items) {
streams.addAll(items); List<PlayQueueItem> itemList = new ArrayList<>(items);
if (backup != null) backup.addAll(items);
broadcast(new AppendEvent(items.size())); if (isShuffled()) {
backup.addAll(itemList);
Collections.shuffle(itemList);
}
streams.addAll(itemList);
broadcast(new AppendEvent(itemList.size()));
} }
/** /**
@ -242,7 +246,7 @@ public abstract class PlayQueue implements Serializable {
public synchronized void remove(final int index) { public synchronized void remove(final int index) {
if (index >= streams.size() || index < 0) return; if (index >= streams.size() || index < 0) return;
removeInternal(index); removeInternal(index);
broadcast(new RemoveEvent(index)); broadcast(new RemoveEvent(index, getIndex()));
} }
/** /**
@ -261,24 +265,28 @@ public abstract class PlayQueue implements Serializable {
removeInternal(index); removeInternal(index);
} }
broadcast(new ErrorEvent(index, skippable)); broadcast(new ErrorEvent(index, getIndex(), skippable));
} }
private synchronized void removeInternal(final int index) { private synchronized void removeInternal(final int removeIndex) {
final int currentIndex = queueIndex.get(); final int currentIndex = queueIndex.get();
final int size = size(); final int size = size();
if (currentIndex > index) { if (currentIndex > removeIndex) {
queueIndex.decrementAndGet(); queueIndex.decrementAndGet();
} else if (currentIndex >= size) { } else if (currentIndex >= size) {
queueIndex.set(currentIndex % (size - 1)); queueIndex.set(currentIndex % (size - 1));
} else if (currentIndex == removeIndex && currentIndex == size - 1){
queueIndex.set(removeIndex - 1);
} }
if (backup != null) { if (backup != null) {
final int backupIndex = backup.indexOf(getItem(index)); final int backupIndex = backup.indexOf(getItem(removeIndex));
backup.remove(backupIndex); backup.remove(backupIndex);
} }
streams.remove(index); streams.remove(removeIndex);
} }
/** /**

View File

@ -82,7 +82,7 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
@Override @Override
public void onNext(@NonNull PlayQueueEvent playQueueMessage) { public void onNext(@NonNull PlayQueueEvent playQueueMessage) {
onPlayQueueChanged(playQueueMessage); if (playQueueReactor != null) onPlayQueueChanged(playQueueMessage);
} }
@Override @Override
@ -116,14 +116,15 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
case ERROR: case ERROR:
final ErrorEvent errorEvent = (ErrorEvent) message; final ErrorEvent errorEvent = (ErrorEvent) message;
if (!errorEvent.isSkippable()) { if (!errorEvent.isSkippable()) {
notifyItemRemoved(errorEvent.index()); notifyItemRemoved(errorEvent.getErrorIndex());
} }
notifyItemChanged(errorEvent.index()); notifyItemChanged(errorEvent.getErrorIndex());
notifyItemChanged(errorEvent.getQueueIndex());
break; break;
case REMOVE: case REMOVE:
final RemoveEvent removeEvent = (RemoveEvent) message; final RemoveEvent removeEvent = (RemoveEvent) message;
notifyItemRemoved(removeEvent.index()); notifyItemRemoved(removeEvent.getRemoveIndex());
notifyItemChanged(removeEvent.index()); notifyItemChanged(removeEvent.getQueueIndex());
break; break;
case MOVE: case MOVE:
final MoveEvent moveEvent = (MoveEvent) message; final MoveEvent moveEvent = (MoveEvent) message;

View File

@ -2,7 +2,8 @@ package org.schabi.newpipe.playlist.events;
public class ErrorEvent implements PlayQueueEvent { public class ErrorEvent implements PlayQueueEvent {
final private int index; final private int errorIndex;
final private int queueIndex;
final private boolean skippable; final private boolean skippable;
@Override @Override
@ -10,13 +11,18 @@ public class ErrorEvent implements PlayQueueEvent {
return PlayQueueEventType.ERROR; return PlayQueueEventType.ERROR;
} }
public ErrorEvent(final int index, final boolean skippable) { public ErrorEvent(final int errorIndex, final int queueIndex, final boolean skippable) {
this.index = index; this.errorIndex = errorIndex;
this.queueIndex = queueIndex;
this.skippable = skippable; this.skippable = skippable;
} }
public int index() { public int getErrorIndex() {
return index; return errorIndex;
}
public int getQueueIndex() {
return queueIndex;
} }
public boolean isSkippable() { public boolean isSkippable() {

View File

@ -2,18 +2,24 @@ package org.schabi.newpipe.playlist.events;
public class RemoveEvent implements PlayQueueEvent { public class RemoveEvent implements PlayQueueEvent {
final private int index; final private int removeIndex;
final private int queueIndex;
@Override @Override
public PlayQueueEventType type() { public PlayQueueEventType type() {
return PlayQueueEventType.REMOVE; return PlayQueueEventType.REMOVE;
} }
public RemoveEvent(final int index) { public RemoveEvent(final int removeIndex, final int queueIndex) {
this.index = index; this.removeIndex = removeIndex;
this.queueIndex = queueIndex;
} }
public int index() { public int getQueueIndex() {
return index; return queueIndex;
}
public int getRemoveIndex() {
return removeIndex;
} }
} }