From 56b2bad05e3739cc5b8cef5865ed91176592eb92 Mon Sep 17 00:00:00 2001 From: Matthew Fritze Date: Mon, 11 Jun 2018 17:20:53 -0700 Subject: [PATCH] Handle special case slices Create a handler for any slice that doesn't include anything from a PreferenceController. Test: robotests Change-Id: If23947152d61877537d0cac6240e96b9ab977bce Bug: 80263568 --- .../settings/slices/CustomSliceManager.java | 88 +++++++++++ .../settings/slices/CustomSliceable.java | 102 +++++++++++++ .../slices/SettingsSliceProvider.java | 33 ++++- .../slices/SliceBroadcastReceiver.java | 9 ++ .../slices/SlicesFeatureProvider.java | 2 + .../slices/SlicesFeatureProviderImpl.java | 13 +- .../Enhanced4gLteSliceHelperTest.java | 14 +- .../slices/SettingsSliceProviderTest.java | 1 + .../slices/SliceBroadcastReceiverTest.java | 5 + .../slices/SpecialCaseSliceManagerTest.java | 137 ++++++++++++++++++ .../calling/WifiCallingSliceHelperTest.java | 6 + 11 files changed, 398 insertions(+), 12 deletions(-) create mode 100644 src/com/android/settings/slices/CustomSliceManager.java create mode 100644 src/com/android/settings/slices/CustomSliceable.java create mode 100644 tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java new file mode 100644 index 00000000000..f3df3c19d55 --- /dev/null +++ b/src/com/android/settings/slices/CustomSliceManager.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 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.slices; + +import android.content.Context; +import android.net.Uri; +import android.util.ArrayMap; + +import java.util.Map; + +/** + * Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by + * preferences. + *

+ * By default, all Slices in Settings should be built by a + *

