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