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