diff options
author | Jason Monk <jmonk@google.com> | 2018-04-20 14:08:57 -0400 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2018-04-26 11:16:04 -0400 |
commit | fcd5f0f1aae7a8b3145b9a37e46c7c200408318c (patch) | |
tree | 149600dc36902856c2ec97c9fd12048b69fe11e3 /src/com/android/settings | |
parent | ab3aa6989afa2328c5eede585614895f04939ce5 (diff) | |
download | packages_apps_Settings-fcd5f0f1aae7a8b3145b9a37e46c7c200408318c.tar.gz packages_apps_Settings-fcd5f0f1aae7a8b3145b9a37e46c7c200408318c.tar.bz2 packages_apps_Settings-fcd5f0f1aae7a8b3145b9a37e46c7c200408318c.zip |
Push full slice index to device index
Test: make RunSettingsRoboTests
Bug: 74555610
Change-Id: I3f0aa1218e1d7e736dc918d83e76423fa81ac6ab
Diffstat (limited to 'src/com/android/settings')
8 files changed, 226 insertions, 45 deletions
diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java index 11f1a28322..f14409624d 100644 --- a/src/com/android/settings/AirplaneModeEnabler.java +++ b/src/com/android/settings/AirplaneModeEnabler.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.SystemProperties; import android.os.UserHandle; @@ -51,7 +52,7 @@ public class AirplaneModeEnabler { void onAirplaneModeChanged(boolean isAirplaneModeOn); } - private Handler mHandler = new Handler() { + private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -62,7 +63,8 @@ public class AirplaneModeEnabler { } }; - private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) { + private ContentObserver mAirplaneModeObserver = new ContentObserver( + new Handler(Looper.getMainLooper())) { @Override public void onChange(boolean selfChange) { onAirplaneModeChanged(); diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java index b4851e6b56..7d32ece301 100644 --- a/src/com/android/settings/network/AirplaneModePreferenceController.java +++ b/src/com/android/settings/network/AirplaneModePreferenceController.java @@ -54,6 +54,7 @@ public class AirplaneModePreferenceController extends TogglePreferenceController public AirplaneModePreferenceController(Context context, String key) { super(context, key); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + mAirplaneModeEnabler = new AirplaneModeEnabler(mContext, mMetricsFeatureProvider, this); } public void setFragment(Fragment hostFragment) { @@ -81,7 +82,6 @@ public class AirplaneModePreferenceController extends TogglePreferenceController super.displayPreference(screen); if (isAvailable()) { mAirplaneModePreference = (SwitchPreference) screen.findPreference(getPreferenceKey()); - mAirplaneModeEnabler = new AirplaneModeEnabler(mContext, mMetricsFeatureProvider, this); } } diff --git a/src/com/android/settings/search/DatabaseIndexingManager.java b/src/com/android/settings/search/DatabaseIndexingManager.java index 0c02b6757b..8a18efae54 100644 --- a/src/com/android/settings/search/DatabaseIndexingManager.java +++ b/src/com/android/settings/search/DatabaseIndexingManager.java @@ -59,6 +59,7 @@ import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.Log; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.indexing.IndexData; import com.android.settings.search.indexing.IndexDataConverter; import com.android.settings.search.indexing.PreIndexData; diff --git a/src/com/android/settings/search/DeviceIndexFeatureProvider.java b/src/com/android/settings/search/DeviceIndexFeatureProvider.java index 2273ffd0e1..37978df169 100644 --- a/src/com/android/settings/search/DeviceIndexFeatureProvider.java +++ b/src/com/android/settings/search/DeviceIndexFeatureProvider.java @@ -17,70 +17,57 @@ 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.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Build; import android.provider.Settings; -import android.util.Log; +import com.android.settings.R; import com.android.settings.slices.SettingsSliceProvider; +import java.util.List; +import java.util.Objects; + 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, - Settings.ACTION_BATTERY_SAVER_SETTINGS, - Settings.ACTION_BLUETOOTH_SETTINGS, - "android.intent.action.POWER_USAGE_SUMMARY", - Settings.ACTION_SOUND_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 = 2; + String VERSION = Build.FINGERPRINT; boolean isIndexingEnabled(); - void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri); + void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri, + List<String> keywords); default void updateIndex(Context context, boolean force) { if (!isIndexingEnabled()) return; - if (!force && Settings.Secure.getInt(context.getContentResolver(), INDEX_VERSION, -1) - == VERSION) { + if (!force && Objects.equals( + Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), 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); + ComponentName jobComponent = new ComponentName(context.getPackageName(), + DeviceIndexUpdateJobService.class.getName()); + int jobId = context.getResources().getInteger(R.integer.device_index_update); + // Schedule a job so that we know it'll be able to complete, but try to run as + // soon as possible. + context.getSystemService(JobScheduler.class).schedule( + new JobInfo.Builder(jobId, jobComponent) + .setPersisted(true) + .setMinimumLatency(1) + .setOverrideDeadline(1) + .build()); + + Settings.Secure.putString(context.getContentResolver(), INDEX_VERSION, VERSION); } static String createDeepLink(String s) { diff --git a/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java b/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java index 4564fe67d0..7a11bd4678 100644 --- a/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java +++ b/src/com/android/settings/search/DeviceIndexFeatureProviderImpl.java @@ -17,6 +17,8 @@ package com.android.settings.search; import android.content.Context; import android.net.Uri; +import java.util.List; + public class DeviceIndexFeatureProviderImpl implements DeviceIndexFeatureProvider { @Override @@ -25,7 +27,8 @@ public class DeviceIndexFeatureProviderImpl implements DeviceIndexFeatureProvide } @Override - public void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri) { + public void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri, + List<String> keywords) { // Not enabled by default. } } diff --git a/src/com/android/settings/search/DeviceIndexUpdateJobService.java b/src/com/android/settings/search/DeviceIndexUpdateJobService.java new file mode 100644 index 0000000000..573dcdf5c8 --- /dev/null +++ b/src/com/android/settings/search/DeviceIndexUpdateJobService.java @@ -0,0 +1,172 @@ +/* + * 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 android.app.slice.Slice.HINT_LARGE; +import static android.app.slice.Slice.HINT_TITLE; +import static android.app.slice.SliceItem.FORMAT_TEXT; + +import static com.android.settings.search.DeviceIndexFeatureProvider.createDeepLink; + +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.net.Uri.Builder; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.slices.SettingsSliceProvider; +import com.android.settings.slices.SliceDeepLinkSpringBoard; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.SliceManager; +import androidx.slice.SliceManager.SliceCallback; +import androidx.slice.SliceMetadata; +import androidx.slice.core.SliceQuery; +import androidx.slice.widget.ListContent; + +public class DeviceIndexUpdateJobService extends JobService { + + private static final String TAG = "DeviceIndexUpdate"; + private static final boolean DEBUG = false; + @VisibleForTesting + protected boolean mRunningJob; + + @Override + public boolean onStartJob(JobParameters params) { + if (DEBUG) Log.d(TAG, "onStartJob"); + mRunningJob = true; + Thread thread = new Thread(() -> updateIndex(params)); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + if (DEBUG) Log.d(TAG, "onStopJob " + mRunningJob); + if (mRunningJob) { + mRunningJob = false; + return true; + } + return false; + } + + @VisibleForTesting + protected void updateIndex(JobParameters params) { + if (DEBUG) Log.d(TAG, "Starting index"); + DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory( + this).getDeviceIndexFeatureProvider(); + SliceManager manager = getSliceManager(); + Uri baseUri = new Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .build(); + Collection<Uri> slices = manager.getSliceDescendants(baseUri); + if (DEBUG) Log.d(TAG, "Indexing " + slices.size() + " slices"); + + for (Uri slice : slices) { + if (!mRunningJob) { + return; + } + Slice loadedSlice = bindSliceSynchronous(manager, slice); + // TODO: Get Title APIs on SliceMetadata and use that. + SliceMetadata metaData = getMetadata(loadedSlice); + CharSequence title = findTitle(loadedSlice, metaData); + if (title != null) { + if (DEBUG) Log.d(TAG, "Indexing: " + slice + " " + title + " " + loadedSlice); + indexProvider.index(this, title, slice, Uri.parse(createDeepLink( + new Intent(SliceDeepLinkSpringBoard.ACTION_VIEW_SLICE) + .setPackage(getPackageName()) + .putExtra(SliceDeepLinkSpringBoard.EXTRA_SLICE, slice.toString()) + .toUri(Intent.URI_ANDROID_APP_SCHEME))), + metaData.getSliceKeywords()); + } + } + if (DEBUG) Log.d(TAG, "Done indexing"); + jobFinished(params, false); + } + + protected SliceManager getSliceManager() { + return SliceManager.getInstance(this); + } + + protected SliceMetadata getMetadata(Slice loadedSlice) { + return SliceMetadata.from(this, loadedSlice); + } + + protected CharSequence findTitle(Slice loadedSlice, SliceMetadata metaData) { + ListContent content = new ListContent(this, loadedSlice); + SliceItem headerItem = content.getHeaderItem(); + if (headerItem == null) { + if (content.getRowItems().size() != 0) { + headerItem = content.getRowItems().get(0); + } else { + return null; + } + } + // Look for a title, then large text, then any text at all. + SliceItem title = SliceQuery.find(headerItem, FORMAT_TEXT, HINT_TITLE, null); + if (title != null) { + return title.getText(); + } + title = SliceQuery.find(headerItem, FORMAT_TEXT, HINT_LARGE, null); + if (title != null) { + return title.getText(); + } + title = SliceQuery.find(headerItem, FORMAT_TEXT); + if (title != null) { + return title.getText(); + } + return null; + } + + protected Slice bindSliceSynchronous(SliceManager manager, Uri slice) { + final Slice[] returnSlice = new Slice[1]; + CountDownLatch latch = new CountDownLatch(1); + SliceCallback callback = new SliceCallback() { + @Override + public void onSliceUpdated(Slice s) { + try { + SliceMetadata m = SliceMetadata.from(DeviceIndexUpdateJobService.this, s); + if (m.getLoadingState() == SliceMetadata.LOADED_ALL) { + returnSlice[0] = s; + latch.countDown(); + manager.unregisterSliceCallback(slice, this); + } + } catch (Exception e) { + Log.w(TAG, slice + " cannot be indexed", e); + returnSlice[0] = s; + } + } + }; + // Register a callback until we get a loaded slice. + manager.registerSliceCallback(slice, callback); + // Trigger the first bind in case no loading is needed. + callback.onSliceUpdated(manager.bindSlice(slice)); + try { + latch.await(); + } catch (InterruptedException e) { + } + return returnSlice[0]; + } +} diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index f4496057e0..b33308c06e 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; import android.net.wifi.WifiManager; +import android.os.StrictMode; import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.support.annotation.VisibleForTesting; @@ -148,6 +149,11 @@ public class SettingsSliceProvider extends SliceProvider { @Override public Slice onBindSlice(Uri sliceUri) { + // TODO: Remove this when all slices are not breaking strict mode + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .permitAll() + .build()); + String path = sliceUri.getPath(); // If adding a new Slice, do not directly match Slice URIs. // Use {@link SlicesDatabaseAccessor}. @@ -277,7 +283,8 @@ public class SettingsSliceProvider extends SliceProvider { * {@link SliceData} is loaded from {@link SlicesDatabaseHelper.Tables#TABLE_SLICES_INDEX}. */ private Slice getSliceStub(Uri uri) { - return new ListBuilder(getContext(), uri).build(); + // TODO: Switch back to ListBuilder when slice loading states are fixed. + return new Slice.Builder(uri).build(); } // TODO (b/70622039) remove this when the proper wifi slice is enabled. diff --git a/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java index fcb452516b..d02431c8f7 100644 --- a/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java +++ b/src/com/android/settings/slices/SliceDeepLinkSpringBoard.java @@ -27,6 +27,8 @@ public class SliceDeepLinkSpringBoard extends Activity { private static final String TAG = "DeeplinkSpringboard"; public static final String INTENT = "intent"; public static final String SETTINGS = "settings"; + public static final String ACTION_VIEW_SLICE = "com.android.settings.action.VIEW_SLICE"; + public static final String EXTRA_SLICE = "slice"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -39,7 +41,14 @@ public class SliceDeepLinkSpringBoard extends Activity { } try { Intent intent = parse(uri, getPackageName()); - startActivity(intent); + if (ACTION_VIEW_SLICE.equals(intent.getAction())) { + // This shouldn't matter since the slice is shown instead of the device + // index caring about the launch uri. + Uri slice = Uri.parse(intent.getStringExtra(EXTRA_SLICE)); + Log.e(TAG, "Slice intent launched: " + slice); + } else { + startActivity(intent); + } finish(); } catch (URISyntaxException e) { Log.e(TAG, "Error decoding uri", e); |