2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.search;
19 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
20 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
21 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
22 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
23 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
24 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
25 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
26 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
27 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
28 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
29 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
30 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
31 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
32 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
33 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
34 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
35 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
36 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
37 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
38 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
39 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
40 import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
41 import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
42 import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
43 import static android.provider.SearchIndexablesContract.SITE_MAP_COLUMNS;
45 import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP;
47 import android.content.Context;
48 import android.database.Cursor;
49 import android.database.MatrixCursor;
50 import android.provider.SearchIndexableResource;
51 import android.provider.SearchIndexablesContract;
52 import android.provider.SearchIndexablesProvider;
53 import android.text.TextUtils;
54 import android.util.ArraySet;
55 import android.util.Log;
57 import com.android.settings.SettingsActivity;
58 import com.android.settings.overlay.FeatureFactory;
59 import com.android.settingslib.drawer.DashboardCategory;
60 import com.android.settingslib.drawer.Tile;
62 import java.util.ArrayList;
63 import java.util.Collection;
64 import java.util.List;
66 public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
68 public static final boolean DEBUG = false;
71 * Flag for a system property which checks if we should crash if there are issues in the
74 public static final String SYSPROP_CRASH_ON_ERROR =
75 "debug.com.android.settings.search.crash_on_error";
77 private static final String TAG = "SettingsSearchProvider";
79 private static final Collection<String> INVALID_KEYS;
82 INVALID_KEYS = new ArraySet<>();
83 INVALID_KEYS.add(null);
88 public boolean onCreate() {
93 public Cursor queryXmlResources(String[] projection) {
94 MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
95 final List<SearchIndexableResource> resources =
96 getSearchIndexableResourcesFromProvider(getContext());
97 for (SearchIndexableResource val : resources) {
98 Object[] ref = new Object[INDEXABLES_XML_RES_COLUMNS.length];
99 ref[COLUMN_INDEX_XML_RES_RANK] = val.rank;
100 ref[COLUMN_INDEX_XML_RES_RESID] = val.xmlResId;
101 ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = val.className;
102 ref[COLUMN_INDEX_XML_RES_ICON_RESID] = val.iconResId;
103 ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = val.intentAction;
104 ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = val.intentTargetPackage;
105 ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = null; // intent target class
113 public Cursor queryRawData(String[] projection) {
114 MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
115 final List<SearchIndexableRaw> raws = getSearchIndexableRawFromProvider(getContext());
116 for (SearchIndexableRaw val : raws) {
117 Object[] ref = new Object[INDEXABLES_RAW_COLUMNS.length];
118 ref[COLUMN_INDEX_RAW_TITLE] = val.title;
119 ref[COLUMN_INDEX_RAW_SUMMARY_ON] = val.summaryOn;
120 ref[COLUMN_INDEX_RAW_SUMMARY_OFF] = val.summaryOff;
121 ref[COLUMN_INDEX_RAW_ENTRIES] = val.entries;
122 ref[COLUMN_INDEX_RAW_KEYWORDS] = val.keywords;
123 ref[COLUMN_INDEX_RAW_SCREEN_TITLE] = val.screenTitle;
124 ref[COLUMN_INDEX_RAW_CLASS_NAME] = val.className;
125 ref[COLUMN_INDEX_RAW_ICON_RESID] = val.iconResId;
126 ref[COLUMN_INDEX_RAW_INTENT_ACTION] = val.intentAction;
127 ref[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = val.intentTargetPackage;
128 ref[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = val.intentTargetClass;
129 ref[COLUMN_INDEX_RAW_KEY] = val.key;
130 ref[COLUMN_INDEX_RAW_USER_ID] = val.userId;
138 * Gets a combined list non-indexable keys that come from providers inside of settings.
139 * The non-indexable keys are used in Settings search at both index and update time to verify
140 * the validity of results in the database.
143 public Cursor queryNonIndexableKeys(String[] projection) {
144 MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
145 final List<String> nonIndexableKeys = getNonIndexableKeysFromProvider(getContext());
146 for (String nik : nonIndexableKeys) {
147 final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length];
148 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = nik;
156 public Cursor querySiteMapPairs() {
157 final MatrixCursor cursor = new MatrixCursor(SITE_MAP_COLUMNS);
158 final Context context = getContext();
159 // Loop through all IA categories and pages and build additional SiteMapPairs
160 final List<DashboardCategory> categories = FeatureFactory.getFactory(context)
161 .getDashboardFeatureProvider(context).getAllCategories();
162 for (DashboardCategory category : categories) {
163 // Use the category key to look up parent (which page hosts this key)
164 final String parentClass = CATEGORY_KEY_TO_PARENT_MAP.get(category.key);
165 if (parentClass == null) {
168 // Build parent-child class pairs for all children listed under this key.
169 for (Tile tile : category.getTiles()) {
170 String childClass = null;
171 if (tile.getMetaData() != null) {
172 childClass = tile.getMetaData().getString(
173 SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
175 if (childClass == null) {
179 .add(SearchIndexablesContract.SiteMapColumns.PARENT_CLASS, parentClass)
180 .add(SearchIndexablesContract.SiteMapColumns.CHILD_CLASS, childClass);
187 private List<String> getNonIndexableKeysFromProvider(Context context) {
188 final Collection<Class> values = FeatureFactory.getFactory(context)
189 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
190 final List<String> nonIndexableKeys = new ArrayList<>();
192 for (Class<?> clazz : values) {
193 final long startTime = System.currentTimeMillis();
194 Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
197 List<String> providerNonIndexableKeys;
199 providerNonIndexableKeys = provider.getNonIndexableKeys(context);
200 } catch (Exception e) {
201 // Catch a generic crash. In the absence of the catch, the background thread will
202 // silently fail anyway, so we aren't losing information by catching the exception.
203 // We crash when the system property exists so that we can test if crashes need to
205 // The gain is that if there is a crash in a specific controller, we don't lose all
206 // non-indexable keys, but we can still find specific crashes in development.
207 if (System.getProperty(SYSPROP_CRASH_ON_ERROR) != null) {
208 throw new RuntimeException(e);
210 Log.e(TAG, "Error trying to get non-indexable keys from: " + clazz.getName() , e);
214 if (providerNonIndexableKeys == null || providerNonIndexableKeys.isEmpty()) {
216 final long totalTime = System.currentTimeMillis() - startTime;
217 Log.d(TAG, "No indexable, total time " + totalTime);
222 if (providerNonIndexableKeys.removeAll(INVALID_KEYS)) {
223 Log.v(TAG, provider + " tried to add an empty non-indexable key");
227 final long totalTime = System.currentTimeMillis() - startTime;
228 Log.d(TAG, "Non-indexables " + providerNonIndexableKeys.size() + ", total time "
232 nonIndexableKeys.addAll(providerNonIndexableKeys);
235 return nonIndexableKeys;
238 private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {
239 Collection<Class> values = FeatureFactory.getFactory(context)
240 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
241 List<SearchIndexableResource> resourceList = new ArrayList<>();
243 for (Class<?> clazz : values) {
244 Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
247 final List<SearchIndexableResource> resList =
248 provider.getXmlResourcesToIndex(context, true);
250 if (resList == null) {
254 for (SearchIndexableResource item : resList) {
255 item.className = TextUtils.isEmpty(item.className)
260 resourceList.addAll(resList);
266 private List<SearchIndexableRaw> getSearchIndexableRawFromProvider(Context context) {
267 final Collection<Class> values = FeatureFactory.getFactory(context)
268 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
269 final List<SearchIndexableRaw> rawList = new ArrayList<>();
271 for (Class<?> clazz : values) {
272 Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
274 final List<SearchIndexableRaw> providerRaws = provider.getRawDataToIndex(context,
277 if (providerRaws == null) {
281 for (SearchIndexableRaw raw : providerRaws) {
282 // The classname and intent information comes from the PreIndexData
283 // This will be more clear when provider conversion is done at PreIndex time.
284 raw.className = clazz.getName();
287 rawList.addAll(providerRaws);