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 android.content.Context;
20 import android.database.Cursor;
21 import android.database.MatrixCursor;
22 import android.provider.SearchIndexableResource;
23 import android.provider.SearchIndexablesProvider;
24 import android.util.Log;
25 
26 import com.android.settings.search2.DatabaseIndexingUtils;
27 
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.List;
32 
33 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
34 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
35 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
36 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
37 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
38 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
39 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
40 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
41 import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
42 import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
43 import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
44 
45 public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
46     private static final String TAG = "SettingsSearchProvider";
47 
48     @Override
onCreate()49     public boolean onCreate() {
50         return true;
51     }
52 
53     @Override
queryXmlResources(String[] projection)54     public Cursor queryXmlResources(String[] projection) {
55         MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
56         Collection<SearchIndexableResource> values = SearchIndexableResources.values();
57         for (SearchIndexableResource val : values) {
58             Object[] ref = new Object[INDEXABLES_XML_RES_COLUMNS.length];
59             ref[COLUMN_INDEX_XML_RES_RANK] = val.rank;
60             ref[COLUMN_INDEX_XML_RES_RESID] = val.xmlResId;
61             ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = val.className;
62             ref[COLUMN_INDEX_XML_RES_ICON_RESID] = val.iconResId;
63             ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = null; // intent action
64             ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = null; // intent target package
65             ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = null; // intent target class
66             cursor.addRow(ref);
67         }
68         return cursor;
69     }
70 
71     @Override
queryRawData(String[] projection)72     public Cursor queryRawData(String[] projection) {
73         MatrixCursor result = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
74         return result;
75     }
76 
77     /**
78      * Gets a combined list non-indexable keys that come from providers inside of settings.
79      * The non-indexable keys are used in Settings search at both index and update time to verify
80      * the validity of results in the database.
81      */
82     @Override
queryNonIndexableKeys(String[] projection)83     public Cursor queryNonIndexableKeys(String[] projection) {
84         MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
85         final Collection<String> values = new HashSet<>();
86         final Context context = getContext();
87 
88         for (SearchIndexableResource sir : SearchIndexableResources.values()) {
89             final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(sir.className);
90             if (clazz == null) {
91                 Log.d(TAG, "SearchIndexableResource '" + sir.className +
92                         "' should implement the " + Indexable.class.getName() + " interface!");
93                 continue;
94             }
95 
96             final Indexable.SearchIndexProvider provider =
97                     DatabaseIndexingUtils.getSearchIndexProvider(clazz);
98 
99             if (provider == null) {
100                 Log.d(TAG, "Unable to get SearchIndexableProvider from " +
101                         Indexable.class.getName());
102                 continue;
103             }
104 
105             List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(context);
106 
107             if (providerNonIndexableKeys == null || providerNonIndexableKeys.isEmpty()) {
108                 continue;
109             }
110 
111             if (providerNonIndexableKeys.removeAll(Collections.singleton(null))
112                     || providerNonIndexableKeys.removeAll(Collections.singleton(""))) {
113                 Log.v(TAG, clazz.getName() + " tried to add an empty non-indexable key");
114             }
115 
116             values.addAll(providerNonIndexableKeys);
117         }
118 
119         for (String nik : values) {
120 
121             final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length];
122             ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = nik;
123             cursor.addRow(ref);
124         }
125         return cursor;
126     }
127 }
128