Add option for settings to push to a device index
The index implementation is optional and left up to the OEM.
Test: Open settings, see content in index
Test: robo tests
Bug: 68378569
Bug: 76102600
Change-Id: Idb8bb1e0cabbbe92e7a852e2eadbdcd8c2ab7d56
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 83bb082..16e1a7b 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -65,6 +65,7 @@
import com.android.settings.dashboard.DashboardSummary;
import com.android.settings.development.DevelopmentSettingsDashboardFragment;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.search.DeviceIndexFeatureProvider;
import com.android.settings.wfd.WifiDisplaySettings;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.core.instrumentation.Instrumentable;
@@ -72,6 +73,7 @@
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.SettingsDrawerActivity;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
@@ -489,6 +491,7 @@
registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
updateTilesList();
+ updateDeviceIndex();
}
@Override
@@ -609,6 +612,14 @@
});
}
+ private void updateDeviceIndex() {
+ DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(
+ this).getDeviceIndexFeatureProvider();
+
+ ThreadUtils.postOnBackgroundThread(
+ () -> 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 7cf437f..110d204 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.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 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 5fc8627..c521eb8 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.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 @@
private BluetoothFeatureProvider mBluetoothFeatureProvider;
private SlicesFeatureProvider mSlicesFeatureProvider;
private AccountFeatureProvider mAccountFeatureProvider;
+ private DeviceIndexFeatureProviderImpl mDeviceIndexFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -208,4 +211,12 @@
}
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 0000000..690943e
--- /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 0000000..4564fe6
--- /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 802f1e4..8b3bdbd 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.
@@ -107,6 +106,17 @@
}
@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();
// If adding a new Slice, do not directly match Slice URIs.
diff --git a/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java
new file mode 100644
index 0000000..fcb4525
--- /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;
+ }
+}