From b6840ced0d735ce09dfea6a357bfab0a736b5873 Mon Sep 17 00:00:00 2001 From: Hugh Chen Date: Thu, 24 Sep 2020 17:57:19 +0800 Subject: [PATCH] Disable entry point of output switcher It adds a minimum value if it shows only one available cast device in the output switcher. Because users can only change the volume slider or stop control in the output switcher. It's too hidden to have the user stop cast in the UI. - This CL will disable the entry point of the output switcher if there is only one available cast device in the list. - Update test cases. Bug: 163095048 Test: make -j42 RunSettingsRoboTests Change-Id: I8906878e1ba769d6940041f17d83b5de6b2a32c0 --- src/com/android/settings/Utils.java | 33 +++++++++++++++++ .../settings/media/RemoteMediaSlice.java | 36 ++++++++++++++++--- .../RemoteVolumeGroupController.java | 13 +++++-- .../src/com/android/settings/UtilsTest.java | 31 ++++++++++++++++ .../settings/media/RemoteMediaSliceTest.java | 2 ++ .../RemoteVolumeGroupControllerTest.java | 2 ++ 6 files changed, 110 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index a7e0eece410..71c016f6f47 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -18,6 +18,10 @@ package com.android.settings; import static android.content.Intent.EXTRA_USER; import static android.content.Intent.EXTRA_USER_ID; +import static android.media.MediaRoute2Info.TYPE_GROUP; +import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER; +import static android.media.MediaRoute2Info.TYPE_REMOTE_TV; +import static android.media.MediaRoute2Info.TYPE_UNKNOWN; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; @@ -53,6 +57,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; +import android.media.MediaRoute2Info; +import android.media.MediaRouter2Manager; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.Network; @@ -1137,4 +1143,31 @@ public final class Utils extends com.android.settingslib.Utils { drawable.draw(canvas); return roundedBitmap; } + + /** + * Returns {@code true} if needed to disable media output, otherwise returns {@code false}. + */ + public static boolean isMediaOutputDisabled( + MediaRouter2Manager router2Manager, String packageName) { + boolean isMediaOutputDisabled = false; + if (!TextUtils.isEmpty(packageName)) { + final List infos = router2Manager.getAvailableRoutes(packageName); + if (infos.size() == 1) { + final MediaRoute2Info info = infos.get(0); + final int deviceType = info.getType(); + switch (deviceType) { + case TYPE_UNKNOWN: + case TYPE_REMOTE_TV: + case TYPE_REMOTE_SPEAKER: + case TYPE_GROUP: + isMediaOutputDisabled = true; + break; + default: + isMediaOutputDisabled = false; + break; + } + } + } + return isMediaOutputDisabled; + } } diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java index c4122393c8d..419826966da 100644 --- a/src/com/android/settings/media/RemoteMediaSlice.java +++ b/src/com/android/settings/media/RemoteMediaSlice.java @@ -17,6 +17,7 @@ package com.android.settings.media; import static android.app.slice.Slice.EXTRA_RANGE_VALUE; +import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; import static com.android.settings.slices.CustomSliceRegistry.REMOTE_MEDIA_SLICE_URI; @@ -24,11 +25,15 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.media.MediaRouter2Manager; import android.media.RoutingSessionInfo; import android.net.Uri; +import android.text.SpannableString; import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; import android.util.Log; +import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.builders.ListBuilder; @@ -59,6 +64,9 @@ public class RemoteMediaSlice implements CustomSliceable { private MediaDeviceUpdateWorker mWorker; + @VisibleForTesting + MediaRouter2Manager mRouterManager; + public RemoteMediaSlice(Context context) { mContext = context; } @@ -80,6 +88,9 @@ public class RemoteMediaSlice implements CustomSliceable { Log.e(TAG, "Unable to get the slice worker."); return listBuilder.build(); } + if (mRouterManager == null) { + mRouterManager = MediaRouter2Manager.getInstance(mContext); + } // Only displaying remote devices final List infos = getWorker().getActiveRemoteMediaDevice(); if (infos.isEmpty()) { @@ -98,8 +109,10 @@ public class RemoteMediaSlice implements CustomSliceable { + maxVolume); continue; } + final CharSequence appName = Utils.getApplicationLabel( + mContext, info.getClientPackageName()); final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title, - Utils.getApplicationLabel(mContext, info.getClientPackageName())); + appName); listBuilder.addInputRange(new InputRangeBuilder() .setTitleItem(icon, ListBuilder.ICON_IMAGE) .setTitle(castVolume) @@ -107,11 +120,21 @@ public class RemoteMediaSlice implements CustomSliceable { .setPrimaryAction(getSoundSettingAction(castVolume, icon, info.getId())) .setMax(maxVolume) .setValue(info.getVolume())); + + final boolean isMediaOutputDisabled = + Utils.isMediaOutputDisabled(mRouterManager, info.getClientPackageName()); + final SpannableString spannableTitle = new SpannableString( + TextUtils.isEmpty(appName) ? "" : appName); + spannableTitle.setSpan(new ForegroundColorSpan( + Utils.getColorAttrDefaultColor( + mContext, android.R.attr.textColorSecondary)), 0, + spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); listBuilder.addRow(new ListBuilder.RowBuilder() - .setTitle(outputTitle) + .setTitle(isMediaOutputDisabled ? spannableTitle : outputTitle) .setSubtitle(info.getName()) .setTitleItem(emptyIcon, ListBuilder.ICON_IMAGE) - .setPrimaryAction(getMediaOutputSliceAction(info.getClientPackageName()))); + .setPrimaryAction(getMediaOutputSliceAction( + info.getClientPackageName(), isMediaOutputDisabled))); } return listBuilder.build(); } @@ -144,9 +167,12 @@ public class RemoteMediaSlice implements CustomSliceable { return primarySliceAction; } - private SliceAction getMediaOutputSliceAction(String packageName) { + private SliceAction getMediaOutputSliceAction( + String packageName, boolean isMediaOutputDisabled) { final Intent intent = new Intent() - .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) + .setAction(isMediaOutputDisabled + ? "" + : MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, packageName); final IconCompat icon = IconCompat.createWithResource(mContext, diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java index 6d3c96dcc3d..bb62a567d58 100644 --- a/src/com/android/settings/notification/RemoteVolumeGroupController.java +++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java @@ -18,6 +18,7 @@ package com.android.settings.notification; import android.content.Context; import android.content.Intent; +import android.media.MediaRouter2Manager; import android.media.RoutingSessionInfo; import android.text.TextUtils; @@ -57,6 +58,8 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem @VisibleForTesting LocalMediaManager mLocalMediaManager; + @VisibleForTesting + MediaRouter2Manager mRouterManager; public RemoteVolumeGroupController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -65,6 +68,7 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem mLocalMediaManager.registerCallback(this); mLocalMediaManager.startScan(); } + mRouterManager = MediaRouter2Manager.getInstance(context); } @Override @@ -111,8 +115,10 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem if (mPreferenceCategory.findPreference(info.getId()) != null) { continue; } + final CharSequence appName = Utils.getApplicationLabel( + mContext, info.getClientPackageName()); final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title, - Utils.getApplicationLabel(mContext, info.getClientPackageName())); + appName); // Add slider final RemoteVolumeSeekBarPreference seekBarPreference = new RemoteVolumeSeekBarPreference(mContext); @@ -125,10 +131,13 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem seekBarPreference.setIcon(R.drawable.ic_volume_remote); mPreferenceCategory.addPreference(seekBarPreference); // Add output indicator + final boolean isMediaOutputDisabled = Utils.isMediaOutputDisabled( + mRouterManager, info.getClientPackageName()); final Preference preference = new Preference(mContext); preference.setKey(SWITCHER_PREFIX + info.getId()); - preference.setTitle(outputTitle); + preference.setTitle(isMediaOutputDisabled ? appName : outputTitle); preference.setSummary(info.getName()); + preference.setEnabled(!isMediaOutputDisabled); mPreferenceCategory.addPreference(preference); } } diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index 2aa8418227f..303fb1bacce 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -40,6 +40,8 @@ import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.VectorDrawable; +import android.media.MediaRoute2Info; +import android.media.MediaRouter2Manager; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; @@ -299,4 +301,33 @@ public class UtilsTest { assertThat(Utils.isSettingsIntelligence(mContext)).isFalse(); } + + @Test + public void isMediaOutputDisabled_infosSizeEqual1_returnsTrue() { + final MediaRouter2Manager router2Manager = mock(MediaRouter2Manager.class); + final MediaRoute2Info info = mock(MediaRoute2Info.class); + final List infos = new ArrayList<>(); + infos.add(info); + + when(router2Manager.getAvailableRoutes(anyString())).thenReturn(infos); + when(info.getType()).thenReturn(0); + + assertThat(Utils.isMediaOutputDisabled(router2Manager, "test")).isTrue(); + } + + @Test + public void isMediaOutputDisabled_infosSizeOverThan1_returnsFalse() { + final MediaRouter2Manager router2Manager = mock(MediaRouter2Manager.class); + final MediaRoute2Info info = mock(MediaRoute2Info.class); + final MediaRoute2Info info2 = mock(MediaRoute2Info.class); + final List infos = new ArrayList<>(); + infos.add(info); + infos.add(info2); + + when(router2Manager.getAvailableRoutes(anyString())).thenReturn(infos); + when(info.getType()).thenReturn(0); + when(info2.getType()).thenReturn(0); + + assertThat(Utils.isMediaOutputDisabled(router2Manager, "test")).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java index 017faa5568f..e0e6e5018ec 100644 --- a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java +++ b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; +import android.media.MediaRouter2Manager; import android.media.RoutingSessionInfo; import android.net.Uri; @@ -87,6 +88,7 @@ public class RemoteMediaSliceTest { SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mRemoteMediaSlice = new RemoteMediaSlice(mContext); + mRemoteMediaSlice.mRouterManager = mock(MediaRouter2Manager.class); sMediaDeviceUpdateWorker = spy(new MediaDeviceUpdateWorker(mContext, REMOTE_MEDIA_SLICE_URI)); sMediaDeviceUpdateWorker.mLocalMediaManager = mLocalMediaManager; diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java index 6dae2a6390d..8b46374b684 100644 --- a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java @@ -31,6 +31,7 @@ import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageStats; +import android.media.MediaRouter2Manager; import android.media.RoutingSessionInfo; import android.media.session.MediaSessionManager; @@ -100,6 +101,7 @@ public class RemoteVolumeGroupControllerTest { Context.MEDIA_SESSION_SERVICE); mController = new RemoteVolumeGroupController(mContext, KEY_REMOTE_VOLUME_GROUP); mController.mLocalMediaManager = mLocalMediaManager; + mController.mRouterManager = mock(MediaRouter2Manager.class); mPreferenceCategory = spy(new PreferenceCategory(mContext)); mPreferenceCategory.setKey(mController.getPreferenceKey());