16f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio/*
26f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * Copyright (C) 2014 The Android Open Source Project
36f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio *
46f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License");
56f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * you may not use this file except in compliance with the License.
66f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * You may obtain a copy of the License at
76f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio *
86f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio *      http://www.apache.org/licenses/LICENSE-2.0
96f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio *
106f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * Unless required by applicable law or agreed to in writing, software
116f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS,
126f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * See the License for the specific language governing permissions and
146f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio * limitations under the License.
156f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio */
166f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
17fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Megliopackage com.android.settings.search;
186f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
19fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.content.ContentResolver;
206f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.content.ContentValues;
216f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.content.Context;
22fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.content.Intent;
23b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglioimport android.content.pm.ApplicationInfo;
24b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglioimport android.content.pm.PackageInfo;
25fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.content.pm.PackageManager;
26fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.content.pm.ResolveInfo;
276f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.content.res.TypedArray;
286f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.content.res.XmlResourceParser;
296f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.database.Cursor;
306f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.database.DatabaseUtils;
31b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglioimport android.database.MergeCursor;
326f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.database.sqlite.SQLiteDatabase;
33dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglioimport android.database.sqlite.SQLiteException;
34ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monkimport android.database.sqlite.SQLiteFullException;
35fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.net.Uri;
366f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.os.AsyncTask;
37fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.provider.SearchIndexableData;
38fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.provider.SearchIndexableResource;
39fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglioimport android.provider.SearchIndexablesContract;
406f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.text.TextUtils;
416f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.util.AttributeSet;
426f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.util.Log;
436f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.util.TypedValue;
446f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport android.util.Xml;
4539b467482d1bf256a111c757e9b7621c6f523271Jason Monk
466f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport com.android.settings.R;
4739b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport com.android.settings.search.IndexDatabaseHelper.IndexColumns;
4839b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport com.android.settings.search.IndexDatabaseHelper.Tables;
4939b467482d1bf256a111c757e9b7621c6f523271Jason Monk
506f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport org.xmlpull.v1.XmlPullParser;
516f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport org.xmlpull.v1.XmlPullParserException;
526f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
536f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport java.io.IOException;
54b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglioimport java.lang.reflect.Field;
55363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglioimport java.text.Normalizer;
566f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport java.util.ArrayList;
5745f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglioimport java.util.Collections;
58d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglioimport java.util.Date;
59df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglioimport java.util.HashMap;
606f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport java.util.List;
616f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport java.util.Locale;
62df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglioimport java.util.Map;
636f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport java.util.concurrent.ExecutionException;
646f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglioimport java.util.concurrent.atomic.AtomicBoolean;
65363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglioimport java.util.regex.Pattern;
666f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
67df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
68b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
6939b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
70b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
71b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
72b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
7339b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
74b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
7539b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
7639b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK;
7739b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
7839b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
7939b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
8039b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
81957ccf30b9c969a6362fccb293d2c2df8bd1ad32Fabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
82b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
83b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
84b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
85b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglioimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
8639b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
8739b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
8839b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
896f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
906f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Megliopublic class Index {
916f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
92b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio    private static final String LOG_TAG = "Index";
936f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
946f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    // Those indices should match the indices of SELECT_COLUMNS !
957d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_RANK = 0;
966f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    public static final int COLUMN_INDEX_TITLE = 1;
979593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    public static final int COLUMN_INDEX_SUMMARY_ON = 2;
989593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
99a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio    public static final int COLUMN_INDEX_ENTRIES = 4;
1007d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_KEYWORDS = 5;
1017d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_CLASS_NAME = 6;
1027d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_SCREEN_TITLE = 7;
1037d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_ICON = 8;
1047d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_INTENT_ACTION = 9;
1057d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 10;
1067d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 11;
1077d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_ENABLED = 12;
1087d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio    public static final int COLUMN_INDEX_KEY = 13;
109957ccf30b9c969a6362fccb293d2c2df8bd1ad32Fabrice Di Meglio    public static final int COLUMN_INDEX_USER_ID = 14;
1106f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
1118f5c65fd010d249770fda81c1129c1d7555a3299Fabrice Di Meglio    public static final String ENTRIES_SEPARATOR = "|";
1128f5c65fd010d249770fda81c1129c1d7555a3299Fabrice Di Meglio
1136f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values
1146f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    private static final String[] SELECT_COLUMNS = new String[] {
1159593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_RANK,               // 0
1169593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_TITLE,              // 1
1179593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_SUMMARY_ON,         // 2
1189593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_SUMMARY_OFF,        // 3
119a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio            IndexColumns.DATA_ENTRIES,            // 4
1207d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.DATA_KEYWORDS,           // 5
1217d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.CLASS_NAME,              // 6
1227d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.SCREEN_TITLE,            // 7
1237d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.ICON,                    // 8
1247d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.INTENT_ACTION,           // 9
1257d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.INTENT_TARGET_PACKAGE,   // 10
1267d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.INTENT_TARGET_CLASS,     // 11
1277d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.ENABLED,                 // 12
1287d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            IndexColumns.DATA_KEY_REF             // 13
1296f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    };
1306f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
131b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio    private static final String[] MATCH_COLUMNS_PRIMARY = {
1320d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio            IndexColumns.DATA_TITLE,
133e6b336d1a05752b546319a33a5f5b5c65bccf4e9Fabrice Di Meglio            IndexColumns.DATA_TITLE_NORMALIZED,
134e6b336d1a05752b546319a33a5f5b5c65bccf4e9Fabrice Di Meglio            IndexColumns.DATA_KEYWORDS
135b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio    };
136b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio
137b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio    private static final String[] MATCH_COLUMNS_SECONDARY = {
1389593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_SUMMARY_ON,
1399593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_SUMMARY_ON_NORMALIZED,
1409593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_SUMMARY_OFF,
1419593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
142e6b336d1a05752b546319a33a5f5b5c65bccf4e9Fabrice Di Meglio            IndexColumns.DATA_ENTRIES
1430d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio    };
1440d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio
145d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    // Max number of saved search queries (who will be used for proposing suggestions)
146d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private static long MAX_SAVED_SEARCH_QUERY = 64;
147d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    // Max number of proposed suggestions
148d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private static final int MAX_PROPOSED_SUGGESTIONS = 5;
149d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
150dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio    private static final String BASE_AUTHORITY = "com.android.settings";
151dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio
1526f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    private static final String EMPTY = "";
1536f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    private static final String NON_BREAKING_HYPHEN = "\u2011";
15435c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette    private static final String LIST_DELIMITERS = "[,]\\s*";
1556f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    private static final String HYPHEN = "-";
15635c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette    private static final String SPACE = " ";
1576f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
1589593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
1599593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            "SEARCH_INDEX_DATA_PROVIDER";
1609593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
1619593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    private static final String NODE_NAME_PREFERENCE_SCREEN = "PreferenceScreen";
1629593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
163a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio    private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
1649593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
16545f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio    private static final List<String> EMPTY_LIST = Collections.<String>emptyList();
16645f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
1676f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    private static Index sInstance;
168363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio
169363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio    private static final Pattern REMOVE_DIACRITICALS_PATTERN
170363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio            = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
171363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio
17251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    /**
17351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio     * A private class to describe the update data for the Index database
17451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio     */
1759301441e2af4f9ee1eca4236e6e04b4666c0d986Fabrice Di Meglio    private static class UpdateData {
17651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        public List<SearchIndexableData> dataToUpdate;
177724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio        public List<SearchIndexableData> dataToDelete;
178df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        public Map<String, List<String>> nonIndexableKeys;
179df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
1805d219a2cfa320501a96968d8827fa9c195b04451Justin Klaassen        public boolean forceUpdate;
1815d219a2cfa320501a96968d8827fa9c195b04451Justin Klaassen        public boolean fullIndex;
1826f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
18351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        public UpdateData() {
18451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            dataToUpdate = new ArrayList<SearchIndexableData>();
185724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio            dataToDelete = new ArrayList<SearchIndexableData>();
186df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            nonIndexableKeys = new HashMap<String, List<String>>();
18751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
18851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
18972e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        public UpdateData(UpdateData other) {
19072e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            dataToUpdate = new ArrayList<SearchIndexableData>(other.dataToUpdate);
19172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            dataToDelete = new ArrayList<SearchIndexableData>(other.dataToDelete);
19272e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            nonIndexableKeys = new HashMap<String, List<String>>(other.nonIndexableKeys);
193f91b4dbc331d3c84e06bcfed3338e38a043f4a13Fabrice Di Meglio            forceUpdate = other.forceUpdate;
1946c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            fullIndex = other.fullIndex;
19572e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        }
19672e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio
19772e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        public UpdateData copy() {
19872e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            return new UpdateData(this);
19972e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        }
20072e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio
20151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        public void clear() {
20251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            dataToUpdate.clear();
20351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            dataToDelete.clear();
204df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            nonIndexableKeys.clear();
20551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            forceUpdate = false;
2066c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            fullIndex = false;
20751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
20851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
2096f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
2109301441e2af4f9ee1eca4236e6e04b4666c0d986Fabrice Di Meglio    private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
2119301441e2af4f9ee1eca4236e6e04b4666c0d986Fabrice Di Meglio    private final UpdateData mDataToProcess = new UpdateData();
2129301441e2af4f9ee1eca4236e6e04b4666c0d986Fabrice Di Meglio    private Context mContext;
2139301441e2af4f9ee1eca4236e6e04b4666c0d986Fabrice Di Meglio    private final String mBaseAuthority;
2149301441e2af4f9ee1eca4236e6e04b4666c0d986Fabrice Di Meglio
2156f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    /**
2166f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio     * A basic singleton
2176f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio     */
2186f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    public static Index getInstance(Context context) {
2196f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        if (sInstance == null) {
2206c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            sInstance = new Index(context.getApplicationContext(), BASE_AUTHORITY);
2216f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        }
2226f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        return sInstance;
2236f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
2246f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
225dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio    public Index(Context context, String baseAuthority) {
2266f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        mContext = context;
227dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio        mBaseAuthority = baseAuthority;
2286f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
2296f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
230d25314d3305ed1a07b53991a978cd71219ef2a10Fabrice Di Meglio    public void setContext(Context context) {
231d25314d3305ed1a07b53991a978cd71219ef2a10Fabrice Di Meglio        mContext = context;
232d25314d3305ed1a07b53991a978cd71219ef2a10Fabrice Di Meglio    }
233d25314d3305ed1a07b53991a978cd71219ef2a10Fabrice Di Meglio
2346f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    public boolean isAvailable() {
2356f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        return mIsAvailable.get();
2366f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
2376f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
23851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    public Cursor search(String query) {
239b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        final SQLiteDatabase database = getReadableDatabase();
240b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        final Cursor[] cursors = new Cursor[2];
241b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio
242b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        final String primarySql = buildSearchSQL(query, MATCH_COLUMNS_PRIMARY, true);
243b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        Log.d(LOG_TAG, "Search primary query: " + primarySql);
244b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        cursors[0] = database.rawQuery(primarySql, null);
245b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio
246b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        // We need to use an EXCEPT operator as negate MATCH queries do not work.
247b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        StringBuilder sql = new StringBuilder(
248b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio                buildSearchSQL(query, MATCH_COLUMNS_SECONDARY, false));
249b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        sql.append(" EXCEPT ");
250b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        sql.append(primarySql);
251b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio
252b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        final String secondarySql = sql.toString();
253b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        Log.d(LOG_TAG, "Search secondary query: " + secondarySql);
254b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        cursors[1] = database.rawQuery(secondarySql, null);
255b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio
256b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        return new MergeCursor(cursors);
257d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    }
258d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
259d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    public Cursor getSuggestions(String query) {
260d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        final String sql = buildSuggestionsSQL(query);
261d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        Log.d(LOG_TAG, "Suggestions query: " + sql);
26251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        return getReadableDatabase().rawQuery(sql, null);
26351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
26451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
265d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private String buildSuggestionsSQL(String query) {
266d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        StringBuilder sb = new StringBuilder();
267d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
268d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append("SELECT ");
269d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append(IndexDatabaseHelper.SavedQueriesColums.QUERY);
270d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append(" FROM ");
271d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append(Tables.TABLE_SAVED_QUERIES);
272d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
273d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        if (TextUtils.isEmpty(query)) {
274d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append(" ORDER BY rowId DESC");
275d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        } else {
276d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append(" WHERE ");
277d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append(IndexDatabaseHelper.SavedQueriesColums.QUERY);
278d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append(" LIKE ");
279d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append("'");
280d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append(query);
281d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append("%");
282d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            sb.append("'");
283d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        }
284d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
285d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append(" LIMIT ");
286d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append(MAX_PROPOSED_SUGGESTIONS);
287d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
288d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        return sb.toString();
289d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    }
290d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
291d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    public long addSavedQuery(String query){
292d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        final SaveSearchQueryTask task = new SaveSearchQueryTask();
293d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        task.execute(query);
294d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        try {
295d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            return task.get();
296d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        } catch (InterruptedException e) {
297d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            Log.e(LOG_TAG, "Cannot insert saved query: " + query, e);
298d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            return -1 ;
299d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        } catch (ExecutionException e) {
300d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            Log.e(LOG_TAG, "Cannot insert saved query: " + query, e);
301d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            return -1;
302d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        }
303d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    }
304d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
30572e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio    public void update() {
3066c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk        AsyncTask.execute(new Runnable() {
3076c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            @Override
3086c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            public void run() {
3096c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
3106c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                List<ResolveInfo> list =
3116c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                        mContext.getPackageManager().queryIntentContentProviders(intent, 0);
3126c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk
3136c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                final int size = list.size();
3146c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                for (int n = 0; n < size; n++) {
3156c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    final ResolveInfo info = list.get(n);
3166c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    if (!isWellKnownProvider(info)) {
3176c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                        continue;
3186c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    }
3196c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    final String authority = info.providerInfo.authority;
3206c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    final String packageName = info.providerInfo.packageName;
321df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
3226c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    addIndexablesFromRemoteProvider(packageName, authority);
3236c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    addNonIndexablesKeysFromRemoteProvider(packageName, authority);
3246c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                }
325df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
3266c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                mDataToProcess.fullIndex = true;
3276c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                updateInternal();
3286c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            }
3296c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk        });
330df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
331df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
332df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
333df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        try {
334eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio            final int baseRank = Ranking.getBaseRankForAuthority(authority);
335eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio
336dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio            final Context context = mBaseAuthority.equals(authority) ?
337dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio                    mContext : mContext.createPackageContext(packageName, 0);
338df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
339df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            final Uri uriForResources = buildUriForXmlResources(authority);
340dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio            addIndexablesForXmlResourceUri(context, packageName, uriForResources,
341eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio                    SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS, baseRank);
342df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
343df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            final Uri uriForRawData = buildUriForRawData(authority);
344dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio            addIndexablesForRawDataUri(context, packageName, uriForRawData,
345eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS, baseRank);
346df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return true;
347df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        } catch (PackageManager.NameNotFoundException e) {
348df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
349df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    + Log.getStackTraceString(e));
350df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return false;
351df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        }
352df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
353df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
354df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    private void addNonIndexablesKeysFromRemoteProvider(String packageName,
355df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                                                        String authority) {
356df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        final List<String> keys =
357df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                getNonIndexablesKeysFromRemoteProvider(packageName, authority);
358df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        addNonIndexableKeys(packageName, keys);
359df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
360df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
361df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
362df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                                                                String authority) {
363df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        try {
364df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            final Context packageContext = mContext.createPackageContext(packageName, 0);
365df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
366df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority);
367df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys,
368df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
369df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        } catch (PackageManager.NameNotFoundException e) {
370df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
371df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    + Log.getStackTraceString(e));
372df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return EMPTY_LIST;
373df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        }
374df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
375df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
376df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    private List<String> getNonIndexablesKeys(Context packageContext, Uri uri,
377df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                                              String[] projection) {
378df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
379df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        final ContentResolver resolver = packageContext.getContentResolver();
380df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        final Cursor cursor = resolver.query(uri, projection, null, null, null);
381df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
382df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        if (cursor == null) {
383df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
384df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return EMPTY_LIST;
385df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        }
386df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
387df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        List<String> result = new ArrayList<String>();
388df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        try {
389df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            final int count = cursor.getCount();
390df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            if (count > 0) {
391df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                while (cursor.moveToNext()) {
392df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE);
393df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    result.add(key);
394df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                }
395df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            }
396df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return result;
397df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        } finally {
398df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            cursor.close();
399df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        }
400df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
401df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
402fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    public void addIndexableData(SearchIndexableData data) {
40351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        synchronized (mDataToProcess) {
40451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            mDataToProcess.dataToUpdate.add(data);
405fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
406fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    }
407fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
408fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    public void addIndexableData(SearchIndexableResource[] array) {
40951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        synchronized (mDataToProcess) {
410b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            final int count = array.length;
411b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            for (int n = 0; n < count; n++) {
41251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                mDataToProcess.dataToUpdate.add(array[n]);
413b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            }
414b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio        }
415b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio    }
416b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
417724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio    public void deleteIndexableData(SearchIndexableData data) {
41851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        synchronized (mDataToProcess) {
419724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio            mDataToProcess.dataToDelete.add(data);
420b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio        }
421b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio    }
422b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
423df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    public void addNonIndexableKeys(String authority, List<String> keys) {
424df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        synchronized (mDataToProcess) {
425df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            mDataToProcess.nonIndexableKeys.put(authority, keys);
42651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
42751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
42851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
429b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio    /**
430b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio     * Only allow a "well known" SearchIndexablesProvider. The provider should:
431b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio     *
432b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio     * - have read/write {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}
433b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio     * - be from a privileged package
434b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio     */
435b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio    private boolean isWellKnownProvider(ResolveInfo info) {
436b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        final String authority = info.providerInfo.authority;
437b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        final String packageName = info.providerInfo.applicationInfo.packageName;
438b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
439b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) {
440b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio            return false;
441b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        }
442b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
443b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        final String readPermission = info.providerInfo.readPermission;
444b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        final String writePermission = info.providerInfo.writePermission;
445b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
446b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) {
447b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio            return false;
448b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        }
449b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
450b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) ||
451b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio            !android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) {
452b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio            return false;
453b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        }
454b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
455b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        return isPrivilegedPackage(packageName);
456b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio    }
457b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
458b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio    private boolean isPrivilegedPackage(String packageName) {
459b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        final PackageManager pm = mContext.getPackageManager();
460b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        try {
461b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio            PackageInfo packInfo = pm.getPackageInfo(packageName, 0);
462cc254f4da96506901268c4a0b1d3cfacb5f44948Alex Klyubin            return ((packInfo.applicationInfo.privateFlags
463cc254f4da96506901268c4a0b1d3cfacb5f44948Alex Klyubin                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
464b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        } catch (PackageManager.NameNotFoundException e) {
465b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio            return false;
466b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio        }
467b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio    }
468b1648e6c546523cd3ede111c6b494a96477de2f1Fabrice Di Meglio
46972e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio    private void updateFromRemoteProvider(String packageName, String authority) {
47072e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        if (addIndexablesFromRemoteProvider(packageName, authority)) {
47172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            updateInternal();
47251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
47351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
47451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
475490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio    /**
476490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     * Update the Index for a specific class name resources
477490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     *
478490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     * @param className the class name (typically a fragment name).
479490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     * @param rebuild true means that you want to delete the data from the Index first.
480490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     * @param includeInSearchResults true means that you want the bit "enabled" set so that the
481490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     *                               data will be seen included into the search results
482490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio     */
4836c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk    public void updateFromClassNameResource(String className, final boolean rebuild,
484490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio            boolean includeInSearchResults) {
48551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        if (className == null) {
48651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            throw new IllegalArgumentException("class name cannot be null!");
48751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
48851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        final SearchIndexableResource res = SearchIndexableResources.getResourceByName(className);
48951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        if (res == null ) {
49051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            Log.e(LOG_TAG, "Cannot find SearchIndexableResources for class name: " + className);
49172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            return;
492fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
49355638cb6c6730104ec04287e7566eae7f3e57385Fabrice Di Meglio        res.context = mContext;
49451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        res.enabled = includeInSearchResults;
4956c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk        AsyncTask.execute(new Runnable() {
4966c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            @Override
4976c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            public void run() {
4986c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                if (rebuild) {
4996c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    deleteIndexableData(res);
5006c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                }
5016c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                addIndexableData(res);
5026c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                mDataToProcess.forceUpdate = true;
5036c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                updateInternal();
5046c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                res.enabled = false;
5056c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            }
5066c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk        });
50751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
508fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
50972e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio    public void updateFromSearchIndexableData(SearchIndexableData data) {
5106c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk        AsyncTask.execute(new Runnable() {
5116c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            @Override
5126c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            public void run() {
5136c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                addIndexableData(data);
5146c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                mDataToProcess.forceUpdate = true;
5156c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                updateInternal();
5166c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            }
5176c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk        });
518fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    }
519fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
52051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private SQLiteDatabase getReadableDatabase() {
52151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        return IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
52251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
52351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
52451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private SQLiteDatabase getWritableDatabase() {
525dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio        try {
526dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio            return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
527dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio        } catch (SQLiteException e) {
528dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio            Log.e(LOG_TAG, "Cannot open writable database", e);
529dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio            return null;
530dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio        }
53151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
53251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
533fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    private static Uri buildUriForXmlResources(String authority) {
534fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        return Uri.parse("content://" + authority + "/" +
535fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
536fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    }
537fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
538fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    private static Uri buildUriForRawData(String authority) {
539fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        return Uri.parse("content://" + authority + "/" +
540fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                SearchIndexablesContract.INDEXABLES_RAW_PATH);
541fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    }
542fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
543df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    private static Uri buildUriForNonIndexableKeys(String authority) {
544df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        return Uri.parse("content://" + authority + "/" +
545df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
546df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
547df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
54872e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio    private void updateInternal() {
54951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        synchronized (mDataToProcess) {
55051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            final UpdateIndexTask task = new UpdateIndexTask();
55172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            UpdateData copy = mDataToProcess.copy();
55272e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            task.execute(copy);
55372e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            mDataToProcess.clear();
55451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
55551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
556fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
55751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
558b2385ea46286933add8ea3fb86db7870fe294334Fabrice Di Meglio            Uri uri, String[] projection, int baseRank) {
55951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
56051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        final ContentResolver resolver = packageContext.getContentResolver();
561df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        final Cursor cursor = resolver.query(uri, projection, null, null, null);
562fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
563fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        if (cursor == null) {
564fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
565fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            return;
566fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
567fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
568fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        try {
569fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            final int count = cursor.getCount();
570fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            if (count > 0) {
571fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                while (cursor.moveToNext()) {
572eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio                    final int providerRank = cursor.getInt(COLUMN_INDEX_XML_RES_RANK);
573eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio                    final int rank = (providerRank > 0) ? baseRank + providerRank : baseRank;
574eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio
575b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
576fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
577b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);
578b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID);
579fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
580b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION);
581b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String targetPackage = cursor.getString(
582b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                            COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE);
583b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String targetClass = cursor.getString(
584b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                            COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);
585fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
5865135dce1429facfd764f4afffe69d60c19589750Fabrice Di Meglio                    SearchIndexableResource sir = new SearchIndexableResource(packageContext);
587fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.rank = rank;
588fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.xmlResId = xmlResId;
589fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.className = className;
590fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.packageName = packageName;
591fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.iconResId = iconResId;
592fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.intentAction = action;
593fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.intentTargetPackage = targetPackage;
594fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    sir.intentTargetClass = targetClass;
595fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
596fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    addIndexableData(sir);
597fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                }
598fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            }
599fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        } finally {
600fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            cursor.close();
601fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
602fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    }
603fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
60451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private void addIndexablesForRawDataUri(Context packageContext, String packageName,
605b2385ea46286933add8ea3fb86db7870fe294334Fabrice Di Meglio            Uri uri, String[] projection, int baseRank) {
606fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
60751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        final ContentResolver resolver = packageContext.getContentResolver();
608df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        final Cursor cursor = resolver.query(uri, projection, null, null, null);
609fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
610fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        if (cursor == null) {
611fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
612fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            return;
613fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
614fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
615fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        try {
616fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            final int count = cursor.getCount();
617fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            if (count > 0) {
618fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                while (cursor.moveToNext()) {
619eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio                    final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
620eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio                    final int rank = (providerRank > 0) ? baseRank + providerRank : baseRank;
621eed44c9d5f72a1981b7057ae8f1744863e036078Fabrice Di Meglio
622b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
623b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
624b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF);
625b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES);
626b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS);
627fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
628b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE);
629fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
630b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME);
631b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID);
632fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
633b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION);
634b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String targetPackage = cursor.getString(
635b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                            COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE);
636b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String targetClass = cursor.getString(
637b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                            COLUMN_INDEX_RAW_INTENT_TARGET_CLASS);
638fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
639b914322590d74b508ff94b4426cb4b11a324e72cFabrice Di Meglio                    final String key = cursor.getString(COLUMN_INDEX_RAW_KEY);
640957ccf30b9c969a6362fccb293d2c2df8bd1ad32Fabrice Di Meglio                    final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID);
641c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio
6425135dce1429facfd764f4afffe69d60c19589750Fabrice Di Meglio                    SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
643fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.rank = rank;
644fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.title = title;
6459593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                    data.summaryOn = summaryOn;
6469593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                    data.summaryOff = summaryOff;
647a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                    data.entries = entries;
648fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.keywords = keywords;
649fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.screenTitle = screenTitle;
650fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.className = className;
651fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.packageName = packageName;
652fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.iconResId = iconResId;
653fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.intentAction = action;
654fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.intentTargetPackage = targetPackage;
655fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    data.intentTargetClass = targetClass;
656c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio                    data.key = key;
657957ccf30b9c969a6362fccb293d2c2df8bd1ad32Fabrice Di Meglio                    data.userId = userId;
658fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
659fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                    addIndexableData(data);
660fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio                }
661fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            }
662fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        } finally {
663fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio            cursor.close();
664fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
665fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio    }
666fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
667b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio    private String buildSearchSQL(String query, String[] colums, boolean withOrderBy) {
6686f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        StringBuilder sb = new StringBuilder();
669b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        sb.append(buildSearchSQLForColumn(query, colums));
670b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        if (withOrderBy) {
671b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio            sb.append(" ORDER BY ");
672b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio            sb.append(IndexColumns.DATA_RANK);
673b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio        }
6746f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        return sb.toString();
6756f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
6766f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
677d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private String buildSearchSQLForColumn(String query, String[] columnNames) {
6786f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        StringBuilder sb = new StringBuilder();
6796f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append("SELECT ");
6806f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        for (int n = 0; n < SELECT_COLUMNS.length; n++) {
6816f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            sb.append(SELECT_COLUMNS[n]);
6826f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            if (n < SELECT_COLUMNS.length - 1) {
6836f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio                sb.append(", ");
6846f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            }
6856f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        }
6866f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(" FROM ");
6876f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(Tables.TABLE_PREFS_INDEX);
6886f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(" WHERE ");
689d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        sb.append(buildSearchWhereStringForColumns(query, columnNames));
6906f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
6916f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        return sb.toString();
6926f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
6936f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
694d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private String buildSearchWhereStringForColumns(String query, String[] columnNames) {
6950d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        final StringBuilder sb = new StringBuilder(Tables.TABLE_PREFS_INDEX);
6966f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(" MATCH ");
697d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        DatabaseUtils.appendEscapedSQLString(sb,
698d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                buildSearchMatchStringForColumns(query, columnNames));
6996f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(" AND ");
7006f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(IndexColumns.LOCALE);
7016f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        sb.append(" = ");
7026f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        DatabaseUtils.appendEscapedSQLString(sb, Locale.getDefault().toString());
70351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        sb.append(" AND ");
70451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        sb.append(IndexColumns.ENABLED);
70551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        sb.append(" = 1");
7066f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        return sb.toString();
7076f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
7086f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
709d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private String buildSearchMatchStringForColumns(String query, String[] columnNames) {
7100d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        final String value = query + "*";
7110d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        StringBuilder sb = new StringBuilder();
7120d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        final int count = columnNames.length;
7130d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        for (int n = 0; n < count; n++) {
7140d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio            sb.append(columnNames[n]);
7150d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio            sb.append(":");
7160d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio            sb.append(value);
7170d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio            if (n < count - 1) {
7180d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio                sb.append(" OR ");
7190d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio            }
7200d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        }
7210d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio        return sb.toString();
7220d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio    }
7230d4005a1c755c4233bdf75d30a1b6ebc99856ec7Fabrice Di Meglio
72451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
725df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            SearchIndexableData data, Map<String, List<String>> nonIndexableKeys) {
72651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        if (data instanceof SearchIndexableResource) {
727df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
72851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        } else if (data instanceof SearchIndexableRaw) {
72951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            indexOneRaw(database, localeStr, (SearchIndexableRaw) data);
73051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
7316f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
7326f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
733df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    private void indexOneRaw(SQLiteDatabase database, String localeStr,
734df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                             SearchIndexableRaw raw) {
735df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        // Should be the same locale as the one we are processing
736df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
737df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            return;
738df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        }
739df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
740df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        updateOneRowWithFilteredData(database, localeStr,
741df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.title,
742df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.summaryOn,
743df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.summaryOff,
744df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.entries,
745df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.className,
746df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.screenTitle,
747df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.iconResId,
748df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.rank,
749df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.keywords,
750df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.intentAction,
751df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.intentTargetPackage,
752df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.intentTargetClass,
753df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                raw.enabled,
754d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                raw.key,
755d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                raw.userId);
756df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
757df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
7589a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio    private static boolean isIndexableClass(final Class<?> clazz) {
7599a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        return (clazz != null) && Indexable.class.isAssignableFrom(clazz);
7609a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio    }
7619a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio
7629a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio    private static Class<?> getIndexableClass(String className) {
7639a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        final Class<?> clazz;
7649a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        try {
7659a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            clazz = Class.forName(className);
7669a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        } catch (ClassNotFoundException e) {
7679a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            Log.d(LOG_TAG, "Cannot find class: " + className);
7689a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            return null;
7699a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        }
7709a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        return isIndexableClass(clazz) ? clazz : null;
7719a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio    }
7729a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio
77351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private void indexOneResource(SQLiteDatabase database, String localeStr,
774df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            SearchIndexableResource sir, Map<String, List<String>> nonIndexableKeysFromResource) {
77545f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
77645f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio        if (sir == null) {
77745f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            Log.e(LOG_TAG, "Cannot index a null resource!");
77845f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            return;
77945f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio        }
78045f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
7819a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        final List<String> nonIndexableKeys = new ArrayList<String>();
782df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
783a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
784df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            List<String> resNonIndxableKeys = nonIndexableKeysFromResource.get(sir.packageName);
785df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            if (resNonIndxableKeys != null && resNonIndxableKeys.size() > 0) {
786df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                nonIndexableKeys.addAll(resNonIndxableKeys);
78745f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            }
7889a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio
78951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            indexFromResource(sir.context, database, localeStr,
79051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                    sir.xmlResId, sir.className, sir.iconResId, sir.rank,
79145f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                    sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass,
792df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    nonIndexableKeys);
7939a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        } else {
7949a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            if (TextUtils.isEmpty(sir.className)) {
7959a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");
7969a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                return;
7979a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            }
7989a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio
7999a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            final Class<?> clazz = getIndexableClass(sir.className);
8009a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            if (clazz == null) {
8019a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                Log.d(LOG_TAG, "SearchIndexableResource '" + sir.className +
8029a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                        "' should implement the " + Indexable.class.getName() + " interface!");
8039a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                return;
8049a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            }
8059a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio
8069a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            // Will be non null only for a Local provider implementing a
8079a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            // SEARCH_INDEX_DATA_PROVIDER field
8089a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz);
809df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            if (provider != null) {
810df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(sir.context);
811df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                if (providerNonIndexableKeys != null && providerNonIndexableKeys.size() > 0) {
812df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                    nonIndexableKeys.addAll(providerNonIndexableKeys);
813df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                }
8149a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio
8159a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                indexFromProvider(mContext, database, localeStr, provider, sir.className,
8169a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                        sir.iconResId, sir.rank, sir.enabled, nonIndexableKeys);
817df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            }
818df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        }
819df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio    }
820df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
8219a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio    private Indexable.SearchIndexProvider getSearchIndexProvider(final Class<?> clazz) {
822df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        try {
8239a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER);
8249a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            return (Indexable.SearchIndexProvider) f.get(null);
825df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        } catch (NoSuchFieldException e) {
8269a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            Log.d(LOG_TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
8279a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        } catch (SecurityException se) {
8289a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            Log.d(LOG_TAG,
8299a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                    "Security exception for field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
830df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        } catch (IllegalAccessException e) {
8319a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            Log.d(LOG_TAG,
8329a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                    "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
8339a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio        } catch (IllegalArgumentException e) {
8349a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio            Log.d(LOG_TAG,
8359a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                    "Illegal argument when accessing field '" +
8369a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio                            FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
83751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
838df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio        return null;
8396f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
8406f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
84151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private void indexFromResource(Context context, SQLiteDatabase database, String localeStr,
84245f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio           int xmlResId, String fragmentName, int iconResId, int rank,
84345f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio           String intentAction, String intentTargetPackage, String intentTargetClass,
844df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio           List<String> nonIndexableKeys) {
845b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
84651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        XmlResourceParser parser = null;
84751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        try {
84851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            parser = context.getResources().getXml(xmlResId);
84951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
85051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            int type;
85151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
85251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                    && type != XmlPullParser.START_TAG) {
85351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                // Parse next until start tag is found
85451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            }
85551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
85651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            String nodeName = parser.getName();
8579593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio            if (!NODE_NAME_PREFERENCE_SCREEN.equals(nodeName)) {
85851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                throw new RuntimeException(
85951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                        "XML document must start with <PreferenceScreen> tag; found"
86051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                                + nodeName + " at " + parser.getPositionDescription());
86151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            }
86251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
86351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            final int outerDepth = parser.getDepth();
86451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            final AttributeSet attrs = Xml.asAttributeSet(parser);
86545f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
86651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            final String screenTitle = getDataTitle(context, attrs);
86751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
868c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio            String key = getDataKey(context, attrs);
86951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
87045f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            String title;
87145f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            String summary;
87245f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            String keywords;
87345f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
87451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
87551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            // hyphens.
876df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            if (!nonIndexableKeys.contains(key)) {
87745f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                title = getDataTitle(context, attrs);
87845f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                summary = getDataSummary(context, attrs);
87945f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                keywords = getDataKeywords(context, attrs);
88045f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
88145f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                updateOneRowWithFilteredData(database, localeStr, title, summary, null, null,
88245f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                        fragmentName, screenTitle, iconResId, rank,
883d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                        keywords, intentAction, intentTargetPackage, intentTargetClass, true,
884d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                        key, -1 /* default user id */);
88545f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            }
88651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
88751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
88851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
88951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
89051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                    continue;
89151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                }
89251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
8939593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                nodeName = parser.getName();
8949593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
89545f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                key = getDataKey(context, attrs);
896df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                if (nonIndexableKeys.contains(key)) {
89745f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                    continue;
89845f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                }
89945f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
90051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                title = getDataTitle(context, attrs);
90151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                keywords = getDataKeywords(context, attrs);
90251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
9039593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
9049593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                    summary = getDataSummary(context, attrs);
9059593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
906a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                    String entries = null;
907dd41dfc483e830bdb33df858dec99e2c4c806bfcFabrice Di Meglio
908a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                    if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) {
909a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                        entries = getDataEntries(context, attrs);
910a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                    }
911a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio
9129593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                    // Insert rows for the child nodes of PreferenceScreen
913a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                    updateOneRowWithFilteredData(database, localeStr, title, summary, null, entries,
9147d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio                            fragmentName, screenTitle, iconResId, rank,
915c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio                            keywords, intentAction, intentTargetPackage, intentTargetClass,
916d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                            true, key, -1 /* default user id */);
917c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio                } else {
918b7390ba5854c0f583858e085bff89eebe307eb39Fabrice Di Meglio                    String summaryOn = getDataSummaryOn(context, attrs);
919b7390ba5854c0f583858e085bff89eebe307eb39Fabrice Di Meglio                    String summaryOff = getDataSummaryOff(context, attrs);
920b7390ba5854c0f583858e085bff89eebe307eb39Fabrice Di Meglio
921b7390ba5854c0f583858e085bff89eebe307eb39Fabrice Di Meglio                    if (TextUtils.isEmpty(summaryOn) && TextUtils.isEmpty(summaryOff)) {
922b7390ba5854c0f583858e085bff89eebe307eb39Fabrice Di Meglio                        summaryOn = getDataSummary(context, attrs);
923b7390ba5854c0f583858e085bff89eebe307eb39Fabrice Di Meglio                    }
9249593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
9259593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                    updateOneRowWithFilteredData(database, localeStr, title, summaryOn, summaryOff,
9267d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio                            null, fragmentName, screenTitle, iconResId, rank,
927c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio                            keywords, intentAction, intentTargetPackage, intentTargetClass,
928d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                            true, key, -1 /* default user id */);
9299593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                }
93051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            }
93151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
93251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        } catch (XmlPullParserException e) {
93351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            throw new RuntimeException("Error parsing PreferenceScreen", e);
93451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        } catch (IOException e) {
93551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            throw new RuntimeException("Error parsing PreferenceScreen", e);
93651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        } finally {
93751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            if (parser != null) parser.close();
938b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio        }
93951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
940b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
9419a60d7b5697873a41810fe87a89e60824e3548daFabrice Di Meglio    private void indexFromProvider(Context context, SQLiteDatabase database, String localeStr,
942df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            Indexable.SearchIndexProvider provider, String className, int iconResId, int rank,
943df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio            boolean enabled, List<String> nonIndexableKeys) {
94445f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
945a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        if (provider == null) {
94645f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio            Log.w(LOG_TAG, "Cannot find provider: " + className);
947a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            return;
948a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        }
949a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio
95045f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio        final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(context, enabled);
95145f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
952a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        if (rawList != null) {
953a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            final int rawSize = rawList.size();
954a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            for (int i = 0; i < rawSize; i++) {
955a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                SearchIndexableRaw raw = rawList.get(i);
956a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio
957a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                // Should be the same locale as the one we are processing
958a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
959a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                    continue;
960a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                }
961a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio
962df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                if (nonIndexableKeys.contains(raw.key)) {
96345f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                    continue;
96445f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                }
96545f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio
966a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                updateOneRowWithFilteredData(database, localeStr,
967a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.title,
968a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.summaryOn,
969a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.summaryOff,
970a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.entries,
97145f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                        className,
972a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.screenTitle,
97345f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                        iconResId,
97445f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                        rank,
975a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.keywords,
976a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.intentAction,
977a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.intentTargetPackage,
978a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.intentTargetClass,
979a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        raw.enabled,
980d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                        raw.key,
981d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio                        raw.userId);
982a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            }
983a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        }
984a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio
985a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        final List<SearchIndexableResource> resList =
98645f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                provider.getXmlResourcesToIndex(context, enabled);
987a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        if (resList != null) {
988a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            final int resSize = resList.size();
989a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            for (int i = 0; i < resSize; i++) {
990a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                SearchIndexableResource item = resList.get(i);
991a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio
992a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                // Should be the same locale as the one we are processing
993a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                if (!item.locale.toString().equalsIgnoreCase(localeStr)) {
994a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                    continue;
995a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                }
996a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio
997caa0b54cbcb71b1124ea50cb0cf5abb6a4d1275fSvetoslav                final int itemIconResId = (item.iconResId == 0) ? iconResId : item.iconResId;
998caa0b54cbcb71b1124ea50cb0cf5abb6a4d1275fSvetoslav                final int itemRank = (item.rank == 0) ? rank : item.rank;
999caa0b54cbcb71b1124ea50cb0cf5abb6a4d1275fSvetoslav                String itemClassName = (TextUtils.isEmpty(item.className))
1000caa0b54cbcb71b1124ea50cb0cf5abb6a4d1275fSvetoslav                        ? className : item.className;
1001caa0b54cbcb71b1124ea50cb0cf5abb6a4d1275fSvetoslav
100245f754e50618076213ed6911ff1e6c37b4db0467Fabrice Di Meglio                indexFromResource(context, database, localeStr,
1003caa0b54cbcb71b1124ea50cb0cf5abb6a4d1275fSvetoslav                        item.xmlResId, itemClassName, itemIconResId, itemRank,
1004a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio                        item.intentAction, item.intentTargetPackage,
1005df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                        item.intentTargetClass, nonIndexableKeys);
1006a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio            }
1007a9d37050cb427cca92587341f399606edfc8ef8dFabrice Di Meglio        }
100851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
100951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
101051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private void updateOneRowWithFilteredData(SQLiteDatabase database, String locale,
1011dd41dfc483e830bdb33df858dec99e2c4c806bfcFabrice Di Meglio            String title, String summaryOn, String summaryOff, String entries,
10127d39310c8d159190cfcc63aa6647714c056d76f6Fabrice Di Meglio            String className,
1013a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio            String screenTitle, int iconResId, int rank, String keywords,
101451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            String intentAction, String intentTargetPackage, String intentTargetClass,
1015d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio            boolean enabled, String key, int userId) {
101651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
1017363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String updatedTitle = normalizeHyphen(title);
1018363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String updatedSummaryOn = normalizeHyphen(summaryOn);
1019363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String updatedSummaryOff = normalizeHyphen(summaryOff);
102051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
1021363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String normalizedTitle = normalizeString(updatedTitle);
1022363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String normalizedSummaryOn = normalizeString(updatedSummaryOn);
1023363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String normalizedSummaryOff = normalizeString(updatedSummaryOff);
102451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
102535c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette        final String spaceDelimitedKeywords = normalizeKeywords(keywords);
102635c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette
102751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        updateOneRow(database, locale,
10289593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                updatedTitle, normalizedTitle, updatedSummaryOn, normalizedSummaryOn,
102935c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette                updatedSummaryOff, normalizedSummaryOff, entries, className, screenTitle, iconResId,
103035c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette                rank, spaceDelimitedKeywords, intentAction, intentTargetPackage, intentTargetClass,
103135c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette                enabled, key, userId);
103251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
103351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
1034a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio    private static String normalizeHyphen(String input) {
1035a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio        return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
1036a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio    }
1037a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio
1038a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio    private static String normalizeString(String input) {
1039363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String nohyphen = (input != null) ? input.replaceAll(HYPHEN, EMPTY) : EMPTY;
1040363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        final String normalized = Normalizer.normalize(nohyphen, Normalizer.Form.NFD);
1041363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio
1042363348dd8bd1e58d8b7faa8a9405589e41d28e48Fabrice Di Meglio        return REMOVE_DIACRITICALS_PATTERN.matcher(normalized).replaceAll("").toLowerCase();
1043a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio    }
1044a8ac78b3925a0b463c2df37a7c83d8f15222a601Fabrice Di Meglio
104535c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette    private static String normalizeKeywords(String input) {
104635c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette        return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
104735c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette    }
104835c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette
104935c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette    private void updateOneRow(SQLiteDatabase database, String locale, String updatedTitle,
105035c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette            String normalizedTitle, String updatedSummaryOn, String normalizedSummaryOn,
105135c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette            String updatedSummaryOff, String normalizedSummaryOff, String entries, String className,
105235c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette            String screenTitle, int iconResId, int rank, String spaceDelimitedKeywords,
105351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            String intentAction, String intentTargetPackage, String intentTargetClass,
1054d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio            boolean enabled, String key, int userId) {
105551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
105651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        if (TextUtils.isEmpty(updatedTitle)) {
105751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            return;
105851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        }
105951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
106014be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio        // The DocID should contains more than the title string itself (you may have two settings
106114be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio        // with the same title). So we need to use a combination of the title and the screenTitle.
106214be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio        StringBuilder sb = new StringBuilder(updatedTitle);
106314be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio        sb.append(screenTitle);
106414be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio        int docId = sb.toString().hashCode();
106514be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio
106651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        ContentValues values = new ContentValues();
106714be7d23a2ee023796126453bba55f4bc84ba065Fabrice Di Meglio        values.put(IndexColumns.DOCID, docId);
106851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.LOCALE, locale);
106951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.DATA_RANK, rank);
107051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.DATA_TITLE, updatedTitle);
107151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle);
10729593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio        values.put(IndexColumns.DATA_SUMMARY_ON, updatedSummaryOn);
10739593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio        values.put(IndexColumns.DATA_SUMMARY_ON_NORMALIZED, normalizedSummaryOn);
10749593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio        values.put(IndexColumns.DATA_SUMMARY_OFF, updatedSummaryOff);
10759593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio        values.put(IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, normalizedSummaryOff);
1076a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        values.put(IndexColumns.DATA_ENTRIES, entries);
107735c3454808a8c9e5ca7826644470798dedda5b26Alan Viverette        values.put(IndexColumns.DATA_KEYWORDS, spaceDelimitedKeywords);
107851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.CLASS_NAME, className);
107951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.SCREEN_TITLE, screenTitle);
108051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.INTENT_ACTION, intentAction);
108151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage);
108251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass);
108351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.ICON, iconResId);
108451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        values.put(IndexColumns.ENABLED, enabled);
1085c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio        values.put(IndexColumns.DATA_KEY_REF, key);
1086d17a15bbc473a7262755ff136b15a3587f4c7ddbFabrice Di Meglio        values.put(IndexColumns.USER_ID, userId);
108751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
108851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        database.replaceOrThrow(Tables.TABLE_PREFS_INDEX, null, values);
108951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
109051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
1091c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio    private String getDataKey(Context context, AttributeSet attrs) {
1092c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio        return getData(context, attrs,
1093c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio                com.android.internal.R.styleable.Preference,
1094c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio                com.android.internal.R.styleable.Preference_key);
1095c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio    }
1096c1457323d271309d5d1955743cd806417c84b9d6Fabrice Di Meglio
109751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private String getDataTitle(Context context, AttributeSet attrs) {
109851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        return getData(context, attrs,
109951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                com.android.internal.R.styleable.Preference,
110051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                com.android.internal.R.styleable.Preference_title);
110151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
110251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
110351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private String getDataSummary(Context context, AttributeSet attrs) {
110451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        return getData(context, attrs,
110551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                com.android.internal.R.styleable.Preference,
110651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                com.android.internal.R.styleable.Preference_summary);
110751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
110851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
11099593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    private String getDataSummaryOn(Context context, AttributeSet attrs) {
11109593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio        return getData(context, attrs,
11119593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                com.android.internal.R.styleable.CheckBoxPreference,
11129593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                com.android.internal.R.styleable.CheckBoxPreference_summaryOn);
11139593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    }
11149593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
11159593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    private String getDataSummaryOff(Context context, AttributeSet attrs) {
11169593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio        return getData(context, attrs,
11179593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                com.android.internal.R.styleable.CheckBoxPreference,
11189593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio                com.android.internal.R.styleable.CheckBoxPreference_summaryOff);
11199593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio    }
11209593782d154f378a0c911cd015d410c18fe74a92Fabrice Di Meglio
1121a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio    private String getDataEntries(Context context, AttributeSet attrs) {
1122a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        return getDataEntries(context, attrs,
1123a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                com.android.internal.R.styleable.ListPreference,
1124a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                com.android.internal.R.styleable.ListPreference_entries);
1125a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio    }
1126a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio
112751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private String getDataKeywords(Context context, AttributeSet attrs) {
112851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords);
112951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    }
113051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
113151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio    private String getData(Context context, AttributeSet set, int[] attrs, int resId) {
113251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        final TypedArray sa = context.obtainStyledAttributes(set, attrs);
113351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        final TypedValue tv = sa.peekValue(resId);
113451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
113551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        CharSequence data = null;
113651bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        if (tv != null && tv.type == TypedValue.TYPE_STRING) {
113751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            if (tv.resourceId != 0) {
113851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                data = context.getText(tv.resourceId);
113951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            } else {
114051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                data = tv.string;
114151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            }
1142b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio        }
114351bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        return (data != null) ? data.toString() : null;
1144b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio    }
1145b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
1146a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio    private String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) {
1147a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        final TypedArray sa = context.obtainStyledAttributes(set, attrs);
1148a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        final TypedValue tv = sa.peekValue(resId);
1149a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio
1150a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        String[] data = null;
1151a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        if (tv != null && tv.type == TypedValue.TYPE_REFERENCE) {
1152a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio            if (tv.resourceId != 0) {
1153a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio                data = context.getResources().getStringArray(tv.resourceId);
1154a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio            }
1155a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        }
1156a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        final int count = (data == null ) ? 0 : data.length;
1157a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        if (count == 0) {
1158a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio            return null;
1159a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        }
1160a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        final StringBuilder result = new StringBuilder();
1161a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        for (int n = 0; n < count; n++) {
1162a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio            result.append(data[n]);
11638f5c65fd010d249770fda81c1129c1d7555a3299Fabrice Di Meglio            result.append(ENTRIES_SEPARATOR);
1164a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        }
1165a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio        return result.toString();
1166a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio    }
1167a41707200b6628376e8fb575e782d8228c0d32a2Fabrice Di Meglio
1168b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio    /**
11696f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio     * A private class for updating the Index database
11706f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio     */
117172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio    private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Void> {
11726f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
11736f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        @Override
11746f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        protected void onPreExecute() {
11756f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            super.onPreExecute();
11766f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            mIsAvailable.set(false);
11776f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        }
11786f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
11796f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        @Override
118072e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        protected void onPostExecute(Void aVoid) {
118172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            super.onPostExecute(aVoid);
11826f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            mIsAvailable.set(true);
11836f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        }
11846f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
11856f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        @Override
118672e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio        protected Void doInBackground(UpdateData... params) {
1187ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk            try {
1188ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate;
1189ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final List<SearchIndexableData> dataToDelete = params[0].dataToDelete;
1190ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final Map<String, List<String>> nonIndexableKeys = params[0].nonIndexableKeys;
1191df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
1192ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final boolean forceUpdate = params[0].forceUpdate;
1193ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final boolean fullIndex = params[0].fullIndex;
1194df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio
1195ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final SQLiteDatabase database = getWritableDatabase();
1196ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                if (database == null) {
1197ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    Log.e(LOG_TAG, "Cannot update Index as I cannot get a writable database");
1198ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    return null;
1199ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                }
1200ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                final String localeStr = Locale.getDefault().toString();
1201b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
1202ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                try {
1203ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    database.beginTransaction();
1204ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    if (dataToDelete.size() > 0) {
1205ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                        processDataToDelete(database, localeStr, dataToDelete);
1206ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    }
1207ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    if (dataToUpdate.size() > 0) {
1208ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                        processDataToUpdate(database, localeStr, dataToUpdate, nonIndexableKeys,
1209ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                                forceUpdate);
1210ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    }
1211ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    database.setTransactionSuccessful();
1212ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                } finally {
1213ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    database.endTransaction();
1214490099b1352f9a4dd5c3f2becf3707d5c0d78452Fabrice Di Meglio                }
1215ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                if (fullIndex) {
1216ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                    IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
1217b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio                }
1218ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk            } catch (SQLiteFullException e) {
1219ef71f605f3290be8c5d2158ead21ae6f9ead2d86Jason Monk                Log.e(LOG_TAG, "Unable to index search, out of space", e);
12206c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk            }
122172e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio
122272e07cf21c33c453bd3a3bd8756f52908432ee0dFabrice Di Meglio            return null;
1223b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio        }
1224b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
122551bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,
1226df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                List<SearchIndexableData> dataToUpdate, Map<String, List<String>> nonIndexableKeys,
1227df278aa959b5e0a5d2b5d9152b7013d22757cd17Fabrice Di Meglio                boolean forceUpdate) {
122851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio
122918fbdd023bfbd9ea7747894c2d9464f66075b703Jason Monk            if (!forceUpdate && IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)) {
123051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
123151bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                return true;
123251bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            }
1233b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
1234b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            boolean result = false;
1235b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            final long current = System.currentTimeMillis();
1236b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
123751bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            final int count = dataToUpdate.size();
1238b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            for (int n = 0; n < count; n++) {
123951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio                final SearchIndexableData data = dataToUpdate.get(n);
1240dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio                try {
1241dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio                    indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);
1242dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio                } catch (Exception e) {
12436c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                    Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
12446c9e884aa04b19634722d95cb65d7df1050a0c28Jason Monk                                    + " for locale: " + localeStr, e);
1245dba577f8f017baac0086c185579340db1c9281d9Fabrice Di Meglio                }
1246b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            }
1247b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
1248b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            final long now = System.currentTimeMillis();
124951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
1250b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio                    (now - current) + " millis");
1251b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            return result;
1252b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio        }
1253b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
125451bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio        private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
1255724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                List<SearchIndexableData> dataToDelete) {
1256b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
1257b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            boolean result = false;
1258b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            final long current = System.currentTimeMillis();
1259b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
126051bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            final int count = dataToDelete.size();
1261b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            for (int n = 0; n < count; n++) {
1262724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                final SearchIndexableData data = dataToDelete.get(n);
1263724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                if (data == null) {
1264724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                    continue;
1265724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                }
1266724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                if (!TextUtils.isEmpty(data.className)) {
1267724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                    delete(database, IndexColumns.CLASS_NAME, data.className);
1268724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                } else  {
1269724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                    if (data instanceof SearchIndexableRaw) {
1270724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                        final SearchIndexableRaw raw = (SearchIndexableRaw) data;
1271724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                        if (!TextUtils.isEmpty(raw.title)) {
1272724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                            delete(database, IndexColumns.DATA_TITLE, raw.title);
1273724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                        }
1274724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                    }
1275724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio                }
1276b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio            }
1277b8dfbf140986a9c0781f268ea40914063f026436Fabrice Di Meglio
12786f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            final long now = System.currentTimeMillis();
127951bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
12806f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio                    (now - current) + " millis");
12816f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio            return result;
12826f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio        }
12836f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio
1284724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio        private int delete(SQLiteDatabase database, String columName, String value) {
1285724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio            final String whereClause = columName + "=?";
1286724b702a8627c933cef536e40d2db5918bf45ccbFabrice Di Meglio            final String[] whereArgs = new String[] { value };
1287fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio
128851bfee595c3ce587e2e26565fd9e8f4ae02c3482Fabrice Di Meglio            return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs);
1289fa7dc240e978b1b77bb5c80a78cd1f8c77648301Fabrice Di Meglio        }
12906f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio    }
1291d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1292d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    /**
1293b86a2bc745c1fbbdf23e470e946b7e57f7e090a9Fabrice Di Meglio     * A basic AsyncTask for saving a Search query into the database
1294d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio     */
1295d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    private class SaveSearchQueryTask extends AsyncTask<String, Void, Long> {
1296d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1297d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        @Override
1298d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        protected Long doInBackground(String... params) {
1299d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            final long now = new Date().getTime();
1300d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1301d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            final ContentValues values = new ContentValues();
1302d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            values.put(IndexDatabaseHelper.SavedQueriesColums.QUERY, params[0]);
1303d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            values.put(IndexDatabaseHelper.SavedQueriesColums.TIME_STAMP, now);
1304d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1305d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            final SQLiteDatabase database = getWritableDatabase();
1306dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio            if (database == null) {
1307dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio                Log.e(LOG_TAG, "Cannot save Search queries as I cannot get a writable database");
1308dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio                return -1L;
1309dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio            }
1310d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1311dff3faaf30bd6d695e4d9f632af53dbc73cf6506Fabrice Di Meglio            long lastInsertedRowId = -1L;
1312d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            try {
1313b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                // First, delete all saved queries that are the same
1314b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                database.delete(Tables.TABLE_SAVED_QUERIES,
1315b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                        IndexDatabaseHelper.SavedQueriesColums.QUERY + " = ?",
1316b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                        new String[] { params[0] });
1317b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio
1318b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                // Second, insert the saved query
1319d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                lastInsertedRowId =
1320b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                        database.insertOrThrow(Tables.TABLE_SAVED_QUERIES, null, values);
1321d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1322b58b28e13e2771cbe5dc102488cdd86dea14e5b2Fabrice Di Meglio                // Last, remove "old" saved queries
1323d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                final long delta = lastInsertedRowId - MAX_SAVED_SEARCH_QUERY;
1324d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                if (delta > 0) {
1325d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                    int count = database.delete(Tables.TABLE_SAVED_QUERIES, "rowId <= ?",
1326d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                            new String[] { Long.toString(delta) });
1327d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                    Log.d(LOG_TAG, "Deleted '" + count + "' saved Search query(ies)");
1328d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                }
1329d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            } catch (Exception e) {
1330d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio                Log.d(LOG_TAG, "Cannot update saved Search queries", e);
1331d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            }
1332d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio
1333d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio            return lastInsertedRowId;
1334d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio        }
1335d297a5840230b769a3c7ad8b85232968a7077f64Fabrice Di Meglio    }
13366f0739a3d9a9eca1591ae6bada1dd4d1d92c0defFabrice Di Meglio}
1337