Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba57c1c3 authored by shaoweishen's avatar shaoweishen Committed by Shaowei Shen
Browse files

[Output Switcher] Fix ArrayIndexOutOfBoundsException

Decouple MediaItem data set between Adpater with MediaOutputController.
Whenever the devicelist update, copy list in Adapter and trigger update,
prevent Adapter touch dataset in controller while refresh.

Bug: 275049691
Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest
Change-Id: I0e9a352125fa8b54b1a19544421996bb96f8f239
parent 330484d4
Loading
Loading
Loading
Loading
+17 −8
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Adapter for media output dialog.
@@ -52,12 +53,20 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final float DEVICE_DISCONNECTED_ALPHA = 0.5f;
    private static final float DEVICE_CONNECTED_ALPHA = 1f;
    protected List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();

    public MediaOutputAdapter(MediaOutputController controller) {
        super(controller);
        setHasStableIds(true);
    }

    @Override
    public void updateItems() {
        mMediaItemList.clear();
        mMediaItemList.addAll(mController.getMediaItemList());
        notifyDataSetChanged();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
            int viewType) {
@@ -79,14 +88,14 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
        if (mController.isAdvancedLayoutSupported()) {
            if (position >= mController.getMediaItemList().size()) {
            if (position >= mMediaItemList.size()) {
                if (DEBUG) {
                    Log.d(TAG, "Incorrect position: " + position + " list size: "
                            + mController.getMediaItemList().size());
                            + mMediaItemList.size());
                }
                return;
            }
            MediaItem currentMediaItem = mController.getMediaItemList().get(position);
            MediaItem currentMediaItem = mMediaItemList.get(position);
            switch (currentMediaItem.getMediaItemType()) {
                case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:
                    ((MediaGroupDividerViewHolder) viewHolder).onBind(currentMediaItem.getTitle());
@@ -119,11 +128,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
    @Override
    public long getItemId(int position) {
        if (mController.isAdvancedLayoutSupported()) {
            if (position >= mController.getMediaItemList().size()) {
            if (position >= mMediaItemList.size()) {
                Log.d(TAG, "Incorrect position for item id: " + position);
                return position;
            }
            MediaItem currentMediaItem = mController.getMediaItemList().get(position);
            MediaItem currentMediaItem = mMediaItemList.get(position);
            return currentMediaItem.getMediaDevice().isPresent()
                    ? currentMediaItem.getMediaDevice().get().getId().hashCode()
                    : position;
@@ -143,12 +152,12 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
    @Override
    public int getItemViewType(int position) {
        if (mController.isAdvancedLayoutSupported()
                && position >= mController.getMediaItemList().size()) {
                && position >= mMediaItemList.size()) {
            Log.d(TAG, "Incorrect position for item type: " + position);
            return MediaItem.MediaItemType.TYPE_GROUP_DIVIDER;
        }
        return mController.isAdvancedLayoutSupported()
                ? mController.getMediaItemList().get(position).getMediaItemType()
                ? mMediaItemList.get(position).getMediaItemType()
                : super.getItemViewType(position);
    }

@@ -156,7 +165,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
    public int getItemCount() {
        // Add extra one for "pair new"
        return mController.isAdvancedLayoutSupported()
                ? mController.getMediaItemList().size()
                ? mMediaItemList.size()
                : mController.getMediaDevices().size() + 1;
    }

+5 −0
Original line number Diff line number Diff line
@@ -81,6 +81,11 @@ public abstract class MediaOutputBaseAdapter extends
        mIsInitVolumeFirstTime = true;
    }

    /**
     * Refresh current dataset
     */
    public abstract void updateItems();

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
            int viewType) {
+1 −1
Original line number Diff line number Diff line
@@ -382,7 +382,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
                    && currentActivePosition < mAdapter.getItemCount()) {
                mAdapter.notifyItemChanged(currentActivePosition);
            } else {
                mAdapter.notifyDataSetChanged();
                mAdapter.updateItems();
            }
        } else {
            mMediaOutputController.setRefreshing(false);
+22 −0
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
        mMediaItems.add(new MediaItem(mMediaDevice2));

        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
        mMediaOutputAdapter.updateItems();
        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
        mSpyMediaOutputSeekbar = spy(mViewHolder.mSeekBar);
@@ -202,9 +203,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
    public void advanced_onBindViewHolder_bindPairNew_verifyView() {
        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
        mMediaOutputAdapter.updateItems();
        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
        mMediaItems.add(new MediaItem());
        mMediaOutputAdapter.updateItems();
        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);

        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -223,6 +226,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
                        Collectors.toList()));
        when(mMediaOutputController.getSessionName()).thenReturn(TEST_SESSION_NAME);
        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
        mMediaOutputAdapter.updateItems();
        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
        mMediaOutputAdapter.getItemCount();
@@ -243,6 +247,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
                        Collectors.toList()));
        when(mMediaOutputController.getSessionName()).thenReturn(null);
        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
        mMediaOutputAdapter.updateItems();
        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
        mMediaOutputAdapter.getItemCount();
@@ -602,9 +607,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
    public void advanced_onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
        mMediaOutputAdapter.updateItems();
        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
        mMediaItems.add(new MediaItem());
        mMediaOutputAdapter.updateItems();
        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
        mViewHolder.mContainerLayout.performClick();

@@ -700,6 +707,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
                mMediaItems.stream().map((item) -> item.getMediaDevice().get()).collect(
                        Collectors.toList()));
        mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
        mMediaOutputAdapter.updateItems();
        mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                .onCreateViewHolder(new LinearLayout(mContext), 0);
        List<MediaDevice> selectableDevices = new ArrayList<>();
@@ -734,4 +742,18 @@ public class MediaOutputAdapterTest extends SysuiTestCase {

        verify(mMediaOutputController).setCurrentColorScheme(wallpaperColors, true);
    }

    @Test
    public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() {
        when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
        mMediaOutputAdapter.updateItems();
        List<MediaItem> updatedList = new ArrayList<>();
        updatedList.add(new MediaItem());
        when(mMediaOutputController.getMediaItemList()).thenReturn(updatedList);
        assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size());

        mMediaOutputAdapter.updateItems();

        assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(updatedList.size());
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -239,7 +239,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
        when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false);
        mMediaOutputBaseDialogImpl.refresh();

        verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
        verify(mMediaOutputBaseAdapter).updateItems();
    }

    @Test