diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9260201af6a..de4ae39e84d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1922,6 +1922,7 @@ + diff --git a/res/layout/adb_qrcode_scanner_fragment.xml b/res/layout/adb_qrcode_scanner_fragment.xml index f37c9a6ff4c..9a337d99859 100644 --- a/res/layout/adb_qrcode_scanner_fragment.xml +++ b/res/layout/adb_qrcode_scanner_fragment.xml @@ -19,7 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:theme="@style/GlifV3Theme.Light" + android:theme="@style/GlifV3Theme" android:icon="@drawable/ic_scan_32dp"> 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/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index f2d60f19814..8eb5c4f6c19 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -16,6 +16,8 @@ package com.android.settings.development; +import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES; + import android.app.Activity; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothA2dp; @@ -23,12 +25,14 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.SystemProperties; import android.os.UserManager; +import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -41,6 +45,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; +import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.development.autofill.AutofillLoggingLevelPreferenceController; import com.android.settings.development.autofill.AutofillResetOptionsPreferenceController; @@ -52,6 +57,7 @@ import com.android.settings.development.bluetooth.BluetoothCodecDialogPreference import com.android.settings.development.bluetooth.BluetoothHDAudioPreferenceController; import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferenceController; import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController; +import com.android.settings.development.qstile.DevelopmentTiles; import com.android.settings.development.storage.SharedDataPreferenceController; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.SwitchBar; @@ -199,11 +205,42 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra // Restore UI state based on whether developer options is enabled if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) { enableDeveloperOptions(); + handleQsTileLongPressActionIfAny(); } else { disableDeveloperOptions(); } } + /** + * Long-pressing a developer options quick settings tile will by default (see + * QS_TILE_PREFERENCES in the manifest) take you to the developer options page. + * Some tiles may want to go into their own page within the developer options. + */ + private void handleQsTileLongPressActionIfAny() { + Intent intent = getActivity().getIntent(); + if (intent == null || !TextUtils.equals(ACTION_QS_TILE_PREFERENCES, intent.getAction())) { + return; + } + + Log.d(TAG, "Developer options started from qstile long-press"); + final ComponentName componentName = (ComponentName) intent.getParcelableExtra( + Intent.EXTRA_COMPONENT_NAME); + if (componentName == null) { + return; + } + + if (DevelopmentTiles.WirelessDebugging.class.getName().equals( + componentName.getClassName()) && getDevelopmentOptionsController( + WirelessDebuggingPreferenceController.class).isAvailable()) { + Log.d(TAG, "Long press from wireless debugging qstile"); + new SubSettingLauncher(getContext()) + .setDestination(WirelessDebuggingFragment.class.getName()) + .setSourceMetricsCategory(SettingsEnums.SETTINGS_ADB_WIRELESS) + .launch(); + } + // Add other qstiles here + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java index 00755296d0a..fc39b59e33e 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java @@ -243,7 +243,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(); - //navigate back to the homepage, screen rotate or after card dismissal + // navigate back to the homepage, screen rotate or after card dismissal if (!mIsFirstLaunch) { onContextualCardUpdated(cardsToKeep.stream() .collect(groupingBy(ContextualCard::getCardType))); @@ -266,8 +266,17 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo SettingsEnums.ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT, SettingsEnums.SETTINGS_HOMEPAGE, null /* key */, (int) loadTime /* value */); + + // display a card on timeout if the one-card space is pre-allocated + if (!cards.isEmpty() && ContextualCardLoader.getCardCount(mContext) == 1) { + onContextualCardUpdated(cards.stream() + .collect(groupingBy(ContextualCard::getCardType))); + metricsFeatureProvider.action(mContext, + SettingsEnums.ACTION_CONTEXTUAL_CARD_SHOW, + ContextualCardLogUtils.buildCardListLog(cards)); + } } - //only log homepage display upon a fresh launch + // only log homepage display upon a fresh launch final long totalTime = System.currentTimeMillis() - mStartTime; metricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CONTEXTUAL_HOME_SHOW, (int) totalTime); diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java index 17c8ef8a9fa..305c7df1913 100644 --- a/src/com/android/settings/media/MediaOutputIndicatorSlice.java +++ b/src/com/android/settings/media/MediaOutputIndicatorSlice.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.media.session.MediaController; import android.net.Uri; +import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; @@ -62,8 +63,12 @@ public class MediaOutputIndicatorSlice implements CustomSliceable { com.android.internal.R.drawable.ic_settings_bluetooth); final CharSequence title = mContext.getString(R.string.media_output_label_title, Utils.getApplicationLabel(mContext, getWorker().getPackageName())); + final int requestCode = TextUtils.isEmpty(getWorker().getPackageName()) + ? 0 + : getWorker().getPackageName().hashCode(); final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, - 0 /* requestCode */, getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT); + requestCode, + getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT); final SliceAction primarySliceAction = SliceAction.createDeeplink( primaryActionIntent, icon, ListBuilder.ICON_IMAGE, title); @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java index b9d43b465b9..6611e8d1cc8 100644 --- a/src/com/android/settings/media/MediaOutputSlice.java +++ b/src/com/android/settings/media/MediaOutputSlice.java @@ -257,9 +257,11 @@ public class MediaOutputSlice implements CustomSliceable { .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, getWorker().getPackageName()); - + final int requestCode = TextUtils.isEmpty(getWorker().getPackageName()) + ? 0 + : getWorker().getPackageName().hashCode(); return SliceAction.createDeeplink( - PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */), + PendingIntent.getActivity(mContext, requestCode, intent, 0 /* flags */), IconCompat.createWithResource(mContext, R.drawable.ic_add_blue_24dp), ListBuilder.ICON_IMAGE, mContext.getText(R.string.add)); diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java index 510a60e48d7..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,15 +167,19 @@ 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, R.drawable.ic_volume_remote); + final int requestCode = TextUtils.isEmpty(packageName) ? 0 : packageName.hashCode(); final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, - 0 /* requestCode */, intent, 0 /* flags */); + requestCode, intent, 0 /* flags */); final SliceAction primarySliceAction = SliceAction.createDeeplink( primaryActionIntent, icon, ListBuilder.ICON_IMAGE, mContext.getString(R.string.media_output_label_title, diff --git a/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java b/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java index 3911fb8038e..78dfe51f007 100644 --- a/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java +++ b/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java @@ -55,6 +55,13 @@ public class NrDisabledInDsdsFooterPreferenceController extends BasePreferenceCo super.updateState(preference); if (preference != null) { + // This is necessary to ensure that setting the title to the spannable string returned + // by getFooterText will be accepted. Internally, setTitle does an equality check on + // the spannable string being set to the text already set on the preference. That + // equality check apparently only takes into account the raw text and not and spannables + // that are part of the text. So we clear the title before applying the spannable + // footer to ensure it is accepted. + preference.setTitle(""); preference.setTitle(getFooterText()); } } 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/homepage/contextualcards/ContextualCardManagerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java index 69333d7ad9d..96d0c3be4db 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java @@ -16,6 +16,7 @@ package com.android.settings.homepage.contextualcards; +import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CONTEXTUAL_CARD_COUNT; import static com.android.settings.homepage.contextualcards.ContextualCardManager.KEY_CONTEXTUAL_CARDS; import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH; import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH; @@ -307,7 +308,7 @@ public class ContextualCardManagerTest { } @Test - public void onFinishCardLoading_fastLoad_shouldCallOnContextualCardUpdated() { + public void onFinishCardLoading_fastLoad_shouldUpdateContextualCard() { mManager.mStartTime = System.currentTimeMillis(); final ContextualCardManager manager = spy(mManager); doNothing().when(manager).onContextualCardUpdated(anyMap()); @@ -318,7 +319,7 @@ public class ContextualCardManagerTest { } @Test - public void onFinishCardLoading_slowLoad_shouldSkipOnContextualCardUpdated() { + public void onFinishCardLoading_slowLoadAndNoCard_shouldNotUpdateContextualCard() { mManager.mStartTime = 0; final ContextualCardManager manager = spy(mManager); doNothing().when(manager).onContextualCardUpdated(anyMap()); @@ -328,6 +329,30 @@ public class ContextualCardManagerTest { verify(manager, never()).onContextualCardUpdated(anyMap()); } + @Test + public void onFinishCardLoading_slowLoadAndNotPreAllocateSpace_shouldNotUpdateContextualCard() { + mManager.mStartTime = 0; + Settings.Global.putInt(mContext.getContentResolver(), CONTEXTUAL_CARD_COUNT, 3); + final ContextualCardManager manager = spy(mManager); + doNothing().when(manager).onContextualCardUpdated(anyMap()); + + manager.onFinishCardLoading(Arrays.asList(buildContextualCard(TEST_SLICE_URI))); + + verify(manager, never()).onContextualCardUpdated(anyMap()); + } + + @Test + public void onFinishCardLoading_slowLoadAndPreAllocateSpace_shouldUpdateContextualCard() { + mManager.mStartTime = 0; + Settings.Global.putInt(mContext.getContentResolver(), CONTEXTUAL_CARD_COUNT, 1); + final ContextualCardManager manager = spy(mManager); + doNothing().when(manager).onContextualCardUpdated(anyMap()); + + manager.onFinishCardLoading(Arrays.asList(buildContextualCard(TEST_SLICE_URI))); + + verify(manager).onContextualCardUpdated(anyMap()); + } + @Test public void onFinishCardLoading_newLaunch_twoLoadedCards_shouldShowTwoCards() { mManager.mStartTime = System.currentTimeMillis(); 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 e8245055653..09a40a7ca01 100644 --- a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java @@ -30,6 +30,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 androidx.preference.Preference; @@ -94,6 +95,7 @@ public class RemoteVolumeGroupControllerTest { mContext = RuntimeEnvironment.application; 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());