diff --git a/res/values/strings.xml b/res/values/strings.xml index 182602691d5..b60bca7e1e0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12094,4 +12094,9 @@ Suspend execution for cached apps + + + Allow screen overlays on Settings + + Allow apps that can display over other apps to overlay Settings screens diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index cce0a37a506..e8605894f6d 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -251,6 +251,11 @@ android:title="@string/show_refresh_rate" android:summary="@string/show_refresh_rate_summary" /> + + subInfoList = + subscriptionManager.getAvailableSubscriptionInfoList(); + if (subInfoList == null) { + Log.i(TAG, "Subscription is not inited: " + subId); + return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId); + } + + for (SubscriptionInfo subInfo : subInfoList) { + if ((subInfo != null) && (subInfo.getSubscriptionId() == subId)) { + return getNormalizedMobileTemplate(telephonyManager, subId); + } + } + Log.i(TAG, "Subscription is not active: " + subId); + return getMobileTemplateForSubId(telephonyManager, mobileDefaultSubId); + } + + private static NetworkTemplate getNormalizedMobileTemplate( + TelephonyManager telephonyManager, int subId) { + final NetworkTemplate mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId); + final String[] mergedSubscriberIds = telephonyManager + .createForSubscriptionId(subId).getMergedImsisFromGroup(); + if (ArrayUtils.isEmpty(mergedSubscriberIds)) { + Log.i(TAG, "mergedSubscriberIds is null."); + return mobileTemplate; + } + + return NetworkTemplate.normalize(mobileTemplate, mergedSubscriberIds); + } + + private static NetworkTemplate getMobileTemplateForSubId( + TelephonyManager telephonyManager, int subId) { + return NetworkTemplate.buildTemplateMobileAll(telephonyManager.getSubscriberId(subId)); + } +} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index eaccebf3ac1..9406c895393 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -539,6 +539,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new BluetoothHDAudioPreferenceController(context, lifecycle, bluetoothA2dpConfigStore, fragment)); controllers.add(new SharedDataPreferenceController(context)); + controllers.add(new OverlaySettingsPreferenceController(context)); return controllers; } diff --git a/src/com/android/settings/development/OverlaySettingsPreferenceController.java b/src/com/android/settings/development/OverlaySettingsPreferenceController.java new file mode 100644 index 00000000000..50f98672976 --- /dev/null +++ b/src/com/android/settings/development/OverlaySettingsPreferenceController.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +/** + * A controller helps enable or disable a developer setting which allows non system overlays on + * Settings app. + */ +public class OverlaySettingsPreferenceController extends DeveloperOptionsPreferenceController + implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { + + public static final String SHARE_PERFS = "overlay_settings"; + private static final String KEY_OVERLAY_SETTINGS = "overlay_settings"; + + public OverlaySettingsPreferenceController(Context context) { + super(context); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_OVERLAY_SETTINGS; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + setOverlaySettingsEnabled(mContext, (Boolean) newValue); + return true; + } + + @Override + public void updateState(Preference preference) { + ((SwitchPreference) preference).setChecked(isOverlaySettingsEnabled(mContext)); + } + + /** + * Check if this setting is enabled or not. + */ + public static boolean isOverlaySettingsEnabled(Context context) { + final SharedPreferences editor = context.getSharedPreferences(SHARE_PERFS, + Context.MODE_PRIVATE); + return editor.getBoolean(SHARE_PERFS, false /* defValue */); + } + + /** + * Enable this setting. + */ + @VisibleForTesting + static void setOverlaySettingsEnabled(Context context, boolean enabled) { + final SharedPreferences editor = context.getSharedPreferences(SHARE_PERFS, + Context.MODE_PRIVATE); + editor.edit().putBoolean(SHARE_PERFS, enabled).apply(); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + setOverlaySettingsEnabled(mContext, false); + } +} diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java index 2cf2b6df7e6..737fb0f8d2f 100644 --- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java +++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsController.java @@ -22,6 +22,7 @@ import android.os.PowerManager; import android.provider.Settings; import android.provider.Settings.Global; import android.text.TextUtils; + import com.android.settingslib.fuelgauge.BatterySaverUtils; /** @@ -72,7 +73,7 @@ public class BatterySaverScheduleRadioButtonsController { if (key == null) { return false; } - + final ContentResolver resolver = mContext.getContentResolver(); int mode = PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE; int triggerLevel = 0; @@ -112,6 +113,10 @@ public class BatterySaverScheduleRadioButtonsController { if (mode != PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC) { Settings.Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, triggerLevel); } + // Suppress battery saver suggestion notification if enabling scheduling battery saver. + if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC || triggerLevel != 0) { + BatterySaverUtils.suppressAutoBatterySaver(mContext); + } mSeekBarController.updateSeekBar(); return true; } diff --git a/src/com/android/settings/network/telephony/DataUsagePreferenceController.java b/src/com/android/settings/network/telephony/DataUsagePreferenceController.java index 08524d67f8c..40eb549308c 100644 --- a/src/com/android/settings/network/telephony/DataUsagePreferenceController.java +++ b/src/com/android/settings/network/telephony/DataUsagePreferenceController.java @@ -29,6 +29,7 @@ import androidx.preference.Preference; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; +import com.android.settings.datausage.lib.DataUsageLib; import com.android.settingslib.net.DataUsageController; import com.android.settingslib.utils.ThreadUtils; @@ -99,7 +100,7 @@ public class DataUsagePreferenceController extends TelephonyBasePreferenceContro if (!SubscriptionManager.isValidSubscriptionId(subId)) { return null; } - return DataUsageUtils.getMobileTemplate(context, subId); + return DataUsageLib.getMobileTemplate(context, subId); } private NetworkTemplate getNetworkTemplate() { diff --git a/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java b/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java index d4f43e2f5fa..cf0f1380cfb 100644 --- a/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java +++ b/tests/robotests/src/com/android/settings/core/HideNonSystemOverlayMixinTest.java @@ -20,7 +20,8 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static com.google.common.truth.Truth.assertThat; -import android.os.Build; +import android.content.Context; +import android.content.SharedPreferences; import android.os.Bundle; import android.view.WindowManager; @@ -28,6 +29,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.android.settings.R; +import com.android.settings.development.OverlaySettingsPreferenceController; import org.junit.Before; import org.junit.Test; @@ -35,7 +37,6 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.android.controller.ActivityController; -import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class HideNonSystemOverlayMixinTest { @@ -69,17 +70,27 @@ public class HideNonSystemOverlayMixinTest { } @Test - public void isEnabled_debug_false() { - ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true); + public void isEnabled_isAllowedOverlaySettings_returnFalse() { + mActivityController.setup(); + final TestActivity activity = mActivityController.get(); + final SharedPreferences editor = activity.getSharedPreferences( + OverlaySettingsPreferenceController.SHARE_PERFS, + Context.MODE_PRIVATE); + editor.edit().putBoolean(OverlaySettingsPreferenceController.SHARE_PERFS, true).apply(); - assertThat(new HideNonSystemOverlayMixin(null).isEnabled()).isFalse(); + assertThat(new HideNonSystemOverlayMixin(activity).isEnabled()).isFalse(); } @Test - public void isEnabled_user_true() { - ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", false); + public void isEnabled_isNotAllowedOverlaySettings_returnTrue() { + mActivityController.setup(); + TestActivity activity = mActivityController.get(); + final SharedPreferences editor = activity.getSharedPreferences( + OverlaySettingsPreferenceController.SHARE_PERFS, + Context.MODE_PRIVATE); + editor.edit().putBoolean(OverlaySettingsPreferenceController.SHARE_PERFS, false).apply(); - assertThat(new HideNonSystemOverlayMixin(null).isEnabled()).isTrue(); + assertThat(new HideNonSystemOverlayMixin(activity).isEnabled()).isTrue(); } public static class TestActivity extends AppCompatActivity { diff --git a/tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java b/tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java new file mode 100644 index 00000000000..8d994d0550e --- /dev/null +++ b/tests/robotests/src/com/android/settings/datausage/lib/DataUsageLibTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.datausage.lib; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.NetworkTemplate; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class DataUsageLibTest { + + private static final int SUB_ID = 1; + private static final int SUB_ID_2 = 2; + private static final String SUBSCRIBER_ID = "Test Subscriber"; + private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2"; + + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private SubscriptionManager mSubscriptionManager; + @Mock + private SubscriptionInfo mInfo1; + @Mock + private SubscriptionInfo mInfo2; + @Mock + private ParcelUuid mParcelUuid; + private Context mContext; + private List mInfos; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); + when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID); + when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2); + when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); + when(mSubscriptionManager.isActiveSubscriptionId(anyInt())).thenReturn(true); + } + + @Test + @Ignore + public void getMobileTemplate_infoNull_returnMobileAll() { + when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false); + + final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); + } + + @Test + @Ignore + public void getMobileTemplate_groupUuidNull_returnMobileAll() { + when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1); + when(mInfo1.getGroupUuid()).thenReturn(null); + when(mTelephonyManager.getMergedImsisFromGroup()) + .thenReturn(new String[] {SUBSCRIBER_ID}); + + final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); + } + + @Test + @Ignore + public void getMobileTemplate_groupUuidExist_returnMobileMerged() { + when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1); + when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid); + when(mTelephonyManager.getMergedImsisFromGroup()) + .thenReturn(new String[] {SUBSCRIBER_ID, SUBSCRIBER_ID_2}); + + final NetworkTemplate networkTemplate = DataUsageLib.getMobileTemplate(mContext, SUB_ID); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/development/OverlaySettingsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OverlaySettingsPreferenceControllerTest.java new file mode 100644 index 00000000000..4f04c384723 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/OverlaySettingsPreferenceControllerTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.preference.SwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class OverlaySettingsPreferenceControllerTest { + + private Context mContext; + private SwitchPreference mPreference; + private OverlaySettingsPreferenceController mController; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mController = new OverlaySettingsPreferenceController(mContext); + mPreference = new SwitchPreference(mContext); + } + + @Test + public void isAvailable_shouldReturnTrue() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void updateState_isOverlaySettingsEnabled_shouldCheckPreference() { + OverlaySettingsPreferenceController.setOverlaySettingsEnabled(mContext, true); + + mController.updateState(mPreference); + + assertThat(mPreference.isChecked()).isTrue(); + } + + @Test + public void updateState_isOverlaySettingsDisabled_shouldUncheckPreference() { + OverlaySettingsPreferenceController.setOverlaySettingsEnabled(mContext, false); + + mController.updateState(mPreference); + + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void onPreferenceChange_preferenceChecked_shouldEnableSettings() { + mController.onPreferenceChange(mPreference, true); + + assertThat(OverlaySettingsPreferenceController.isOverlaySettingsEnabled(mContext)).isTrue(); + } + + @Test + public void onPreferenceChange_preferenceUnchecked_shouldDisableSettings() { + mController.onPreferenceChange(mPreference, false); + + assertThat( + OverlaySettingsPreferenceController.isOverlaySettingsEnabled(mContext)).isFalse(); + } + + @Test + public void isOverlaySettingsEnabled_sharePreferenceSetTrue_shouldReturnTrue() { + final SharedPreferences editor = mContext.getSharedPreferences( + OverlaySettingsPreferenceController.SHARE_PERFS, + Context.MODE_PRIVATE); + editor.edit().putBoolean(OverlaySettingsPreferenceController.SHARE_PERFS, true).apply(); + + assertThat(OverlaySettingsPreferenceController.isOverlaySettingsEnabled(mContext)).isTrue(); + } + + @Test + public void isOverlaySettingsEnabled_sharePreferenceSetFalse_shouldReturnFalse() { + final SharedPreferences editor = mContext.getSharedPreferences( + OverlaySettingsPreferenceController.SHARE_PERFS, + Context.MODE_PRIVATE); + editor.edit().putBoolean(OverlaySettingsPreferenceController.SHARE_PERFS, false).apply(); + + assertThat( + OverlaySettingsPreferenceController.isOverlaySettingsEnabled(mContext)).isFalse(); + } + + @Test + public void setOverlaySettingsEnabled_setTrue_shouldStoreTrue() { + OverlaySettingsPreferenceController.setOverlaySettingsEnabled(mContext, true); + + assertThat( + OverlaySettingsPreferenceController.isOverlaySettingsEnabled(mContext)).isTrue(); + } + + @Test + public void setOverlaySettingsEnabled_setFalse_shouldStoreTrue() { + OverlaySettingsPreferenceController.setOverlaySettingsEnabled(mContext, false); + + assertThat( + OverlaySettingsPreferenceController.isOverlaySettingsEnabled(mContext)).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java index 8654a4e5a7b..2919edc406c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleRadioButtonsControllerTest.java @@ -8,6 +8,7 @@ import android.os.PowerManager; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,9 +60,36 @@ public class BatterySaverScheduleRadioButtonsControllerTest { @Test public void setDefaultKey_any_defaultsToNoScheduleIfWarningNotSeen() { Secure.putString( - mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); + mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null"); mController.setDefaultKey(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE); assertThat(mController.getDefaultKey()) .isEqualTo(BatterySaverScheduleRadioButtonsController.KEY_NO_SCHEDULE); } + + @Test + public void setDefaultKey_percentage_shouldSuppressNotification() { + Secure.putInt( + mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); + Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, + PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); + Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5); + mController.setDefaultKey(BatterySaverScheduleRadioButtonsController.KEY_PERCENTAGE); + + final int result = Settings.Secure.getInt(mResolver, + Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0); + assertThat(result).isEqualTo(1); + } + + @Test + public void setDefaultKey_routine_shouldSuppressNotification() { + Secure.putInt( + mContext.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); + Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, + PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC); + mController.setDefaultKey(BatterySaverScheduleRadioButtonsController.KEY_ROUTINE); + + final int result = Settings.Secure.getInt(mResolver, + Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0); + assertThat(result).isEqualTo(1); + } } diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java index 154d106c12d..722833c8b04 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java @@ -36,6 +36,8 @@ import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.slice.Slice; +import androidx.slice.SliceProvider; +import androidx.slice.widget.SliceLiveData; import com.android.settings.R; import com.android.settings.homepage.contextualcards.ContextualCard; @@ -79,6 +81,7 @@ public class SliceContextualCardRendererTest { mLifecycleOwner = new ContextualCardsFragment(); mRenderer = new SliceContextualCardRenderer(mActivity, mLifecycleOwner, mControllerRendererPool); + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); } @Test