Merge "[Output Switcher] Add group title" into tm-qpr-dev am: b379d5c37d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20689811

Change-Id: I05397020d743426077eda66d6f0ca9acb8fec16b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Shaowei Shen
2022-12-19 10:26:17 +00:00
committed by Automerger Merge Worker
5 changed files with 78 additions and 21 deletions

View File

@@ -2406,6 +2406,8 @@
<string name="media_output_dialog_accessibility_seekbar">Volume</string> <string name="media_output_dialog_accessibility_seekbar">Volume</string>
<!-- Summary for media output volume of a device in percentage [CHAR LIMIT=NONE] --> <!-- 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> <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 &amp; Displays</string>
<!-- Media Output Broadcast Dialog --> <!-- Media Output Broadcast Dialog -->
<!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] --> <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] -->

View File

@@ -70,6 +70,13 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
@Override @Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
if (mController.isAdvancedLayoutSupported()) { 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); MediaItem currentMediaItem = mController.getMediaItemList().get(position);
switch (currentMediaItem.getMediaItemType()) { switch (currentMediaItem.getMediaItemType()) {
case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER: case MediaItem.MediaItemType.TYPE_GROUP_DIVIDER:

View File

@@ -84,8 +84,7 @@ public abstract class MediaOutputBaseAdapter extends
int viewType) { int viewType) {
mContext = viewGroup.getContext(); mContext = viewGroup.getContext();
mHolderView = LayoutInflater.from(mContext).inflate( mHolderView = LayoutInflater.from(mContext).inflate(
mController.isAdvancedLayoutSupported() ? MediaItem.getMediaLayoutId( mController.isAdvancedLayoutSupported() ? MediaItem.getMediaLayoutId(viewType)
viewType) /*R.layout.media_output_list_item_advanced*/
: R.layout.media_output_list_item, viewGroup, false); : R.layout.media_output_list_item, viewGroup, false);
return null; return null;
@@ -308,9 +307,10 @@ public abstract class MediaOutputBaseAdapter extends
updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off
: R.drawable.media_output_icon_volume, : R.drawable.media_output_icon_volume,
mController.getColorItemContent()); mController.getColorItemContent());
} } else {
animateCornerAndVolume(mSeekBar.getProgress(), animateCornerAndVolume(mSeekBar.getProgress(),
MediaOutputSeekbar.scaleVolumeToProgress(currentVolume)); MediaOutputSeekbar.scaleVolumeToProgress(currentVolume));
}
} else { } else {
if (!mVolumeAnimator.isStarted()) { if (!mVolumeAnimator.isStarted()) {
if (mController.isAdvancedLayoutSupported()) { if (mController.isAdvancedLayoutSupported()) {
@@ -396,23 +396,18 @@ public abstract class MediaOutputBaseAdapter extends
} }
void updateMutedVolumeIcon() { void updateMutedVolumeIcon() {
mIconAreaLayout.setBackground(
mContext.getDrawable(R.drawable.media_output_item_background_active));
updateTitleIcon(R.drawable.media_output_icon_volume_off, updateTitleIcon(R.drawable.media_output_icon_volume_off,
mController.getColorItemContent()); mController.getColorItemContent());
final GradientDrawable iconAreaBackgroundDrawable =
(GradientDrawable) mIconAreaLayout.getBackground();
iconAreaBackgroundDrawable.setCornerRadius(mController.getActiveRadius());
} }
void updateUnmutedVolumeIcon() { void updateUnmutedVolumeIcon() {
mIconAreaLayout.setBackground(
mContext.getDrawable(R.drawable.media_output_title_icon_area)
);
updateTitleIcon(R.drawable.media_output_icon_volume, updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent()); 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) { void updateTitleIcon(@DrawableRes int id, int color) {

View File

@@ -87,9 +87,11 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -275,7 +277,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
@Override @Override
public void onDeviceListUpdate(List<MediaDevice> devices) { public void onDeviceListUpdate(List<MediaDevice> devices) {
if (mMediaDevices.isEmpty() || !mIsRefreshing) { boolean isListEmpty =
isAdvancedLayoutSupported() ? mMediaItemList.isEmpty() : mMediaDevices.isEmpty();
if (isListEmpty || !mIsRefreshing) {
buildMediaDevices(devices); buildMediaDevices(devices);
mCallback.onDeviceListChanged(); mCallback.onDeviceListChanged();
} else { } else {
@@ -646,16 +650,19 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
for (MediaDevice device : devices) { for (MediaDevice device : devices) {
if (device.isMutingExpectedDevice()) { if (device.isMutingExpectedDevice()) {
mMediaItemList.add(0, new MediaItem(device)); 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 { } else {
mMediaItemList.add(new MediaItem(device)); mMediaItemList.add(new MediaItem(device));
} }
} }
mMediaItemList.add(new MediaItem());
} else { } else {
mMediaItemList.addAll( mMediaItemList.addAll(
devices.stream().map(MediaItem::new).collect(Collectors.toList())); devices.stream().map(MediaItem::new).collect(Collectors.toList()));
categorizeMediaItems(null);
} }
categorizeMediaItems();
return; return;
} }
// selected device exist // selected device exist
@@ -666,11 +673,12 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mMediaItemList.add(new MediaItem(device)); mMediaItemList.add(new MediaItem(device));
} }
} }
categorizeMediaItems(); categorizeMediaItems(connectedMediaDevice);
return; return;
} }
// To keep the same list order // To keep the same list order
final List<MediaDevice> targetMediaDevices = new ArrayList<>(); final List<MediaDevice> targetMediaDevices = new ArrayList<>();
final Map<Integer, MediaItem> dividerItems = new HashMap<>();
for (MediaItem originalMediaItem : mMediaItemList) { for (MediaItem originalMediaItem : mMediaItemList) {
for (MediaDevice newDevice : devices) { for (MediaDevice newDevice : devices) {
if (originalMediaItem.getMediaDevice().isPresent() if (originalMediaItem.getMediaDevice().isPresent()
@@ -680,6 +688,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
break; break;
} }
} }
if (originalMediaItem.getMediaItemType()
== MediaItem.MediaItemType.TYPE_GROUP_DIVIDER) {
dividerItems.put(mMediaItemList.indexOf(originalMediaItem), originalMediaItem);
}
} }
if (targetMediaDevices.size() != devices.size()) { if (targetMediaDevices.size() != devices.size()) {
devices.removeAll(targetMediaDevices); devices.removeAll(targetMediaDevices);
@@ -688,13 +700,34 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mMediaItemList.clear(); mMediaItemList.clear();
mMediaItemList.addAll( mMediaItemList.addAll(
targetMediaDevices.stream().map(MediaItem::new).collect(Collectors.toList())); 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) { 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()); mMediaItemList.add(new MediaItem());
} }
} }