+ */ +public class CustomSliceManager { + + protected final Map> mUriMap; + + private final Context mContext; + + public CustomSliceManager(Context context) { + mContext = context; + mUriMap = new ArrayMap<>(); + addSlices(); + } + + /** + * Return a {@link CustomSliceable} associated to the Uri. + *

+ * Do not change this method signature to accommodate for a special-case slicable - a context is + * the only thing that should be needed to create the object. + */ + public CustomSliceable getSliceableFromUri(Uri uri) { + final Class clazz = mUriMap.get(uri); + + if (clazz == null) { + throw new IllegalArgumentException("No Slice found for uri: " + uri); + } + + return CustomSliceable.createInstance(mContext, clazz); + } + + /** + * Return a {@link CustomSliceable} associated to the Action. + *

+ * Do not change this method signature to accommodate for a special-case sliceable - a context + * is the only thing that should be needed to create the object. + */ + public CustomSliceable getSliceableFromIntentAction(String action) { + return getSliceableFromUri(Uri.parse(action)); + } + + /** + * Returns {@code true} if {@param uri} is a valid Slice Uri handled by + * {@link CustomSliceManager}. + */ + public boolean isValidUri(Uri uri) { + return mUriMap.containsKey(uri); + } + + /** + * Returns {@code true} if {@param action} is a valid intent action handled by + * {@link CustomSliceManager}. + */ + public boolean isValidAction(String action) { + return isValidUri(Uri.parse(action)); + } + + private void addSlices() { + } +} \ No newline at end of file diff --git a/src/com/android/settings/slices/CustomSliceable.java b/src/com/android/settings/slices/CustomSliceable.java new file mode 100644 index 00000000000..afe7170c508 --- /dev/null +++ b/src/com/android/settings/slices/CustomSliceable.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 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.slices; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import androidx.slice.Slice; + + +/** + * Common functions for custom Slices. + *

+ * A template for all Settings slices which are not represented by a Preference. By + * standardizing the methods used by the Slice helpers, we can use generically take actions + * rather than maintaining a list of all of the custom slices every time we reference Slices in + * Settings. + *

+ * By default, all Slices in Settings should be built through Preference Controllers extending + * {@link com.android.settings.core.BasePreferenceController}, which are automatically piped + * into Settings-Slices infrastructure. Cases where you should implement this interface are: + *

+ *

+ * Note that if your UI is supported because the Preference is not backed by a + * {@link com.android.settings.dashboard.DashboardFragment}, then you should first convert the + * existing fragment into a dashboard fragment, and then extend + * {@link com.android.settings.core.BasePreferenceController}. + *

+ * If you implement this interface, you should add your Slice to {@link CustomSliceManager}. + */ +public interface CustomSliceable { + + /** + * @return an complete instance of the {@link Slice}. + */ + Slice getSlice(Context context); + + /** + * @return a {@link android.content.ContentResolver#SCHEME_CONTENT content} {@link Uri} which + * backs the {@link Slice} returned by {@link #getSlice(Context)}. + */ + Uri getUri(); + + /** + * Handles the actions sent by the {@link Intent intents} bound to the {@link Slice} returned by + * {@link #getSlice(Context)}. + * + * @param intent which has the action taken on a {@link Slice}. + */ + void onNotifyChange(Intent intent); + + /** + * Settings Slices which can represent components that are updatable by the framework should + * listen to changes matched to the {@link IntentFilter} returned here. + * + * @return an {@link IntentFilter} for updates related to the {@link Slice} returned by + * {@link #getSlice(Context)}. + */ + default IntentFilter getIntentFilter() { + return null; + } + + /** + * Build an instance of a {@link CustomSliceable} which has a {@link Context}-only constructor. + */ + static CustomSliceable createInstance(Context context, Class sliceableClass) { + try { + //final Class clazz = Class.forName(sliceableClassName); + final Constructor sliceable = + sliceableClass.getConstructor(Context.class); + final Object[] params = new Object[]{context}; + return sliceable.newInstance(params); + } catch (NoSuchMethodException | InstantiationException | + IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { + throw new IllegalStateException( + "Invalid sliceable class: " + sliceableClass, e); + } + } +} \ No newline at end of file diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 9cde91a8558..da705c94dd6 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -32,15 +32,15 @@ import android.util.KeyValueListParser; import android.util.Log; import android.util.Pair; -import com.android.settings.bluetooth.BluetoothSliceBuilder; -import com.android.settings.core.BasePreferenceController; import com.android.settings.flashlight.FlashlightSliceBuilder; import com.android.settings.location.LocationSliceBuilder; import com.android.settings.mobilenetwork.Enhanced4gLteSliceHelper; import com.android.settings.notification.ZenModeSliceBuilder; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.core.BasePreferenceController; import com.android.settings.wifi.WifiSliceBuilder; import com.android.settings.wifi.calling.WifiCallingSliceHelper; +import com.android.settings.bluetooth.BluetoothSliceBuilder; import com.android.settingslib.SliceBroadcastRelay; import com.android.settingslib.utils.ThreadUtils; @@ -113,11 +113,15 @@ public class SettingsSliceProvider extends SliceProvider { public static final String EXTRA_SLICE_PLATFORM_DEFINED = "com.android.settings.slice.extra.platform"; + @VisibleForTesting + CustomSliceManager mCustomSliceManager; + @VisibleForTesting SlicesDatabaseAccessor mSlicesDatabaseAccessor; @VisibleForTesting Map mSliceWeakDataCache; + @VisibleForTesting Map mSliceDataCache; @@ -135,6 +139,8 @@ public class SettingsSliceProvider extends SliceProvider { mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); mSliceDataCache = new ConcurrentHashMap<>(); mSliceWeakDataCache = new WeakHashMap<>(); + mCustomSliceManager = FeatureFactory.getFactory( + getContext()).getSlicesFeatureProvider().getCustomSliceManager(getContext()); return true; } @@ -151,6 +157,15 @@ public class SettingsSliceProvider extends SliceProvider { @Override public void onSlicePinned(Uri sliceUri) { + if (mCustomSliceManager.isValidUri(sliceUri)) { + final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(sliceUri); + final IntentFilter filter = sliceable.getIntentFilter(); + if (filter != null) { + registerIntentToUri(filter, sliceUri); + } + return; + } + if (WifiSliceBuilder.WIFI_URI.equals(sliceUri)) { registerIntentToUri(WifiSliceBuilder.INTENT_FILTER, sliceUri); mRegisteredUris.add(sliceUri); @@ -162,7 +177,7 @@ public class SettingsSliceProvider extends SliceProvider { registerIntentToUri(BluetoothSliceBuilder.INTENT_FILTER, sliceUri); return; } else if (FlashlightSliceBuilder.FLASHLIGHT_URI.equals(sliceUri)) { - registerIntentToUri(FlashlightSliceBuilder.INTENT_FILTER , sliceUri); + registerIntentToUri(FlashlightSliceBuilder.INTENT_FILTER, sliceUri); mRegisteredUris.add(sliceUri); return; } @@ -197,8 +212,14 @@ public class SettingsSliceProvider extends SliceProvider { return null; } - // If adding a new Slice, do not directly match Slice URIs. - // Use {@link SlicesDatabaseAccessor}. + // Before adding a slice to {@link CustomSliceManager}, please get approval + // from the Settings team. + if (mCustomSliceManager.isValidUri(sliceUri)) { + final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri( + sliceUri); + return sliceable.getSlice(getContext()); + } + if (WifiCallingSliceHelper.WIFI_CALLING_URI.equals(sliceUri)) { return FeatureFactory.getFactory(getContext()) .getSlicesFeatureProvider() @@ -433,4 +454,4 @@ public class SettingsSliceProvider extends SliceProvider { } return new String[0]; } -} +} \ No newline at end of file diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java index 7d50410731f..a44a2cd6e0e 100644 --- a/src/com/android/settings/slices/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -71,6 +71,15 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED, false /* default */); + final CustomSliceManager mCustomSliceManager = FeatureFactory.getFactory( + context).getSlicesFeatureProvider().getCustomSliceManager(context); + if (mCustomSliceManager.isValidAction(action)) { + final CustomSliceable sliceable = + mCustomSliceManager.getSliceableFromIntentAction(action); + sliceable.onNotifyChange(intent); + return; + } + switch (action) { case ACTION_TOGGLE_CHANGED: final boolean isChecked = intent.getBooleanExtra(Slice.EXTRA_TOGGLE_STATE, false); diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java index 8395a58dc16..5940aa452fb 100644 --- a/src/com/android/settings/slices/SlicesFeatureProvider.java +++ b/src/com/android/settings/slices/SlicesFeatureProvider.java @@ -28,6 +28,8 @@ public interface SlicesFeatureProvider { */ void indexSliceData(Context context); + CustomSliceManager getCustomSliceManager(Context context); + /** * Gets new WifiCallingSliceHelper object */ diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java index 988bcfe2273..fc2298cea69 100644 --- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java +++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java @@ -13,23 +13,32 @@ public class SlicesFeatureProviderImpl implements SlicesFeatureProvider { private SlicesIndexer mSlicesIndexer; private SliceDataConverter mSliceDataConverter; + private CustomSliceManager mCustomSliceManager; @Override public SlicesIndexer getSliceIndexer(Context context) { if (mSlicesIndexer == null) { - mSlicesIndexer = new SlicesIndexer(context); + mSlicesIndexer = new SlicesIndexer(context.getApplicationContext()); } return mSlicesIndexer; } @Override public SliceDataConverter getSliceDataConverter(Context context) { - if(mSliceDataConverter == null) { + if (mSliceDataConverter == null) { mSliceDataConverter = new SliceDataConverter(context.getApplicationContext()); } return mSliceDataConverter; } + @Override + public CustomSliceManager getCustomSliceManager(Context context) { + if (mCustomSliceManager == null) { + mCustomSliceManager = new CustomSliceManager(context.getApplicationContext()); + } + return mCustomSliceManager; + } + @Override public void indexSliceDataAsync(Context context) { SlicesIndexer indexer = getSliceIndexer(context); diff --git a/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java b/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java index 9c719cd8153..434a89ddf90 100644 --- a/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java +++ b/tests/robotests/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelperTest.java @@ -20,6 +20,7 @@ import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.app.slice.Slice.HINT_TITLE; import static android.app.slice.SliceItem.FORMAT_TEXT; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -37,6 +38,7 @@ import com.android.settings.R; import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBroadcastReceiver; import com.android.settings.slices.SlicesFeatureProvider; +import com.android.settings.slices.CustomSliceManager; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -61,13 +63,13 @@ import androidx.slice.widget.SliceLiveData; @RunWith(SettingsRobolectricTestRunner.class) public class Enhanced4gLteSliceHelperTest { - private Context mContext; @Mock private CarrierConfigManager mMockCarrierConfigManager; @Mock private ImsManager mMockImsManager; + private Context mContext; private FakeEnhanced4gLteSliceHelper mEnhanced4gLteSliceHelper; private SettingsSliceProvider mProvider; private SliceBroadcastReceiver mReceiver; @@ -79,16 +81,20 @@ public class Enhanced4gLteSliceHelperTest { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); + + when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class))) + .thenReturn(new CustomSliceManager(mContext)); + //setup for SettingsSliceProvider tests mProvider = spy(new SettingsSliceProvider()); doReturn(mContext).when(mProvider).getContext(); + mProvider.onCreateSliceProvider(); //setup for SliceBroadcastReceiver test mReceiver = spy(new SliceBroadcastReceiver()); - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); - // Prevent crash in SliceMetadata. Resources resources = spy(mContext.getResources()); doReturn(60).when(resources).getDimensionPixelSize(anyInt()); diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index c72e9f6c542..ea2f2cac6e7 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -109,6 +109,7 @@ public class SettingsSliceProviderTest { mProvider.mSliceWeakDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); + mProvider.mCustomSliceManager = new CustomSliceManager(mContext); when(mProvider.getContext()).thenReturn(mContext); mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase(); diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java index d57834851f9..955cdf107c6 100644 --- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java @@ -18,12 +18,15 @@ package com.android.settings.slices; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.slice.Slice; import android.content.ContentResolver; @@ -82,6 +85,8 @@ public class SliceBroadcastReceiverTest { mSearchFeatureProvider = new SearchFeatureProviderImpl(); mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mFakeFeatureFactory.searchFeatureProvider = mSearchFeatureProvider; + when(mFakeFeatureFactory.slicesFeatureProvider.getCustomSliceManager(any())) + .thenReturn(new CustomSliceManager(mContext)); mLoggingNameArgumentCatpor = ArgumentCaptor.forClass(Pair.class); mLoggingValueArgumentCatpor = ArgumentCaptor.forClass(Pair.class); } diff --git a/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java b/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java new file mode 100644 index 00000000000..ab226f9db54 --- /dev/null +++ b/tests/robotests/src/com/android/settings/slices/SpecialCaseSliceManagerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2018 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.slices; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.provider.SettingsSlicesContract; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +import androidx.slice.Slice; + +@RunWith(SettingsRobolectricTestRunner.class) +public class SpecialCaseSliceManagerTest { + + private Context mContext; + + private CustomSliceManager mCustomSliceManager; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mCustomSliceManager = spy(new CustomSliceManager(mContext)); + mCustomSliceManager.mUriMap.clear(); + mCustomSliceManager.mUriMap.put(FakeSliceable.URI, FakeSliceable.class); + } + + @Test + public void getSliceableFromUri_returnsCorrectObject() { + final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri( + FakeSliceable.URI); + + assertThat(sliceable).isInstanceOf(FakeSliceable.class); + } + + @Test + public void getSliceableFromIntentAction_returnsCorrectObject() { + final CustomSliceable sliceable = + mCustomSliceManager.getSliceableFromIntentAction(FakeSliceable.URI.toString()); + + assertThat(sliceable).isInstanceOf(FakeSliceable.class); + } + + @Test + public void isValidUri_validUri_returnsTrue() { + final boolean isValidUri = mCustomSliceManager.isValidUri(FakeSliceable.URI); + + assertThat(isValidUri).isTrue(); + } + + @Test + public void isValidUri_invalidUri_returnsFalse() { + final boolean isValidUri = mCustomSliceManager.isValidUri(null); + + assertThat(isValidUri).isFalse(); + } + + @Test + public void isValidAction_validActions_returnsTrue() { + final boolean isValidAction = + mCustomSliceManager.isValidAction(FakeSliceable.URI.toString()); + + assertThat(isValidAction).isTrue(); + } + + @Test + public void isValidAction_invalidAction_returnsFalse() { + final boolean isValidAction = mCustomSliceManager.isValidAction("action"); + + assertThat(isValidAction).isFalse(); + } + + static class FakeSliceable implements CustomSliceable { + + static final String KEY = "magic key of khazad dum"; + + static final Uri URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(KEY) + .build(); + + static final Slice SLICE = new Slice.Builder(URI).build(); + + static boolean backingData = false; + + public FakeSliceable(Context context) {} + + @Override + public Slice getSlice(Context context) { + return SLICE; + } + + @Override + public Uri getUri() { + return URI; + } + + @Override + public void onNotifyChange(Intent intent) { + backingData = !backingData; + } + + @Override + public IntentFilter getIntentFilter() { + return new IntentFilter(); + } + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java index cdc8ecbfcf9..c9c9abb7383 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java @@ -20,6 +20,8 @@ import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.app.slice.Slice.HINT_TITLE; import static android.app.slice.SliceItem.FORMAT_TEXT; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -40,6 +42,7 @@ import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBroadcastReceiver; import com.android.settings.slices.SliceData; import com.android.settings.slices.SlicesFeatureProvider; +import com.android.settings.slices.CustomSliceManager; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -88,12 +91,15 @@ public class WifiCallingSliceHelperTest { //setup for SettingsSliceProvider tests mProvider = spy(new SettingsSliceProvider()); doReturn(mContext).when(mProvider).getContext(); + mProvider.onCreateSliceProvider(); //setup for SliceBroadcastReceiver test mReceiver = spy(new SliceBroadcastReceiver()); mFeatureFactory = FakeFeatureFactory.setupForTest(); mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); + when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class))) + .thenReturn(new CustomSliceManager(mContext)); // Prevent crash in SliceMetadata. Resources resources = spy(mContext.getResources());