1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.search; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.database.sqlite.SQLiteDatabase; 22 import android.database.sqlite.SQLiteOpenHelper; 23 import android.os.Build; 24 import android.util.Log; 25 26 public class IndexDatabaseHelper extends SQLiteOpenHelper { 27 28 private static final String TAG = "IndexDatabaseHelper"; 29 30 private static final String DATABASE_NAME = "search_index.db"; 31 private static final int DATABASE_VERSION = 115; 32 33 private static final String INDEX = "index"; 34 35 public interface Tables { 36 public static final String TABLE_PREFS_INDEX = "prefs_index"; 37 public static final String TABLE_META_INDEX = "meta_index"; 38 public static final String TABLE_SAVED_QUERIES = "saved_queries"; 39 } 40 41 public interface IndexColumns { 42 public static final String DOCID = "docid"; 43 public static final String LOCALE = "locale"; 44 public static final String DATA_RANK = "data_rank"; 45 public static final String DATA_TITLE = "data_title"; 46 public static final String DATA_TITLE_NORMALIZED = "data_title_normalized"; 47 public static final String DATA_SUMMARY_ON = "data_summary_on"; 48 public static final String DATA_SUMMARY_ON_NORMALIZED = "data_summary_on_normalized"; 49 public static final String DATA_SUMMARY_OFF = "data_summary_off"; 50 public static final String DATA_SUMMARY_OFF_NORMALIZED = "data_summary_off_normalized"; 51 public static final String DATA_ENTRIES = "data_entries"; 52 public static final String DATA_KEYWORDS = "data_keywords"; 53 public static final String CLASS_NAME = "class_name"; 54 public static final String SCREEN_TITLE = "screen_title"; 55 public static final String INTENT_ACTION = "intent_action"; 56 public static final String INTENT_TARGET_PACKAGE = "intent_target_package"; 57 public static final String INTENT_TARGET_CLASS = "intent_target_class"; 58 public static final String ICON = "icon"; 59 public static final String ENABLED = "enabled"; 60 public static final String DATA_KEY_REF = "data_key_reference"; 61 public static final String USER_ID = "user_id"; 62 } 63 64 public interface MetaColumns { 65 public static final String BUILD = "build"; 66 } 67 68 public interface SavedQueriesColums { 69 public static final String QUERY = "query"; 70 public static final String TIME_STAMP = "timestamp"; 71 } 72 73 private static final String CREATE_INDEX_TABLE = 74 "CREATE VIRTUAL TABLE " + Tables.TABLE_PREFS_INDEX + " USING fts4" + 75 "(" + 76 IndexColumns.LOCALE + 77 ", " + 78 IndexColumns.DATA_RANK + 79 ", " + 80 IndexColumns.DATA_TITLE + 81 ", " + 82 IndexColumns.DATA_TITLE_NORMALIZED + 83 ", " + 84 IndexColumns.DATA_SUMMARY_ON + 85 ", " + 86 IndexColumns.DATA_SUMMARY_ON_NORMALIZED + 87 ", " + 88 IndexColumns.DATA_SUMMARY_OFF + 89 ", " + 90 IndexColumns.DATA_SUMMARY_OFF_NORMALIZED + 91 ", " + 92 IndexColumns.DATA_ENTRIES + 93 ", " + 94 IndexColumns.DATA_KEYWORDS + 95 ", " + 96 IndexColumns.SCREEN_TITLE + 97 ", " + 98 IndexColumns.CLASS_NAME + 99 ", " + 100 IndexColumns.ICON + 101 ", " + 102 IndexColumns.INTENT_ACTION + 103 ", " + 104 IndexColumns.INTENT_TARGET_PACKAGE + 105 ", " + 106 IndexColumns.INTENT_TARGET_CLASS + 107 ", " + 108 IndexColumns.ENABLED + 109 ", " + 110 IndexColumns.DATA_KEY_REF + 111 ", " + 112 IndexColumns.USER_ID + 113 ");"; 114 115 private static final String CREATE_META_TABLE = 116 "CREATE TABLE " + Tables.TABLE_META_INDEX + 117 "(" + 118 MetaColumns.BUILD + " VARCHAR(32) NOT NULL" + 119 ")"; 120 121 private static final String CREATE_SAVED_QUERIES_TABLE = 122 "CREATE TABLE " + Tables.TABLE_SAVED_QUERIES + 123 "(" + 124 SavedQueriesColums.QUERY + " VARCHAR(64) NOT NULL" + 125 ", " + 126 SavedQueriesColums.TIME_STAMP + " INTEGER" + 127 ")"; 128 129 private static final String INSERT_BUILD_VERSION = 130 "INSERT INTO " + Tables.TABLE_META_INDEX + 131 " VALUES ('" + Build.VERSION.INCREMENTAL + "');"; 132 133 private static final String SELECT_BUILD_VERSION = 134 "SELECT " + MetaColumns.BUILD + " FROM " + Tables.TABLE_META_INDEX + " LIMIT 1;"; 135 136 private static IndexDatabaseHelper sSingleton; 137 138 private final Context mContext; 139 140 public static synchronized IndexDatabaseHelper getInstance(Context context) { 141 if (sSingleton == null) { 142 sSingleton = new IndexDatabaseHelper(context); 143 } 144 return sSingleton; 145 } 146 147 public IndexDatabaseHelper(Context context) { 148 super(context, DATABASE_NAME, null, DATABASE_VERSION); 149 mContext = context; 150 } 151 152 @Override 153 public void onCreate(SQLiteDatabase db) { 154 bootstrapDB(db); 155 } 156 157 private void bootstrapDB(SQLiteDatabase db) { 158 db.execSQL(CREATE_INDEX_TABLE); 159 db.execSQL(CREATE_META_TABLE); 160 db.execSQL(CREATE_SAVED_QUERIES_TABLE); 161 db.execSQL(INSERT_BUILD_VERSION); 162 Log.i(TAG, "Bootstrapped database"); 163 } 164 165 @Override 166 public void onOpen(SQLiteDatabase db) { 167 super.onOpen(db); 168 169 Log.i(TAG, "Using schema version: " + db.getVersion()); 170 171 if (!Build.VERSION.INCREMENTAL.equals(getBuildVersion(db))) { 172 Log.w(TAG, "Index needs to be rebuilt as build-version is not the same"); 173 // We need to drop the tables and recreate them 174 reconstruct(db); 175 } else { 176 Log.i(TAG, "Index is fine"); 177 } 178 } 179 180 @Override 181 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 182 if (oldVersion < DATABASE_VERSION) { 183 Log.w(TAG, "Detected schema version '" + oldVersion + "'. " + 184 "Index needs to be rebuilt for schema version '" + newVersion + "'."); 185 // We need to drop the tables and recreate them 186 reconstruct(db); 187 } 188 } 189 190 @Override 191 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 192 Log.w(TAG, "Detected schema version '" + oldVersion + "'. " + 193 "Index needs to be rebuilt for schema version '" + newVersion + "'."); 194 // We need to drop the tables and recreate them 195 reconstruct(db); 196 } 197 198 private void reconstruct(SQLiteDatabase db) { 199 dropTables(db); 200 bootstrapDB(db); 201 } 202 203 private String getBuildVersion(SQLiteDatabase db) { 204 String version = null; 205 Cursor cursor = null; 206 try { 207 cursor = db.rawQuery(SELECT_BUILD_VERSION, null); 208 if (cursor.moveToFirst()) { 209 version = cursor.getString(0); 210 } 211 } catch (Exception e) { 212 Log.e(TAG, "Cannot get build version from Index metadata"); 213 } finally { 214 if (cursor != null) { 215 cursor.close(); 216 } 217 } 218 return version; 219 } 220 221 public static void clearLocalesIndexed(Context context) { 222 context.getSharedPreferences(INDEX, 0).edit().clear().commit(); 223 } 224 225 public static void setLocaleIndexed(Context context, String locale) { 226 context.getSharedPreferences(INDEX, 0).edit().putBoolean(locale, true).commit(); 227 } 228 229 public static boolean isLocaleAlreadyIndexed(Context context, String locale) { 230 return context.getSharedPreferences(INDEX, 0).getBoolean(locale, false); 231 } 232 233 private void dropTables(SQLiteDatabase db) { 234 clearLocalesIndexed(mContext); 235 db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX); 236 db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX); 237 db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SAVED_QUERIES); 238 } 239 } 240