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
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index a7e0eec..71c016f 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -18,6 +18,10 @@
 
 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.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 @@
         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<MediaRoute2Info> 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 c412239..4198269 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.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 @@
 
     private MediaDeviceUpdateWorker mWorker;
 
+    @VisibleForTesting
+    MediaRouter2Manager mRouterManager;
+
     public RemoteMediaSlice(Context context) {
         mContext = context;
     }
@@ -80,6 +88,9 @@
             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<RoutingSessionInfo> infos = getWorker().getActiveRemoteMediaDevice();
         if (infos.isEmpty()) {
@@ -98,8 +109,10 @@
                         + 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 @@
                     .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 @@
         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 6d3c96d..bb62a56 100644
--- a/src/com/android/settings/notification/RemoteVolumeGroupController.java
+++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.text.TextUtils;
 
@@ -57,6 +58,8 @@
 
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
+    @VisibleForTesting
+    MediaRouter2Manager mRouterManager;
 
     public RemoteVolumeGroupController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -65,6 +68,7 @@
             mLocalMediaManager.registerCallback(this);
             mLocalMediaManager.startScan();
         }
+        mRouterManager = MediaRouter2Manager.getInstance(context);
     }
 
     @Override
@@ -111,8 +115,10 @@
             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 @@
             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 2aa8418..303fb1b 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.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 @@
 
         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<MediaRoute2Info> 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<MediaRoute2Info> 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 017faa5..e0e6e50 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 android.content.Context;
 import android.content.Intent;
+import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.net.Uri;
 
@@ -87,6 +88,7 @@
         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 6dae2a6..8b46374 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.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 @@
                 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());