diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 80e969ead1696..7b1c6c6d67ce6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2406,6 +2406,8 @@ Volume %1$d%% + + Speakers & Displays diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index fb47d97ed1d45..51b5a3d1a08c0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -70,6 +70,13 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { if (mController.isAdvancedLayoutSupported()) { + if (position >= mController.getMediaItemList().size()) { + if (DEBUG) { + Log.d(TAG, "Incorrect position: " + position + " list size: " + + mController.getMediaItemList().size()); + } + return; + } MediaItem currentMediaItem = mController.getMediaItemList().get(position); switch (currentMediaItem.getMediaItemType()) { case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER: diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index 3b1d861d03125..4e08050a6f65f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -84,8 +84,7 @@ public abstract class MediaOutputBaseAdapter extends int viewType) { mContext = viewGroup.getContext(); mHolderView = LayoutInflater.from(mContext).inflate( - mController.isAdvancedLayoutSupported() ? MediaItem.getMediaLayoutId( - viewType) /*R.layout.media_output_list_item_advanced*/ + mController.isAdvancedLayoutSupported() ? MediaItem.getMediaLayoutId(viewType) : R.layout.media_output_list_item, viewGroup, false); return null; @@ -308,9 +307,10 @@ public abstract class MediaOutputBaseAdapter extends updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off : R.drawable.media_output_icon_volume, mController.getColorItemContent()); + } else { + animateCornerAndVolume(mSeekBar.getProgress(), + MediaOutputSeekbar.scaleVolumeToProgress(currentVolume)); } - animateCornerAndVolume(mSeekBar.getProgress(), - MediaOutputSeekbar.scaleVolumeToProgress(currentVolume)); } else { if (!mVolumeAnimator.isStarted()) { if (mController.isAdvancedLayoutSupported()) { @@ -396,23 +396,18 @@ public abstract class MediaOutputBaseAdapter extends } void updateMutedVolumeIcon() { + mIconAreaLayout.setBackground( + mContext.getDrawable(R.drawable.media_output_item_background_active)); updateTitleIcon(R.drawable.media_output_icon_volume_off, mController.getColorItemContent()); - final GradientDrawable iconAreaBackgroundDrawable = - (GradientDrawable) mIconAreaLayout.getBackground(); - iconAreaBackgroundDrawable.setCornerRadius(mController.getActiveRadius()); } void updateUnmutedVolumeIcon() { + mIconAreaLayout.setBackground( + mContext.getDrawable(R.drawable.media_output_title_icon_area) + ); updateTitleIcon(R.drawable.media_output_icon_volume, mController.getColorItemContent()); - final GradientDrawable iconAreaBackgroundDrawable = - (GradientDrawable) mIconAreaLayout.getBackground(); - iconAreaBackgroundDrawable.setCornerRadii(new float[]{ - mController.getActiveRadius(), - mController.getActiveRadius(), - 0, 0, 0, 0, mController.getActiveRadius(), mController.getActiveRadius() - }); } void updateTitleIcon(@DrawableRes int id, int color) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index b43656207821a..8eb25c4c24957 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -87,9 +87,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; @@ -275,7 +277,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, @Override public void onDeviceListUpdate(List devices) { - if (mMediaDevices.isEmpty() || !mIsRefreshing) { + boolean isListEmpty = + isAdvancedLayoutSupported() ? mMediaItemList.isEmpty() : mMediaDevices.isEmpty(); + if (isListEmpty || !mIsRefreshing) { buildMediaDevices(devices); mCallback.onDeviceListChanged(); } else { @@ -646,16 +650,19 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, for (MediaDevice device : devices) { if (device.isMutingExpectedDevice()) { mMediaItemList.add(0, new MediaItem(device)); + mMediaItemList.add(1, new MediaItem(mContext.getString( + R.string.media_output_group_title_speakers_and_displays), + MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); } else { mMediaItemList.add(new MediaItem(device)); } } + mMediaItemList.add(new MediaItem()); } else { mMediaItemList.addAll( devices.stream().map(MediaItem::new).collect(Collectors.toList())); + categorizeMediaItems(null); } - - categorizeMediaItems(); return; } // selected device exist @@ -666,11 +673,12 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, mMediaItemList.add(new MediaItem(device)); } } - categorizeMediaItems(); + categorizeMediaItems(connectedMediaDevice); return; } // To keep the same list order final List targetMediaDevices = new ArrayList<>(); + final Map dividerItems = new HashMap<>(); for (MediaItem originalMediaItem : mMediaItemList) { for (MediaDevice newDevice : devices) { if (originalMediaItem.getMediaDevice().isPresent() @@ -680,6 +688,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, break; } } + if (originalMediaItem.getMediaItemType() + == MediaItem.MediaItemType.TYPE_GROUP_DIVIDER) { + dividerItems.put(mMediaItemList.indexOf(originalMediaItem), originalMediaItem); + } } if (targetMediaDevices.size() != devices.size()) { devices.removeAll(targetMediaDevices); @@ -688,13 +700,34 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, mMediaItemList.clear(); mMediaItemList.addAll( targetMediaDevices.stream().map(MediaItem::new).collect(Collectors.toList())); - categorizeMediaItems(); + dividerItems.forEach((key, item) -> { + mMediaItemList.add(key, item); + }); + mMediaItemList.add(new MediaItem()); } } - private void categorizeMediaItems() { + private void categorizeMediaItems(MediaDevice connectedMediaDevice) { synchronized (mMediaDevicesLock) { - //TODO(255124239): do the categorization here + Set selectedDevicesIds = getSelectedMediaDevice().stream().map( + MediaDevice::getId).collect(Collectors.toSet()); + if (connectedMediaDevice != null) { + selectedDevicesIds.add(connectedMediaDevice.getId()); + } + int latestSelected = 1; + for (MediaItem item : mMediaItemList) { + if (item.getMediaDevice().isPresent()) { + MediaDevice device = item.getMediaDevice().get(); + if (selectedDevicesIds.contains(device.getId())) { + latestSelected = mMediaItemList.indexOf(item) + 1; + } else { + mMediaItemList.add(latestSelected, new MediaItem(mContext.getString( + R.string.media_output_group_title_speakers_and_displays), + MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); + break; + } + } + } mMediaItemList.add(new MediaItem()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 71c300c3fb6f9..b16a39f37e395 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -102,6 +102,8 @@ public class MediaOutputControllerTest extends SysuiTestCase { private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class); private MediaDevice mMediaDevice1 = mock(MediaDevice.class); private MediaDevice mMediaDevice2 = mock(MediaDevice.class); + private MediaItem mMediaItem1 = mock(MediaItem.class); + private MediaItem mMediaItem2 = mock(MediaItem.class); private NearbyDevice mNearbyDevice1 = mock(NearbyDevice.class); private NearbyDevice mNearbyDevice2 = mock(NearbyDevice.class); private MediaMetadata mMediaMetadata = mock(MediaMetadata.class); @@ -125,6 +127,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { private LocalMediaManager mLocalMediaManager; private List mMediaControllers = new ArrayList<>(); private List mMediaDevices = new ArrayList<>(); + private List mMediaItemList = new ArrayList<>(); private List mNearbyDevices = new ArrayList<>(); private MediaDescription mMediaDescription; private List mRoutingSessionInfos = new ArrayList<>(); @@ -157,6 +160,11 @@ public class MediaOutputControllerTest extends SysuiTestCase { when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice2); + when(mMediaItem1.getMediaDevice()).thenReturn(Optional.of(mMediaDevice1)); + when(mMediaItem2.getMediaDevice()).thenReturn(Optional.of(mMediaDevice2)); + mMediaItemList.add(mMediaItem1); + mMediaItemList.add(mMediaItem2); + when(mNearbyDevice1.getMediaRoute2Id()).thenReturn(TEST_DEVICE_1_ID); when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_CLOSE); @@ -313,6 +321,18 @@ public class MediaOutputControllerTest extends SysuiTestCase { assertThat(mMediaOutputController.mNeedRefresh).isTrue(); } + @Test + public void advanced_onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() { + when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true); + mMediaOutputController.start(mCb); + reset(mCb); + mMediaOutputController.mIsRefreshing = true; + + mMediaOutputController.onDeviceListUpdate(mMediaDevices); + + assertThat(mMediaOutputController.mNeedRefresh).isTrue(); + } + @Test public void cancelMuteAwaitConnection_cancelsWithMediaManager() { when(mAudioManager.getMutingExpectedDevice()).thenReturn(mock(AudioDeviceAttributes.class));