From bdcc45e31a05553d1fdd508b45bb370a8dc99711 Mon Sep 17 00:00:00 2001 From: shaoweishen Date: Mon, 5 Dec 2022 12:07:02 +0000 Subject: [PATCH] [Output Switcher] Update device selection layout Update device selection layout to match with UX markup. 1. divide device item part with group part, to allow both transfer and grouping. 2. update checkbox image to match with markup. guard this change by flag. Bug: 249371764 Bug: 255126338 Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest Change-Id: I6e92a40f25ea940286cb5ca49aa0193fd9f406a5 --- .../res/drawable/ic_circle_check_box.xml | 2 +- .../drawable/media_output_item_check_box.xml | 26 +++++ .../drawable/media_output_status_checked.xml | 26 +++++ ...=> media_output_status_filled_checked.xml} | 0 .../media_output_status_selectable.xml | 26 +++++ .../media_output_list_item_advanced.xml | 58 +++++----- packages/SystemUI/res/values/dimens.xml | 2 + .../media/dialog/MediaOutputAdapter.java | 26 ++++- .../media/dialog/MediaOutputBaseAdapter.java | 12 ++- .../media/dialog/MediaOutputController.java | 14 +++ .../media/dialog/MediaOutputAdapterTest.java | 102 +++++++++++++++++- 11 files changed, 255 insertions(+), 39 deletions(-) create mode 100644 packages/SystemUI/res/drawable/media_output_item_check_box.xml create mode 100644 packages/SystemUI/res/drawable/media_output_status_checked.xml rename packages/SystemUI/res/drawable/{media_output_status_check.xml => media_output_status_filled_checked.xml} (100%) create mode 100644 packages/SystemUI/res/drawable/media_output_status_selectable.xml diff --git a/packages/SystemUI/res/drawable/ic_circle_check_box.xml b/packages/SystemUI/res/drawable/ic_circle_check_box.xml index b44a32dda1723..00c10cecfe952 100644 --- a/packages/SystemUI/res/drawable/ic_circle_check_box.xml +++ b/packages/SystemUI/res/drawable/ic_circle_check_box.xml @@ -18,7 +18,7 @@ + android:drawable="@drawable/media_output_status_filled_checked" /> + + + + + diff --git a/packages/SystemUI/res/drawable/media_output_status_checked.xml b/packages/SystemUI/res/drawable/media_output_status_checked.xml new file mode 100644 index 0000000000000..8f83ee2fa1609 --- /dev/null +++ b/packages/SystemUI/res/drawable/media_output_status_checked.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/media_output_status_check.xml b/packages/SystemUI/res/drawable/media_output_status_filled_checked.xml similarity index 100% rename from packages/SystemUI/res/drawable/media_output_status_check.xml rename to packages/SystemUI/res/drawable/media_output_status_filled_checked.xml diff --git a/packages/SystemUI/res/drawable/media_output_status_selectable.xml b/packages/SystemUI/res/drawable/media_output_status_selectable.xml new file mode 100644 index 0000000000000..5465aa7300abb --- /dev/null +++ b/packages/SystemUI/res/drawable/media_output_status_selectable.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml index 9d0bfd74af8ee..d49b9f1058031 100644 --- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml +++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml @@ -1,6 +1,6 @@ - + android:layout_height="wrap_content"> - - - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3bec72ecd2b36..f3d2638c4ee7c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1208,6 +1208,8 @@ 20dp 16dp 28dp + 16dp + 80dp 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 72d4e947b30fc..3dccae03e906a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -166,6 +166,20 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { setUpDeviceIcon(device); updateFullItemClickListener(v -> cancelMuteAwaitConnection()); setSingleLineLayout(getItemTitle(device)); + } else if (mController.isCurrentConnectedDeviceRemote() + && !mController.getSelectableMediaDevice().isEmpty() + && mController.isAdvancedLayoutSupported()) { + //If device is connected and there's other selectable devices, layout as + // one of selected devices. + boolean isDeviceDeselectable = isDeviceIncluded( + mController.getDeselectableMediaDevice(), device); + updateGroupableCheckBox(true, isDeviceDeselectable, device); + updateEndClickArea(device, isDeviceDeselectable); + setUpContentDescriptionForView(mContainerLayout, false, device); + setSingleLineLayout(getItemTitle(device), true /* showSeekBar */, + false /* showProgressBar */, true /* showCheckBox */, + true /* showEndTouchArea */); + initSeekbar(device, isCurrentSeekbarInvisible); } else { updateTitleIcon(R.drawable.media_output_icon_volume, mController.getColorItemContent()); @@ -179,7 +193,12 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { setUpDeviceIcon(device); updateGroupableCheckBox(false, true, device); - updateFullItemClickListener(v -> onGroupActionTriggered(true, device)); + if (mController.isAdvancedLayoutSupported()) { + updateEndClickArea(device, true); + } + updateFullItemClickListener(mController.isAdvancedLayoutSupported() + ? v -> onItemClick(v, device) + : v -> onGroupActionTriggered(true, device)); setSingleLineLayout(getItemTitle(device), false /* showSeekBar */, false /* showProgressBar */, true /* showCheckBox */, true /* showEndTouchArea */); @@ -217,6 +236,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { isDeviceDeselectable ? (v) -> mCheckBox.performClick() : null); mEndTouchArea.setImportantForAccessibility( View.IMPORTANT_FOR_ACCESSIBILITY_YES); + if (mController.isAdvancedLayoutSupported()) { + mEndTouchArea.getBackground().setColorFilter( + new PorterDuffColorFilter(mController.getColorItemBackground(), + PorterDuff.Mode.SRC_IN)); + } setUpContentDescriptionForView(mEndTouchArea, true, device); } 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 8987e7ca9efb4..db62e51be7e7a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -133,7 +133,7 @@ public abstract class MediaOutputBaseAdapter extends private static final int ANIM_DURATION = 500; - final LinearLayout mContainerLayout; + final ViewGroup mContainerLayout; final FrameLayout mItemLayout; final FrameLayout mIconAreaLayout; final TextView mTitleText; @@ -146,7 +146,7 @@ public abstract class MediaOutputBaseAdapter extends final LinearLayout mTwoLineLayout; final ImageView mStatusIcon; final CheckBox mCheckBox; - final LinearLayout mEndTouchArea; + final ViewGroup mEndTouchArea; private String mDeviceId; private ValueAnimator mCornerAnimator; private ValueAnimator mVolumeAnimator; @@ -253,6 +253,13 @@ public abstract class MediaOutputBaseAdapter extends mTitleText.setVisibility(View.VISIBLE); mCheckBox.setVisibility(showCheckBox ? View.VISIBLE : View.GONE); mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE); + if (mController.isAdvancedLayoutSupported()) { + ViewGroup.MarginLayoutParams params = + (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams(); + params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable() + : mController.getItemMarginEndDefault(); + } + mTitleIcon.setColorFilter(mController.getColorItemContent()); } void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar, @@ -531,6 +538,7 @@ public abstract class MediaOutputBaseAdapter extends return; } mTitleIcon.setImageIcon(icon); + mTitleIcon.setColorFilter(mController.getColorItemContent()); }); }); } 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 ecfc0e6d75740..9b361e3edc57b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -147,6 +147,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, private int mColorConnectedItemBackground; private int mColorPositiveButtonText; private int mColorDialogBackground; + private int mItemMarginEndDefault; + private int mItemMarginEndSelectable; private float mInactiveRadius; private float mActiveRadius; private FeatureFlags mFeatureFlags; @@ -200,6 +202,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, R.dimen.media_output_dialog_active_background_radius); mColorDialogBackground = Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_background); + mItemMarginEndDefault = (int) mContext.getResources().getDimension( + R.dimen.media_output_dialog_default_margin_end); + mItemMarginEndSelectable = (int) mContext.getResources().getDimension( + R.dimen.media_output_dialog_selectable_margin_end); } void start(@NonNull Callback cb) { @@ -532,6 +538,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mActiveRadius; } + public int getItemMarginEndDefault() { + return mItemMarginEndDefault; + } + + public int getItemMarginEndSelectable() { + return mItemMarginEndSelectable; + } + private void buildMediaDevices(List devices) { synchronized (mMediaDevicesLock) { attachRangeInfo(devices); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 5f643363bd2e2..5c0f0fee096aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -38,6 +38,8 @@ import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,11 +75,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Before public void setUp() { - mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); - mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter - .onCreateViewHolder(new LinearLayout(mContext), 0); - mSpyMediaOutputSeekbar = spy(mViewHolder.mSeekBar); - when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false); @@ -85,6 +82,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat); when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1); when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true); + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false); when(mIconCompat.toIcon(mContext)).thenReturn(mIcon); when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1); when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1); @@ -96,6 +94,11 @@ public class MediaOutputAdapterTest extends SysuiTestCase { LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice2); + + mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mSpyMediaOutputSeekbar = spy(mViewHolder.mSeekBar); } @Test @@ -168,6 +171,63 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); } + @Test + public void advanced_onBindViewHolder_bindNonRemoteConnectedDevice_verifyView() { + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true); + when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(false); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void advanced_onBindViewHolder_bindConnectedRemoteDevice_verifyView() { + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true); + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn( + ImmutableList.of(mMediaDevice2)); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void advanced_onBindViewHolder_bindSingleConnectedRemoteDevice_verifyView() { + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true); + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn( + ImmutableList.of()); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE); + } + @Test public void onBindViewHolder_bindConnectedDeviceWithMutingExpectedDeviceExist_verifyView() { when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true); @@ -351,6 +411,38 @@ public class MediaOutputAdapterTest extends SysuiTestCase { verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2); } + @Test + public void advanced_onGroupActionTriggered_clicksEndAreaOfSelectableDevice_triggerGrouping() { + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true); + List selectableDevices = new ArrayList<>(); + selectableDevices.add(mMediaDevice2); + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); + + mViewHolder.mEndTouchArea.performClick(); + + verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2); + } + + @Test + public void advanced_onGroupActionTriggered_clickSelectedRemoteDevice_triggerUngrouping() { + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true); + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn( + ImmutableList.of(mMediaDevice2)); + when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn( + ImmutableList.of(mMediaDevice1)); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + mViewHolder.mEndTouchArea.performClick(); + + verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1); + } + @Test public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() { when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);