diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index 156993d15f310..50f371351f616 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -32,7 +32,7 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION; import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE; -import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_NONE; +import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_TRANSFER; import static android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED; import static android.media.RouteListingPreference.Item.SUBTEXT_CUSTOM; import static android.media.RouteListingPreference.Item.SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED; @@ -209,7 +209,7 @@ public abstract class MediaDevice implements Comparable { @RouteListingPreference.Item.SubText public int getSelectionBehavior() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null - ? mItem.getSelectionBehavior() : SELECTION_BEHAVIOR_NONE; + ? mItem.getSelectionBehavior() : SELECTION_BEHAVIOR_TRANSFER; } /** diff --git a/packages/SystemUI/res/drawable/media_output_status_edit_session.xml b/packages/SystemUI/res/drawable/media_output_status_edit_session.xml new file mode 100644 index 0000000000000..0bd45eda78c4a --- /dev/null +++ b/packages/SystemUI/res/drawable/media_output_status_edit_session.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/media_output_status_session.xml b/packages/SystemUI/res/drawable/media_output_status_session.xml new file mode 100644 index 0000000000000..1deba1ed0f4a9 --- /dev/null +++ b/packages/SystemUI/res/drawable/media_output_status_session.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 d49b9f1058031..a65051206e7b6 100644 --- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml +++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml @@ -152,5 +152,16 @@ android:button="@drawable/media_output_item_check_box" android:visibility="gone" /> + \ No newline at end of file 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 9f5d3722fe0bd..3b456156507ec 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -54,6 +54,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { private static final String TAG = "MediaOutputAdapter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final float DEVICE_DISCONNECTED_ALPHA = 0.5f; + private static final float DEVICE_CONNECTED_ALPHA = 1f; public MediaOutputAdapter(MediaOutputController controller) { super(controller); @@ -201,26 +203,48 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { && mController.isSubStatusSupported() && mController.isAdvancedLayoutSupported() && device.hasSubtext()) { boolean isActiveWithOngoingSession = - device.hasOngoingSession() && currentlyConnected; - if (isActiveWithOngoingSession) { - //Selected device which has ongoing session, disable seekbar since we - //only allow volume control on Host - mSeekBar.setVolume(0); - disableSeekBar(); + (device.hasOngoingSession() && currentlyConnected); + boolean isHost = mController.isVolumeControlEnabled(device) + && isActiveWithOngoingSession; + if (isHost) { mCurrentActivePosition = position; + updateTitleIcon(R.drawable.media_output_icon_volume, + mController.getColorItemContent()); + mSubTitleText.setText(device.getSubtextString()); + updateTwoLineLayoutContentAlpha(DEVICE_CONNECTED_ALPHA); + updateEndClickAreaAsSessionEditing(device); + setTwoLineLayout(device, null /* title */, true /* bFocused */, + true /* showSeekBar */, false /* showProgressBar */, + true /* showSubtitle */, false /* showStatus */, + true /* showEndTouchArea */, false /* isFakeActive */); + initSeekbar(device, isCurrentSeekbarInvisible); + } else { + if (isActiveWithOngoingSession) { + //Selected device which has ongoing session, disable seekbar since we + //only allow volume control on Host + initSeekbar(device, isCurrentSeekbarInvisible); + mCurrentActivePosition = position; + } + setUpDeviceIcon(device); + mSubTitleText.setText(device.getSubtextString()); + Drawable deviceStatusIcon = + isActiveWithOngoingSession ? mContext.getDrawable( + R.drawable.media_output_status_session) + : Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior( + device, + mContext); + if (deviceStatusIcon != null) { + updateDeviceStatusIcon(deviceStatusIcon); + } + updateTwoLineLayoutContentAlpha( + updateClickActionBasedOnSelectionBehavior(device) + ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA); + setTwoLineLayout(device, isActiveWithOngoingSession /* bFocused */, + isActiveWithOngoingSession /* showSeekBar */, + false /* showProgressBar */, true /* showSubtitle */, + deviceStatusIcon != null /* showStatus */, + isActiveWithOngoingSession /* isFakeActive */); } - setUpDeviceIcon(device); - mSubTitleText.setText(device.getSubtextString()); - Drawable deviceStatusIcon = - Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext); - if (deviceStatusIcon != null) { - updateDeviceStatusIcon(deviceStatusIcon); - } - updateClickActionBasedOnSelectionBehavior(device); - setTwoLineLayout(device, isActiveWithOngoingSession /* bFocused */, - isActiveWithOngoingSession /* showSeekBar */, - false /* showProgressBar */, true /* showSubtitle */, - deviceStatusIcon != null /* showStatus */); } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { setUpDeviceIcon(device); updateConnectionFailedStatusIcon(); @@ -228,7 +252,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { updateFullItemClickListener(v -> onItemClick(v, device)); setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */, false /* showProgressBar */, true /* showSubtitle */, - true /* showStatus */); + true /* showStatus */, false /*isFakeActive*/); } else if (device.getState() == MediaDeviceState.STATE_GROUPING) { setUpDeviceIcon(device); updateProgressBarColor(); @@ -317,10 +341,35 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { ColorStateList(states, colors)); } - private void updateClickActionBasedOnSelectionBehavior(MediaDevice device) { + private void updateTwoLineLayoutContentAlpha(float alphaValue) { + mSubTitleText.setAlpha(alphaValue); + mTitleIcon.setAlpha(alphaValue); + mTwoLineTitleText.setAlpha(alphaValue); + mStatusIcon.setAlpha(alphaValue); + } + + private void updateEndClickAreaAsSessionEditing(MediaDevice device) { + mEndClickIcon.setOnClickListener(null); + mEndTouchArea.setOnClickListener(null); + updateEndClickAreaColor(mController.getColorSeekbarProgress()); + mEndClickIcon.setColorFilter(mController.getColorItemContent()); + mEndClickIcon.setOnClickListener( + v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v)); + mEndTouchArea.setOnClickListener(v -> mCheckBox.performClick()); + } + + public void updateEndClickAreaColor(int color) { + if (mController.isAdvancedLayoutSupported()) { + mEndTouchArea.getBackground().setColorFilter( + new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + } + } + + private boolean updateClickActionBasedOnSelectionBehavior(MediaDevice device) { View.OnClickListener clickListener = Api34Impl.getClickListenerBasedOnSelectionBehavior( device, mController, v -> onItemClick(v, device)); updateFullItemClickListener(clickListener); + return clickListener != null; } private void updateConnectionFailedStatusIcon() { @@ -457,7 +506,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { case SELECTION_BEHAVIOR_GO_TO_APP: return v -> controller.tryToLaunchInAppRoutingIntent(device.getId(), v); } - return null; + return defaultTransferListener; } @DoNotInline 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 b5e829e7dcbac..b1cbee8e54a1c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -147,6 +147,7 @@ public abstract class MediaOutputBaseAdapter extends final ImageView mStatusIcon; final CheckBox mCheckBox; final ViewGroup mEndTouchArea; + final ImageView mEndClickIcon; @VisibleForTesting MediaOutputSeekbar mSeekBar; private String mDeviceId; @@ -168,11 +169,13 @@ public abstract class MediaOutputBaseAdapter extends mCheckBox = view.requireViewById(R.id.check_box); mEndTouchArea = view.requireViewById(R.id.end_action_area); if (mController.isAdvancedLayoutSupported()) { + mEndClickIcon = view.requireViewById(R.id.media_output_item_end_click_icon); mVolumeValueText = view.requireViewById(R.id.volume_value); mIconAreaLayout = view.requireViewById(R.id.icon_area); } else { mVolumeValueText = null; mIconAreaLayout = null; + mEndClickIcon = null; } initAnimator(); } @@ -218,20 +221,7 @@ public abstract class MediaOutputBaseAdapter extends .mutate(); mItemLayout.setBackground(backgroundDrawable); if (showSeekBar) { - final ClipDrawable clipDrawable = - (ClipDrawable) ((LayerDrawable) mSeekBar.getProgressDrawable()) - .findDrawableByLayerId(android.R.id.progress); - final GradientDrawable progressDrawable = - (GradientDrawable) clipDrawable.getDrawable(); - if (mController.isAdvancedLayoutSupported()) { - progressDrawable.setCornerRadii( - new float[]{0, 0, mController.getActiveRadius(), - mController.getActiveRadius(), - mController.getActiveRadius(), - mController.getActiveRadius(), 0, 0}); - } else { - progressDrawable.setCornerRadius(mController.getActiveRadius()); - } + updateSeekbarProgressBackground(); } } mItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter( @@ -265,14 +255,15 @@ public abstract class MediaOutputBaseAdapter extends } void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar, - boolean showProgressBar, boolean showSubtitle, boolean showStatus) { + boolean showProgressBar, boolean showSubtitle, boolean showStatus, + boolean isFakeActive) { setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle, - showStatus); + showStatus, false, isFakeActive); } - private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused, + void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused, boolean showSeekBar, boolean showProgressBar, boolean showSubtitle, - boolean showStatus) { + boolean showStatus , boolean showEndTouchArea, boolean isFakeActive) { mTitleText.setVisibility(View.GONE); mTwoLineLayout.setVisibility(View.VISIBLE); mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE); @@ -287,12 +278,21 @@ public abstract class MediaOutputBaseAdapter extends showSeekBar ? mController.getColorConnectedItemBackground() : mController.getColorItemBackground(), PorterDuff.Mode.SRC_IN)); mIconAreaLayout.getBackground().setColorFilter(new PorterDuffColorFilter( - showSeekBar ? mController.getColorConnectedItemBackground() + showProgressBar || isFakeActive + ? mController.getColorConnectedItemBackground() + : showSeekBar ? mController.getColorSeekbarProgress() : mController.getColorItemBackground(), PorterDuff.Mode.SRC_IN)); + if (showSeekBar) { + updateSeekbarProgressBackground(); + } + //update end click area by isActive + mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE); + mEndClickIcon.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams(); - params.rightMargin = mController.getItemMarginEndDefault(); + params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable() + : mController.getItemMarginEndDefault(); } else { backgroundDrawable = mContext.getDrawable( R.drawable.media_output_item_background) @@ -312,6 +312,23 @@ public abstract class MediaOutputBaseAdapter extends Typeface.NORMAL)); } + void updateSeekbarProgressBackground() { + final ClipDrawable clipDrawable = + (ClipDrawable) ((LayerDrawable) mSeekBar.getProgressDrawable()) + .findDrawableByLayerId(android.R.id.progress); + final GradientDrawable progressDrawable = + (GradientDrawable) clipDrawable.getDrawable(); + if (mController.isAdvancedLayoutSupported()) { + progressDrawable.setCornerRadii( + new float[]{0, 0, mController.getActiveRadius(), + mController.getActiveRadius(), + mController.getActiveRadius(), + mController.getActiveRadius(), 0, 0}); + } else { + progressDrawable.setCornerRadius(mController.getActiveRadius()); + } + } + void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) { if (!mController.isVolumeControlEnabled(device)) { disableSeekBar(); 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 d234dff536aed..2aedd36346262 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -704,19 +704,21 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, devices.removeAll(targetMediaDevices); targetMediaDevices.addAll(devices); } - mMediaItemList.clear(); - mMediaItemList.addAll( - targetMediaDevices.stream().map(MediaItem::new).collect(Collectors.toList())); + List finalMediaItems = targetMediaDevices.stream().map( + MediaItem::new).collect(Collectors.toList()); dividerItems.forEach((key, item) -> { - mMediaItemList.add(key, item); + finalMediaItems.add(key, item); }); - mMediaItemList.add(new MediaItem()); + finalMediaItems.add(new MediaItem()); + mMediaItemList.clear(); + mMediaItemList.addAll(finalMediaItems); } } private void categorizeMediaItems(MediaDevice connectedMediaDevice, List devices, boolean needToHandleMutingExpectedDevice) { synchronized (mMediaDevicesLock) { + List finalMediaItems = new ArrayList<>(); Set selectedDevicesIds = getSelectedMediaDevice().stream().map( MediaDevice::getId).collect(Collectors.toSet()); if (connectedMediaDevice != null) { @@ -726,32 +728,32 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, boolean displayGroupAdded = false; for (MediaDevice device : devices) { if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { - mMediaItemList.add(0, new MediaItem(device)); + finalMediaItems.add(0, new MediaItem(device)); } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( device.getId())) { - mMediaItemList.add(0, new MediaItem(device)); + finalMediaItems.add(0, new MediaItem(device)); } else { if (device.isSuggestedDevice() && !suggestedDeviceAdded) { - attachGroupDivider(mContext.getString( + attachGroupDivider(finalMediaItems, mContext.getString( R.string.media_output_group_title_suggested_device)); suggestedDeviceAdded = true; } else if (!device.isSuggestedDevice() && !displayGroupAdded) { - attachGroupDivider(mContext.getString( + attachGroupDivider(finalMediaItems, mContext.getString( R.string.media_output_group_title_speakers_and_displays)); displayGroupAdded = true; } - mMediaItemList.add(new MediaItem(device)); + finalMediaItems.add(new MediaItem(device)); } } - mMediaItemList.add(new MediaItem()); + finalMediaItems.add(new MediaItem()); + mMediaItemList.clear(); + mMediaItemList.addAll(finalMediaItems); } } - private void attachGroupDivider(String title) { - synchronized (mMediaDevicesLock) { - mMediaItemList.add( - new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); - } + private void attachGroupDivider(List mediaItems, String title) { + mediaItems.add( + new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); } private void attachRangeInfo(List 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 003af80f47c46..f779845721b38 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 @@ -435,6 +435,34 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); } + @Test + public void subStatusSupported_onBindViewHolder_bindHostDeviceWithOngoingSession_verifyView() { + when(mMediaOutputController.isSubStatusSupported()).thenReturn(true); + when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true); + when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true); + when(mMediaDevice1.hasSubtext()).thenReturn(true); + when(mMediaDevice1.getSubtext()).thenReturn(SUBTEXT_CUSTOM); + when(mMediaDevice1.getSubtextString()).thenReturn(TEST_CUSTOM_SUBTEXT); + when(mMediaDevice1.hasOngoingSession()).thenReturn(true); + when(mMediaDevice1.getSelectionBehavior()).thenReturn(SELECTION_BEHAVIOR_GO_TO_APP); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT); + assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo( + TEST_DEVICE_NAME_1); + assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse(); + } + @Test public void subStatusSupported_onBindViewHolder_bindDeviceRequirePremium_verifyView() { String deviceStatus = (String) mContext.getText( @@ -505,7 +533,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); - assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT);