From 55e6d1d70895f735b7a13f821dc452d7c88bb0d7 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 12 Feb 2019 11:30:54 +0000 Subject: [PATCH] Prevent concurrent queue modifications from breaking the cast demo app UI PiperOrigin-RevId: 233577854 --- .../exoplayer2/castdemo/MainActivity.java | 10 +++-- .../exoplayer2/castdemo/PlayerManager.java | 39 +++++++++++++------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java index 5ecfaec78e..22f9e18b24 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java @@ -211,6 +211,7 @@ public class MainActivity extends AppCompatActivity private class QueueItemViewHolder extends RecyclerView.ViewHolder implements OnClickListener { public final TextView textView; + public MediaItem item; public QueueItemViewHolder(TextView textView) { super(textView); @@ -236,8 +237,9 @@ public class MainActivity extends AppCompatActivity @Override public void onBindViewHolder(QueueItemViewHolder holder, int position) { + holder.item = playerManager.getItem(position); TextView view = holder.textView; - view.setText(playerManager.getItem(position).title); + view.setText(holder.item.title); // TODO: Solve coloring using the theme's ColorStateList. view.setTextColor(ColorUtils.setAlphaComponent(view.getCurrentTextColor(), position == playerManager.getCurrentItemIndex() ? 255 : 100)); @@ -278,7 +280,8 @@ public class MainActivity extends AppCompatActivity @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); - if (playerManager.removeItem(position)) { + QueueItemViewHolder queueItemHolder = (QueueItemViewHolder) viewHolder; + if (playerManager.removeItem(queueItemHolder.item)) { mediaQueueListAdapter.notifyItemRemoved(position); // Update whichever item took its place, in case it became the new selected item. mediaQueueListAdapter.notifyItemChanged(position); @@ -289,8 +292,9 @@ public class MainActivity extends AppCompatActivity public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); if (draggingFromPosition != C.INDEX_UNSET) { + QueueItemViewHolder queueItemHolder = (QueueItemViewHolder) viewHolder; // A drag has ended. We reflect the media queue change in the player. - if (!playerManager.moveItem(draggingFromPosition, draggingToPosition)) { + if (!playerManager.moveItem(queueItemHolder.item, draggingToPosition)) { // The move failed. The entire sequence of onMove calls since the drag started needs to be // invalidated. mediaQueueListAdapter.notifyDataSetChanged(); diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java index 6b25934227..28dd808fdf 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java @@ -168,10 +168,16 @@ import java.util.ArrayList; /** * Removes the item at the given index from the media queue. * - * @param itemIndex The index of the item to remove. + * @param item The item to remove. * @return Whether the removal was successful. */ - public boolean removeItem(int itemIndex) { + public boolean removeItem(MediaItem item) { + int itemIndex = mediaQueue.indexOf(item); + if (itemIndex == -1) { + // This may happen if another sender app removes items while this sender app is in "swiping + // an item" state. + return false; + } concatenatingMediaSource.removeMediaSource(itemIndex); mediaQueue.remove(itemIndex); if (currentPlayer == exoCastPlayer) { @@ -188,20 +194,29 @@ import java.util.ArrayList; /** * Moves an item within the queue. * - * @param fromIndex The index of the item to move. - * @param toIndex The target index of the item in the queue. - * @return Whether the item move was successful. + * @param item The item to move. This method does nothing if {@code item} is not contained in the + * queue. + * @param toIndex The target index of the item in the queue. If {@code toIndex} exceeds the last + * position in the queue, {@code toIndex} is clamped to match the largest possible value. + * @return True if {@code item} was contained in the queue, and {@code toIndex} was a valid + * position. False otherwise. */ - public boolean moveItem(int fromIndex, int toIndex) { - mediaQueue.add(toIndex, mediaQueue.remove(fromIndex)); - concatenatingMediaSource.moveMediaSource(fromIndex, toIndex); - if (currentPlayer == exoCastPlayer) { - exoCastPlayer.moveItemInQueue(fromIndex, toIndex); + public boolean moveItem(MediaItem item, int toIndex) { + int indexOfItem = mediaQueue.indexOf(item); + if (indexOfItem == -1) { + // This may happen if another sender app removes items while this sender app is in "dragging + // an item" state. + return false; + } + int clampedToIndex = Math.min(toIndex, mediaQueue.size() - 1); + mediaQueue.add(clampedToIndex, mediaQueue.remove(indexOfItem)); + concatenatingMediaSource.moveMediaSource(indexOfItem, clampedToIndex); + if (currentPlayer == exoCastPlayer) { + exoCastPlayer.moveItemInQueue(indexOfItem, clampedToIndex); } - // Index update. maybeSetCurrentItemAndNotify(currentPlayer.getCurrentWindowIndex()); - return true; + return clampedToIndex == toIndex; } // Miscellaneous methods.