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