diff --git a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java index e6452def3d7..da759a33e5e 100644 --- a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java +++ b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java @@ -17,6 +17,8 @@ package com.android.settings.bluetooth; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static androidx.slice.builders.ListBuilder.ICON_IMAGE; + import android.annotation.ColorInt; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; diff --git a/src/com/android/settings/location/LocationSliceBuilder.java b/src/com/android/settings/location/LocationSliceBuilder.java new file mode 100644 index 00000000000..e69ccfba54c --- /dev/null +++ b/src/com/android/settings/location/LocationSliceBuilder.java @@ -0,0 +1,94 @@ +/* + * 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.location; + +import static android.provider.SettingsSlicesContract.KEY_LOCATION; + +import static androidx.slice.builders.ListBuilder.ICON_IMAGE; + +import android.annotation.ColorInt; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.provider.SettingsSlicesContract; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; +import com.android.settings.SubSettings; +import com.android.settings.Utils; +import com.android.settings.search.DatabaseIndexingUtils; + +import androidx.slice.Slice; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + +import android.support.v4.graphics.drawable.IconCompat; + +/** + * Utility class to build an intent-based Location Slice. + */ +public class LocationSliceBuilder { + + /** + * Backing Uri for the Location Slice. + */ + public static final Uri LOCATION_URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSlicesContract.AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(KEY_LOCATION) + .build(); + + private LocationSliceBuilder() { + } + + /** + * Return a Location Slice bound to {@link #LOCATION_URI}. + */ + public static Slice getSlice(Context context) { + final IconCompat icon = IconCompat.createWithResource(context, + R.drawable.ic_signal_location); + final String title = context.getString(R.string.location_settings_title); + @ColorInt final int color = Utils.getColorAccent(context); + final PendingIntent primaryAction = getPrimaryAction(context); + final SliceAction primarySliceAction = new SliceAction(primaryAction, icon, title); + + return new ListBuilder(context, LOCATION_URI, ListBuilder.INFINITY) + .setAccentColor(color) + .addRow(b -> b + .setTitle(title) + .setTitleItem(icon, ICON_IMAGE) + .setPrimaryAction(primarySliceAction)) + .build(); + } + + private static PendingIntent getPrimaryAction(Context context) { + final String screenTitle = context.getText(R.string.location_settings_title).toString(); + final Uri contentUri = new Uri.Builder().appendPath(KEY_LOCATION).build(); + final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context, + LocationSettings.class.getName(), KEY_LOCATION, screenTitle, + MetricsEvent.LOCATION) + .setClassName(context.getPackageName(), SubSettings.class.getName()) + .setData(contentUri); + + return PendingIntent.getActivity(context, 0 /* requestCode */, + intent, 0 /* flags */); + } +} diff --git a/src/com/android/settings/notification/ZenModeSliceBuilder.java b/src/com/android/settings/notification/ZenModeSliceBuilder.java index 0edf214179c..aedd0b3e4c3 100644 --- a/src/com/android/settings/notification/ZenModeSliceBuilder.java +++ b/src/com/android/settings/notification/ZenModeSliceBuilder.java @@ -18,6 +18,8 @@ package com.android.settings.notification; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static androidx.slice.builders.ListBuilder.ICON_IMAGE; + import android.annotation.ColorInt; import android.app.NotificationManager; import android.app.PendingIntent; diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 3ed6185563c..557ecad5428 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -33,6 +33,7 @@ import android.util.KeyValueListParser; import android.util.Log; import android.util.Pair; +import com.android.settings.location.LocationSliceBuilder; import com.android.settings.overlay.FeatureFactory; import com.android.settings.core.BasePreferenceController; import com.android.settings.wifi.WifiSliceBuilder; @@ -188,6 +189,8 @@ public class SettingsSliceProvider extends SliceProvider { return ZenModeSliceBuilder.getSlice(getContext()); } else if (BluetoothSliceBuilder.BLUETOOTH_URI.equals(sliceUri)) { return BluetoothSliceBuilder.getSlice(getContext()); + } else if (LocationSliceBuilder.LOCATION_URI.equals(sliceUri)) { + return LocationSliceBuilder.getSlice(getContext()); } SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); @@ -289,10 +292,17 @@ public class SettingsSliceProvider extends SliceProvider { void loadSlice(Uri uri) { long startBuildTime = System.currentTimeMillis(); - final SliceData sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri); + final SliceData sliceData; + try { + sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri); + } catch (IllegalStateException e) { + Log.e(TAG, "Could not get slice data for uri: " + uri, e); + return; + } final BasePreferenceController controller = SliceBuilderUtils.getPreferenceController( getContext(), sliceData); + final IntentFilter filter = controller.getIntentFilter(); if (filter != null) { registerIntentToUri(filter, uri); @@ -336,7 +346,8 @@ public class SettingsSliceProvider extends SliceProvider { private List getSpecialCasePlatformUris() { return Arrays.asList( WifiSliceBuilder.WIFI_URI, - BluetoothSliceBuilder.BLUETOOTH_URI + BluetoothSliceBuilder.BLUETOOTH_URI, + LocationSliceBuilder.LOCATION_URI ); } diff --git a/src/com/android/settings/wifi/WifiSliceBuilder.java b/src/com/android/settings/wifi/WifiSliceBuilder.java index 96d1b82b86a..7df7f19ad78 100644 --- a/src/com/android/settings/wifi/WifiSliceBuilder.java +++ b/src/com/android/settings/wifi/WifiSliceBuilder.java @@ -19,6 +19,8 @@ package com.android.settings.wifi; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import static android.provider.SettingsSlicesContract.KEY_WIFI; +import static androidx.slice.builders.ListBuilder.ICON_IMAGE; + import android.annotation.ColorInt; import android.app.PendingIntent; import android.content.ContentResolver; diff --git a/tests/robotests/src/com/android/settings/location/LocationSliceBuilderTest.java b/tests/robotests/src/com/android/settings/location/LocationSliceBuilderTest.java new file mode 100644 index 00000000000..22928bfee95 --- /dev/null +++ b/tests/robotests/src/com/android/settings/location/LocationSliceBuilderTest.java @@ -0,0 +1,65 @@ +package com.android.settings.location; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.content.res.Resources; +import android.support.v4.graphics.drawable.IconCompat; + +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.SliceTester; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +import java.util.List; + +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.SliceMetadata; +import androidx.slice.SliceProvider; +import androidx.slice.core.SliceAction; +import androidx.slice.widget.SliceLiveData; + +@RunWith(SettingsRobolectricTestRunner.class) +public class LocationSliceBuilderTest { + + private Context mContext; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + + // Prevent crash in SliceMetadata. + Resources resources = spy(mContext.getResources()); + doReturn(60).when(resources).getDimensionPixelSize(anyInt()); + doReturn(resources).when(mContext).getResources(); + + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + } + + @Test + public void getLocationSlice_correctSliceContent() { + final Slice LocationSlice = LocationSliceBuilder.getSlice(mContext); + final SliceMetadata metadata = SliceMetadata.from(mContext, LocationSlice); + + final List toggles = metadata.getToggles(); + assertThat(toggles).isEmpty(); + + final SliceAction primaryAction = metadata.getPrimaryAction(); + final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext, + R.drawable.ic_signal_location); + assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); + + final List sliceItems = LocationSlice.getItems(); + SliceTester.assertTitle(sliceItems, mContext.getString(R.string.location_settings_title)); + } +} diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index 722f481d10f..bb064d1f915 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.slice.SliceManager; +import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; @@ -35,6 +36,7 @@ import android.net.Uri; import android.os.StrictMode; import android.provider.SettingsSlicesContract; +import com.android.settings.location.LocationSliceBuilder; import com.android.settings.wifi.WifiSliceBuilder; import com.android.settings.bluetooth.BluetoothSliceBuilder; import com.android.settings.notification.ZenModeSliceBuilder; @@ -81,7 +83,8 @@ public class SettingsSliceProviderTest { private static final List SPECIAL_CASE_PLATFORM_URIS = Arrays.asList( WifiSliceBuilder.WIFI_URI, - BluetoothSliceBuilder.BLUETOOTH_URI + BluetoothSliceBuilder.BLUETOOTH_URI, + LocationSliceBuilder.LOCATION_URI ); private static final List SPECIAL_CASE_OEM_URIS = Arrays.asList( @@ -401,6 +404,18 @@ public class SettingsSliceProviderTest { assertThat(wifiSlice.getUri()).isEqualTo(WifiSliceBuilder.WIFI_URI); } + @Test + public void onSlicePinned_noIntentRegistered_specialCaseUri_doesNotCrash() { + final Uri uri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSlicesContract.AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath(SettingsSlicesContract.KEY_LOCATION) + .build(); + + mProvider.onSlicePinned(uri); + } + private void insertSpecialCase(String key) { insertSpecialCase(key, true); } diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSliceBuilderTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSliceBuilderTest.java index 865785fc745..605a661a307 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiSliceBuilderTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiSliceBuilderTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.spy; import android.content.Context; import com.android.settings.R; -import com.android.settings.wifi.WifiSliceBuilder; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SliceTester;