diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 27788dcbaa1..c21eaba3377 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -255,6 +255,8 @@
android:value="com.android.settings.wifi.WifiSettings" />
+
+
+
+
+
+
+
+
+
+
+ android:exported="true"
+ android:grantUriPermissions="true">
+
+
+
+
+
+
+
indexProvider.updateIndex(SettingsActivity.this, false /* force */));
+ }
+
private void doUpdateTilesList() {
PackageManager pm = getPackageManager();
final UserManager um = UserManager.get(this);
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 7cf437f3439..110d204ed93 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -30,6 +30,7 @@ import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
+import com.android.settings.search.DeviceIndexFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.security.SecurityFeatureProvider;
import com.android.settings.slices.SlicesFeatureProvider;
@@ -106,6 +107,8 @@ public abstract class FeatureFactory {
public abstract AccountFeatureProvider getAccountFeatureProvider();
+ public abstract DeviceIndexFeatureProvider getDeviceIndexFeatureProvider();
+
public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 5fc8627dded..c521eb80d53 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -41,6 +41,8 @@ import com.android.settings.gestures.AssistGestureFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProviderImpl;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProviderImpl;
+import com.android.settings.search.DeviceIndexFeatureProvider;
+import com.android.settings.search.DeviceIndexFeatureProviderImpl;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.search.SearchFeatureProviderImpl;
import com.android.settings.security.SecurityFeatureProvider;
@@ -75,6 +77,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
private BluetoothFeatureProvider mBluetoothFeatureProvider;
private SlicesFeatureProvider mSlicesFeatureProvider;
private AccountFeatureProvider mAccountFeatureProvider;
+ private DeviceIndexFeatureProviderImpl mDeviceIndexFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -208,4 +211,12 @@ public class FeatureFactoryImpl extends FeatureFactory {
}
return mAccountFeatureProvider;
}
+
+ @Override
+ public DeviceIndexFeatureProvider getDeviceIndexFeatureProvider() {
+ if (mDeviceIndexFeatureProvider == null) {
+ mDeviceIndexFeatureProvider = new DeviceIndexFeatureProviderImpl();
+ }
+ return mDeviceIndexFeatureProvider;
+ }
}
diff --git a/src/com/android/settings/search/DeviceIndexFeatureProvider.java b/src/com/android/settings/search/DeviceIndexFeatureProvider.java
new file mode 100644
index 00000000000..690943ee7a4
--- /dev/null
+++ b/src/com/android/settings/search/DeviceIndexFeatureProvider.java
@@ -0,0 +1,89 @@
+/*
+ * 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.search;
+
+import static com.android.settings.slices.SliceDeepLinkSpringBoard.INTENT;
+import static com.android.settings.slices.SliceDeepLinkSpringBoard.SETTINGS;
+
+import android.app.slice.SliceManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.settings.slices.SettingsSliceProvider;
+
+public interface DeviceIndexFeatureProvider {
+
+ // TODO: Remove this and index all action and intent slices through search index.
+ String[] ACTIONS_TO_INDEX = new String[]{
+ Settings.ACTION_WIFI_SETTINGS,
+ };
+
+ String TAG = "DeviceIndex";
+
+ String INDEX_VERSION = "settings:index_version";
+
+ // Increment when new items are added to ensure they get pushed to the device index.
+ int VERSION = 1;
+
+ boolean isIndexingEnabled();
+
+ void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri);
+
+ default void updateIndex(Context context, boolean force) {
+ if (!isIndexingEnabled()) return;
+
+ if (!force && Settings.Secure.getInt(context.getContentResolver(), INDEX_VERSION, -1)
+ == VERSION) {
+ // No need to update.
+ return;
+ }
+
+ PackageManager pm = context.getPackageManager();
+ for (String action : ACTIONS_TO_INDEX) {
+ Intent intent = new Intent(action);
+ intent.setPackage(context.getPackageName());
+ ResolveInfo activity = pm.resolveActivity(intent, PackageManager.GET_META_DATA);
+ if (activity == null) {
+ Log.e(TAG, "Unable to resolve " + action);
+ continue;
+ }
+ String sliceUri = activity.activityInfo.metaData
+ .getString(SliceManager.SLICE_METADATA_KEY);
+ if (sliceUri != null) {
+ Log.d(TAG, "Intent: " + createDeepLink(intent.toUri(Intent.URI_ANDROID_APP_SCHEME)));
+ index(context, activity.activityInfo.loadLabel(pm),
+ Uri.parse(sliceUri),
+ Uri.parse(createDeepLink(intent.toUri(Intent.URI_ANDROID_APP_SCHEME))));
+ } else {
+ Log.e(TAG, "No slice uri found for " + activity.activityInfo.name);
+ }
+ }
+
+ Settings.Secure.putInt(context.getContentResolver(), INDEX_VERSION, VERSION);
+ }
+
+ static String createDeepLink(String s) {
+ return new Uri.Builder().scheme(SETTINGS)
+ .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+ .appendQueryParameter(INTENT, s)
+ .build()
+ .toString();
+ }
+}
diff --git a/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java b/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java
new file mode 100644
index 00000000000..4564fe67d04
--- /dev/null
+++ b/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.search;
+
+import android.content.Context;
+import android.net.Uri;
+
+public class DeviceIndexFeatureProviderImpl implements DeviceIndexFeatureProvider {
+
+ @Override
+ public boolean isIndexingEnabled() {
+ return false;
+ }
+
+ @Override
+ public void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri) {
+ // Not enabled by default.
+ }
+}
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 802f1e4a86d..8b3bdbd2392 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -17,27 +17,26 @@
package com.android.settings.slices;
import android.app.PendingIntent;
-
-import android.content.ContentResolver;
+import android.app.slice.SliceManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.wifi.WifiManager;
-import android.provider.SettingsSlicesContract;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.android.settings.R;
import com.android.settingslib.utils.ThreadUtils;
+import java.net.URISyntaxException;
import java.util.Map;
import java.util.WeakHashMap;
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
-import androidx.slice.builders.SliceAction;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
/**
* A {@link SliceProvider} for Settings to enabled inline results in system apps.
@@ -106,6 +105,17 @@ public class SettingsSliceProvider extends SliceProvider {
return true;
}
+ @Override
+ public Uri onMapIntentToUri(Intent intent) {
+ try {
+ return getContext().getSystemService(SliceManager.class).mapIntentToUri(
+ SliceDeepLinkSpringBoard.parse(
+ intent.getData(), getContext().getPackageName()));
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
@Override
public Slice onBindSlice(Uri sliceUri) {
String path = sliceUri.getPath();
diff --git a/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java
new file mode 100644
index 00000000000..fcb452516bc
--- /dev/null
+++ b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java
@@ -0,0 +1,64 @@
+/*
+ * 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.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.net.URISyntaxException;
+
+public class SliceDeepLinkSpringBoard extends Activity {
+
+ private static final String TAG = "DeeplinkSpringboard";
+ public static final String INTENT = "intent";
+ public static final String SETTINGS = "settings";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Uri uri = getIntent().getData();
+ if (uri == null) {
+ Log.e(TAG, "No data found");
+ finish();
+ return;
+ }
+ try {
+ Intent intent = parse(uri, getPackageName());
+ startActivity(intent);
+ finish();
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Error decoding uri", e);
+ finish();
+ }
+ }
+
+ public static Intent parse(Uri uri, String pkg) throws URISyntaxException {
+ Intent intent = Intent.parseUri(uri.getQueryParameter(INTENT),
+ Intent.URI_ANDROID_APP_SCHEME);
+ // Start with some really strict constraints and loosen them if we need to.
+ // Don't allow components.
+ intent.setComponent(null);
+ // Clear out the extras.
+ if (intent.getExtras() != null) {
+ intent.getExtras().clear();
+ }
+ // Make sure this points at Settings.
+ intent.setPackage(pkg);
+ return intent;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search/DeviceIndexFeatureProviderTest.java b/tests/robotests/src/com/android/settings/search/DeviceIndexFeatureProviderTest.java
new file mode 100644
index 00000000000..25acc63cf02
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/DeviceIndexFeatureProviderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.search;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+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.Activity;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class DeviceIndexFeatureProviderTest {
+
+ private DeviceIndexFeatureProvider mProvider;
+ private Activity mActivity;
+
+ @Before
+ public void setUp() {
+ FakeFeatureFactory.setupForTest();
+ mActivity = Robolectric.buildActivity(Activity.class).create().visible().get();
+ mProvider = spy(new DeviceIndexFeatureProviderImpl());
+ }
+
+ @Test
+ public void verifyDisabled() {
+ when(mProvider.isIndexingEnabled()).thenReturn(false);
+
+ mProvider.updateIndex(mActivity, false);
+ verify(mProvider, never()).index(any(), any(), any(), any());
+ }
+
+ @Test
+ public void verifyIndexing() {
+ when(mProvider.isIndexingEnabled()).thenReturn(true);
+
+ mProvider.updateIndex(mActivity, false);
+ verify(mProvider, atLeastOnce()).index(any(), any(), any(), any());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 601164032d5..8945af924e7 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -33,6 +33,7 @@ import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.overlay.SupportFeatureProvider;
import com.android.settings.overlay.SurveyFeatureProvider;
+import com.android.settings.search.DeviceIndexFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.security.SecurityFeatureProvider;
import com.android.settings.slices.SlicesFeatureProvider;
@@ -63,6 +64,7 @@ public class FakeFeatureFactory extends FeatureFactory {
public final SlicesFeatureProvider slicesFeatureProvider;
public SearchFeatureProvider searchFeatureProvider;
public final AccountFeatureProvider mAccountFeatureProvider;
+ public final DeviceIndexFeatureProvider deviceIndexFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -101,6 +103,7 @@ public class FakeFeatureFactory extends FeatureFactory {
bluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
slicesFeatureProvider = mock(SlicesFeatureProvider.class);
mAccountFeatureProvider = mock(AccountFeatureProvider.class);
+ deviceIndexFeatureProvider = mock(DeviceIndexFeatureProvider.class);
}
@Override
@@ -182,4 +185,9 @@ public class FakeFeatureFactory extends FeatureFactory {
public AccountFeatureProvider getAccountFeatureProvider() {
return mAccountFeatureProvider;
}
+
+ @Override
+ public DeviceIndexFeatureProvider getDeviceIndexFeatureProvider() {
+ return deviceIndexFeatureProvider;
+ }
}