OSDN Git Service

0a846de317f6bd635e609d198caa4593cd75983a
[android-x86/packages-apps-Settings.git] / src / com / android / settings / search / SettingsSearchIndexablesProvider.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.settings.search;
18
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;
44
45 import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP;
46
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;
56
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;
61
62 import java.util.ArrayList;
63 import java.util.Collection;
64 import java.util.List;
65
66 public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
67
68     public static final boolean DEBUG = false;
69
70     /**
71      * Flag for a system property which checks if we should crash if there are issues in the
72      * indexing pipeline.
73      */
74     public static final String SYSPROP_CRASH_ON_ERROR =
75             "debug.com.android.settings.search.crash_on_error";
76
77     private static final String TAG = "SettingsSearchProvider";
78
79     private static final Collection<String> INVALID_KEYS;
80
81     static {
82         INVALID_KEYS = new ArraySet<>();
83         INVALID_KEYS.add(null);
84         INVALID_KEYS.add("");
85     }
86
87     @Override
88     public boolean onCreate() {
89         return true;
90     }
91
92     @Override
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
106             cursor.addRow(ref);
107         }
108
109         return cursor;
110     }
111
112     @Override
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;
131             cursor.addRow(ref);
132         }
133
134         return cursor;
135     }
136
137     /**
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.
141      */
142     @Override
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;
149             cursor.addRow(ref);
150         }
151
152         return cursor;
153     }
154
155     @Override
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) {
166                 continue;
167             }
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);
174                 }
175                 if (childClass == null) {
176                     continue;
177                 }
178                 cursor.newRow()
179                         .add(SearchIndexablesContract.SiteMapColumns.PARENT_CLASS, parentClass)
180                         .add(SearchIndexablesContract.SiteMapColumns.CHILD_CLASS, childClass);
181             }
182         }
183         // Done.
184         return cursor;
185     }
186
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<>();
191
192         for (Class<?> clazz : values) {
193             final long startTime = System.currentTimeMillis();
194             Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
195                     clazz);
196
197             List<String> providerNonIndexableKeys;
198             try {
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
204                 // be fixed.
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);
209                 }
210                 Log.e(TAG, "Error trying to get non-indexable keys from: " + clazz.getName() , e);
211                 continue;
212             }
213
214             if (providerNonIndexableKeys == null || providerNonIndexableKeys.isEmpty()) {
215                 if (DEBUG) {
216                     final long totalTime = System.currentTimeMillis() - startTime;
217                     Log.d(TAG, "No indexable, total time " + totalTime);
218                 }
219                 continue;
220             }
221
222             if (providerNonIndexableKeys.removeAll(INVALID_KEYS)) {
223                 Log.v(TAG, provider + " tried to add an empty non-indexable key");
224             }
225
226             if (DEBUG) {
227                 final long totalTime = System.currentTimeMillis() - startTime;
228                 Log.d(TAG, "Non-indexables " + providerNonIndexableKeys.size() + ", total time "
229                         + totalTime);
230             }
231
232             nonIndexableKeys.addAll(providerNonIndexableKeys);
233         }
234
235         return nonIndexableKeys;
236     }
237
238     private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {
239         Collection<Class> values = FeatureFactory.getFactory(context)
240                 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
241         List<SearchIndexableResource> resourceList = new ArrayList<>();
242
243         for (Class<?> clazz : values) {
244             Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
245                     clazz);
246
247             final List<SearchIndexableResource> resList =
248                     provider.getXmlResourcesToIndex(context, true);
249
250             if (resList == null) {
251                 continue;
252             }
253
254             for (SearchIndexableResource item : resList) {
255                 item.className = TextUtils.isEmpty(item.className)
256                         ? clazz.getName()
257                         : item.className;
258             }
259
260             resourceList.addAll(resList);
261         }
262
263         return resourceList;
264     }
265
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<>();
270
271         for (Class<?> clazz : values) {
272             Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
273                     clazz);
274             final List<SearchIndexableRaw> providerRaws = provider.getRawDataToIndex(context,
275                     true /* enabled */);
276
277             if (providerRaws == null) {
278                 continue;
279             }
280
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();
285
286             }
287             rawList.addAll(providerRaws);
288         }
289
290         return rawList;
291     }
292 }