View File

@@ -102,6 +102,8 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class); private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class);
private MediaDevice mMediaDevice1 = mock(MediaDevice.class); private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
private MediaDevice mMediaDevice2 = 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 mNearbyDevice1 = mock(NearbyDevice.class);
private NearbyDevice mNearbyDevice2 = mock(NearbyDevice.class); private NearbyDevice mNearbyDevice2 = mock(NearbyDevice.class);
private MediaMetadata mMediaMetadata = mock(MediaMetadata.class); private MediaMetadata mMediaMetadata = mock(MediaMetadata.class);
@@ -125,6 +127,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
private LocalMediaManager mLocalMediaManager; private LocalMediaManager mLocalMediaManager;
private List<MediaController> mMediaControllers = new ArrayList<>(); private List<MediaController> mMediaControllers = new ArrayList<>();
private List<MediaDevice> mMediaDevices = new ArrayList<>(); private List<MediaDevice> mMediaDevices = new ArrayList<>();
private List<MediaItem> mMediaItemList = new ArrayList<>();
private List<NearbyDevice> mNearbyDevices = new ArrayList<>(); private List<NearbyDevice> mNearbyDevices = new ArrayList<>();
private MediaDescription mMediaDescription; private MediaDescription mMediaDescription;
private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>(); private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
@@ -157,6 +160,11 @@ public class MediaOutputControllerTest extends SysuiTestCase {
when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice1);
mMediaDevices.add(mMediaDevice2); 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.getMediaRoute2Id()).thenReturn(TEST_DEVICE_1_ID);
when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_CLOSE); when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_CLOSE);
@@ -313,6 +321,18 @@ public class MediaOutputControllerTest extends SysuiTestCase {
assertThat(mMediaOutputController.mNeedRefresh).isTrue(); 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 @Test
public void cancelMuteAwaitConnection_cancelsWithMediaManager() { public void cancelMuteAwaitConnection_cancelsWithMediaManager() {
when(mAudioManager.getMutingExpectedDevice()).thenReturn(mock(AudioDeviceAttributes.class)); when(mAudioManager.getMutingExpectedDevice()).thenReturn(mock(AudioDeviceAttributes.class));