Loading packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +22 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; import static android.media.MediaRoute2Info.TYPE_USB_HEADSET; import static android.media.MediaRoute2Info.TYPE_USB_HEADSET; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static android.media.RouteListingPreference.Item.DISABLE_REASON_NONE; import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE; import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE; import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED; import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED; Loading Loading @@ -192,6 +193,27 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { */ */ public abstract String getId(); public abstract String getId(); /** * Get disabled reason of device * * @return disabled reason of device */ @RouteListingPreference.Item.DisableReason public int getDisableReason() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null ? mItem.getDisableReason() : -1; } /** * Checks if device is has disabled reason * * @return true if device has disabled reason */ public boolean hasDisabledReason() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null && mItem.getDisableReason() != DISABLE_REASON_NONE; } /** /** * Checks if device is suggested device from application * Checks if device is suggested device from application * * Loading packages/SystemUI/res/values/strings.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -2522,6 +2522,8 @@ <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] --> <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] --> <string name="media_output_group_title_suggested_device">Suggested Devices</string> <string name="media_output_group_title_suggested_device">Suggested Devices</string> <!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] --> <string name="media_output_status_require_premium">Requires premium account</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] --> Loading packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +31 −0 Original line number Original line Diff line number Diff line Loading @@ -16,17 +16,24 @@ package com.android.systemui.media.dialog; package com.android.systemui.media.dialog; import static android.media.RouteListingPreference.Item.DISABLE_REASON_SUBSCRIPTION_REQUIRED; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.ColorStateList; import android.graphics.PorterDuff; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.media.RouteListingPreference; import android.os.Build; import android.util.Log; import android.util.Log; import android.view.View; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CheckBox; import android.widget.TextView; import android.widget.TextView; import androidx.annotation.DoNotInline; import androidx.annotation.NonNull; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.widget.CompoundButtonCompat; import androidx.core.widget.CompoundButtonCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView; Loading Loading @@ -186,6 +193,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mCurrentActivePosition = position; mCurrentActivePosition = position; updateFullItemClickListener(v -> onItemClick(v, device)); updateFullItemClickListener(v -> onItemClick(v, device)); setSingleLineLayout(getItemTitle(device)); setSingleLineLayout(getItemTitle(device)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && mController.isSubStatusSupported() && device.hasDisabledReason()) { //update to subtext with device status setUpDeviceIcon(device); mSubTitleText.setText( Api34Impl.composeDisabledReason(device.getDisableReason(), mContext)); updateConnectionFailedStatusIcon(); updateFullItemClickListener(null); setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */, false /* showProgressBar */, true /* showSubtitle */, true /* showStatus */); } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { setUpDeviceIcon(device); setUpDeviceIcon(device); updateConnectionFailedStatusIcon(); updateConnectionFailedStatusIcon(); Loading Loading @@ -389,4 +407,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mTitleText.setText(groupDividerTitle); mTitleText.setText(groupDividerTitle); } } } } @RequiresApi(34) private static class Api34Impl { @DoNotInline static String composeDisabledReason(@RouteListingPreference.Item.DisableReason int reason, Context context) { switch(reason) { case DISABLE_REASON_SUBSCRIPTION_REQUIRED: return context.getString(R.string.media_output_status_require_premium); } return ""; } } } } packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -748,6 +748,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING); return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING); } } public boolean isSubStatusSupported() { return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_DEVICE_STATUS); } List<MediaDevice> getGroupMediaDevices() { List<MediaDevice> getGroupMediaDevices() { final List<MediaDevice> selectedDevices = getSelectedMediaDevice(); final List<MediaDevice> selectedDevices = getSelectedMediaDevice(); final List<MediaDevice> selectableDevices = getSelectableMediaDevice(); final List<MediaDevice> selectableDevices = getSelectableMediaDevice(); Loading packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +23 −0 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.media.dialog; package com.android.systemui.media.dialog; import static android.media.RouteListingPreference.Item.DISABLE_REASON_SUBSCRIPTION_REQUIRED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; Loading Loading @@ -57,6 +59,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { private static final String TEST_DEVICE_ID_1 = "test_device_id_1"; private static final String TEST_DEVICE_ID_1 = "test_device_id_1"; private static final String TEST_DEVICE_ID_2 = "test_device_id_2"; private static final String TEST_DEVICE_ID_2 = "test_device_id_2"; private static final String TEST_SESSION_NAME = "test_session_name"; private static final String TEST_SESSION_NAME = "test_session_name"; private static final int TEST_MAX_VOLUME = 20; private static final int TEST_MAX_VOLUME = 20; private static final int TEST_CURRENT_VOLUME = 10; private static final int TEST_CURRENT_VOLUME = 10; Loading @@ -78,6 +81,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Before @Before public void setUp() { public void setUp() { when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false); when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false); when(mMediaOutputController.isSubStatusSupported()).thenReturn(false); when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems); when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); Loading Loading @@ -403,6 +407,25 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); } } @Test public void subStatusSupported_onBindViewHolder_bindFailedStateDevice_verifyView() { String deviceStatus = (String) mContext.getText( R.string.media_output_status_require_premium); when(mMediaOutputController.isSubStatusSupported()).thenReturn(true); when(mMediaDevice2.hasDisabledReason()).thenReturn(true); when(mMediaDevice2.getDisableReason()).thenReturn(DISABLE_REASON_SUBSCRIPTION_REQUIRED); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(deviceStatus); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); } @Test @Test public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() { public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() { when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true); when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true); Loading Loading
packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +22 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; import static android.media.MediaRoute2Info.TYPE_USB_HEADSET; import static android.media.MediaRoute2Info.TYPE_USB_HEADSET; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static android.media.RouteListingPreference.Item.DISABLE_REASON_NONE; import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE; import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE; import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED; import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED; Loading Loading @@ -192,6 +193,27 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { */ */ public abstract String getId(); public abstract String getId(); /** * Get disabled reason of device * * @return disabled reason of device */ @RouteListingPreference.Item.DisableReason public int getDisableReason() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null ? mItem.getDisableReason() : -1; } /** * Checks if device is has disabled reason * * @return true if device has disabled reason */ public boolean hasDisabledReason() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null && mItem.getDisableReason() != DISABLE_REASON_NONE; } /** /** * Checks if device is suggested device from application * Checks if device is suggested device from application * * Loading
packages/SystemUI/res/values/strings.xml +2 −0 Original line number Original line Diff line number Diff line Loading @@ -2522,6 +2522,8 @@ <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <string name="media_output_group_title_speakers_and_displays">Speakers & Displays</string> <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] --> <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] --> <string name="media_output_group_title_suggested_device">Suggested Devices</string> <string name="media_output_group_title_suggested_device">Suggested Devices</string> <!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] --> <string name="media_output_status_require_premium">Requires premium account</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] --> Loading
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +31 −0 Original line number Original line Diff line number Diff line Loading @@ -16,17 +16,24 @@ package com.android.systemui.media.dialog; package com.android.systemui.media.dialog; import static android.media.RouteListingPreference.Item.DISABLE_REASON_SUBSCRIPTION_REQUIRED; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.ColorStateList; import android.graphics.PorterDuff; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.media.RouteListingPreference; import android.os.Build; import android.util.Log; import android.util.Log; import android.view.View; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CheckBox; import android.widget.TextView; import android.widget.TextView; import androidx.annotation.DoNotInline; import androidx.annotation.NonNull; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.widget.CompoundButtonCompat; import androidx.core.widget.CompoundButtonCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView; Loading Loading @@ -186,6 +193,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mCurrentActivePosition = position; mCurrentActivePosition = position; updateFullItemClickListener(v -> onItemClick(v, device)); updateFullItemClickListener(v -> onItemClick(v, device)); setSingleLineLayout(getItemTitle(device)); setSingleLineLayout(getItemTitle(device)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && mController.isSubStatusSupported() && device.hasDisabledReason()) { //update to subtext with device status setUpDeviceIcon(device); mSubTitleText.setText( Api34Impl.composeDisabledReason(device.getDisableReason(), mContext)); updateConnectionFailedStatusIcon(); updateFullItemClickListener(null); setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */, false /* showProgressBar */, true /* showSubtitle */, true /* showStatus */); } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { setUpDeviceIcon(device); setUpDeviceIcon(device); updateConnectionFailedStatusIcon(); updateConnectionFailedStatusIcon(); Loading Loading @@ -389,4 +407,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mTitleText.setText(groupDividerTitle); mTitleText.setText(groupDividerTitle); } } } } @RequiresApi(34) private static class Api34Impl { @DoNotInline static String composeDisabledReason(@RouteListingPreference.Item.DisableReason int reason, Context context) { switch(reason) { case DISABLE_REASON_SUBSCRIPTION_REQUIRED: return context.getString(R.string.media_output_status_require_premium); } return ""; } } } }
packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -748,6 +748,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING); return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING); } } public boolean isSubStatusSupported() { return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_DEVICE_STATUS); } List<MediaDevice> getGroupMediaDevices() { List<MediaDevice> getGroupMediaDevices() { final List<MediaDevice> selectedDevices = getSelectedMediaDevice(); final List<MediaDevice> selectedDevices = getSelectedMediaDevice(); final List<MediaDevice> selectableDevices = getSelectableMediaDevice(); final List<MediaDevice> selectableDevices = getSelectableMediaDevice(); Loading
packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +23 −0 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.media.dialog; package com.android.systemui.media.dialog; import static android.media.RouteListingPreference.Item.DISABLE_REASON_SUBSCRIPTION_REQUIRED; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; Loading Loading @@ -57,6 +59,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { private static final String TEST_DEVICE_ID_1 = "test_device_id_1"; private static final String TEST_DEVICE_ID_1 = "test_device_id_1"; private static final String TEST_DEVICE_ID_2 = "test_device_id_2"; private static final String TEST_DEVICE_ID_2 = "test_device_id_2"; private static final String TEST_SESSION_NAME = "test_session_name"; private static final String TEST_SESSION_NAME = "test_session_name"; private static final int TEST_MAX_VOLUME = 20; private static final int TEST_MAX_VOLUME = 20; private static final int TEST_CURRENT_VOLUME = 10; private static final int TEST_CURRENT_VOLUME = 10; Loading @@ -78,6 +81,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Before @Before public void setUp() { public void setUp() { when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false); when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false); when(mMediaOutputController.isSubStatusSupported()).thenReturn(false); when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems); when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); Loading Loading @@ -403,6 +407,25 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); } } @Test public void subStatusSupported_onBindViewHolder_bindFailedStateDevice_verifyView() { String deviceStatus = (String) mContext.getText( R.string.media_output_status_require_premium); when(mMediaOutputController.isSubStatusSupported()).thenReturn(true); when(mMediaDevice2.hasDisabledReason()).thenReturn(true); when(mMediaDevice2.getDisableReason()).thenReturn(DISABLE_REASON_SUBSCRIPTION_REQUIRED); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(deviceStatus); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); } @Test @Test public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() { public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() { when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true); when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true); Loading