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));