Loading packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2406,6 +2406,8 @@ <string name="media_output_dialog_accessibility_seekbar">Volume</string> <!-- Summary for media output volume of a device in percentage [CHAR LIMIT=NONE] --> <string name="media_output_dialog_volume_percentage"><xliff:g id="percentage" example="10">%1$d</xliff:g>%%</string> <!-- Title for Speakers and Displays group. [CHAR LIMIT=NONE] --> <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <!-- Media Output Broadcast Dialog --> <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] --> Loading packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +7 −0 Original line number Diff line number Diff line Loading @@ -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: Loading packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +9 −14 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)); } } else { if (!mVolumeAnimator.isStarted()) { if (mController.isAdvancedLayoutSupported()) { Loading Loading @@ -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) { Loading packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +40 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -275,7 +277,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, @Override public void onDeviceListUpdate(List<MediaDevice> devices) { if (mMediaDevices.isEmpty() || !mIsRefreshing) { boolean isListEmpty = isAdvancedLayoutSupported() ? mMediaItemList.isEmpty() : mMediaDevices.isEmpty(); if (isListEmpty || !mIsRefreshing) { buildMediaDevices(devices); mCallback.onDeviceListChanged(); } else { Loading Loading @@ -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 Loading @@ -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<MediaDevice> targetMediaDevices = new ArrayList<>(); final Map<Integer, MediaItem> dividerItems = new HashMap<>(); for (MediaItem originalMediaItem : mMediaItemList) { for (MediaDevice newDevice : devices) { if (originalMediaItem.getMediaDevice().isPresent() Loading @@ -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); Loading @@ -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<String> 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()); } } Loading packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -125,6 +127,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { private LocalMediaManager mLocalMediaManager; private List<MediaController> mMediaControllers = new ArrayList<>(); private List<MediaDevice> mMediaDevices = new ArrayList<>(); private List<MediaItem> mMediaItemList = new ArrayList<>(); private List<NearbyDevice> mNearbyDevices = new ArrayList<>(); private MediaDescription mMediaDescription; private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>(); Loading Loading @@ -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); Loading Loading @@ -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)); Loading Loading
packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2406,6 +2406,8 @@ <string name="media_output_dialog_accessibility_seekbar">Volume</string> <!-- Summary for media output volume of a device in percentage [CHAR LIMIT=NONE] --> <string name="media_output_dialog_volume_percentage"><xliff:g id="percentage" example="10">%1$d</xliff:g>%%</string> <!-- Title for Speakers and Displays group. [CHAR LIMIT=NONE] --> <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <!-- Media Output Broadcast Dialog --> <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] --> Loading
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +7 −0 Original line number Diff line number Diff line Loading @@ -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: Loading
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +9 −14 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)); } } else { if (!mVolumeAnimator.isStarted()) { if (mController.isAdvancedLayoutSupported()) { Loading Loading @@ -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) { Loading
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +40 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -275,7 +277,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, @Override public void onDeviceListUpdate(List<MediaDevice> devices) { if (mMediaDevices.isEmpty() || !mIsRefreshing) { boolean isListEmpty = isAdvancedLayoutSupported() ? mMediaItemList.isEmpty() : mMediaDevices.isEmpty(); if (isListEmpty || !mIsRefreshing) { buildMediaDevices(devices); mCallback.onDeviceListChanged(); } else { Loading Loading @@ -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 Loading @@ -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<MediaDevice> targetMediaDevices = new ArrayList<>(); final Map<Integer, MediaItem> dividerItems = new HashMap<>(); for (MediaItem originalMediaItem : mMediaItemList) { for (MediaDevice newDevice : devices) { if (originalMediaItem.getMediaDevice().isPresent() Loading @@ -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); Loading @@ -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<String> 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()); } } Loading
packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -125,6 +127,7 @@ public class MediaOutputControllerTest extends SysuiTestCase { private LocalMediaManager mLocalMediaManager; private List<MediaController> mMediaControllers = new ArrayList<>(); private List<MediaDevice> mMediaDevices = new ArrayList<>(); private List<MediaItem> mMediaItemList = new ArrayList<>(); private List<NearbyDevice> mNearbyDevices = new ArrayList<>(); private MediaDescription mMediaDescription; private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>(); Loading Loading @@ -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); Loading Loading @@ -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)); Loading