| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.settings.slices; |
| |
| import android.content.Context; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.database.sqlite.SQLiteOpenHelper; |
| import android.os.Build; |
| import android.util.Log; |
| |
| import androidx.annotation.VisibleForTesting; |
| |
| import java.util.Locale; |
| |
| /** |
| * Defines the schema for the Slices database. |
| */ |
| public class SlicesDatabaseHelper extends SQLiteOpenHelper { |
| |
| private static final String TAG = "SlicesDatabaseHelper"; |
| |
| private static final String DATABASE_NAME = "slices_index.db"; |
| private static final String SHARED_PREFS_TAG = "slices_shared_prefs"; |
| |
| private static final int DATABASE_VERSION = 10; |
| |
| public interface Tables { |
| String TABLE_SLICES_INDEX = "slices_index"; |
| } |
| |
| public interface IndexColumns { |
| /** |
| * Primary key of the DB. Preference key from preference controllers. |
| */ |
| String KEY = "key"; |
| |
| /** |
| * Title of the Setting. |
| */ |
| String TITLE = "title"; |
| |
| /** |
| * Summary / Subtitle for the setting. |
| */ |
| String SUMMARY = "summary"; |
| |
| /** |
| * Title of the Setting screen on which the Setting lives. |
| */ |
| String SCREENTITLE = "screentitle"; |
| |
| /** |
| * String with a comma separated list of keywords relating to the Slice. |
| */ |
| String KEYWORDS = "keywords"; |
| |
| /** |
| * Resource ID for the icon of the setting. Should be 0 for no icon. |
| */ |
| String ICON_RESOURCE = "icon"; |
| |
| /** |
| * Classname of the fragment name of the page that hosts the setting. |
| */ |
| String FRAGMENT = "fragment"; |
| |
| /** |
| * Class name of the controller backing the setting. Must be a |
| * {@link com.android.settings.core.BasePreferenceController}. |
| */ |
| String CONTROLLER = "controller"; |
| |
| /** |
| * {@link SliceData.SliceType} representing the inline type of the result. |
| */ |
| String SLICE_TYPE = "slice_type"; |
| |
| /** |
| * Customized subtitle if it's a unavailable slice |
| */ |
| String UNAVAILABLE_SLICE_SUBTITLE = "unavailable_slice_subtitle"; |
| |
| /** |
| * The uri of slice. |
| */ |
| String SLICE_URI = "slice_uri"; |
| |
| /** |
| * Whether the slice should be exposed publicly. |
| */ |
| String PUBLIC_SLICE = "public_slice"; |
| |
| /** |
| * Resource ID for the menu entry of the setting. |
| */ |
| String HIGHLIGHT_MENU_RESOURCE = "highlight_menu"; |
| |
| /** |
| * The name of user restriction for the setting. |
| */ |
| String USER_RESTRICTION = "user_restriction"; |
| } |
| |
| private static final String CREATE_SLICES_TABLE = |
| "CREATE VIRTUAL TABLE " + Tables.TABLE_SLICES_INDEX + " USING fts4" |
| + "(" |
| + IndexColumns.KEY |
| + ", " |
| + IndexColumns.SLICE_URI |
| + ", " |
| + IndexColumns.TITLE |
| + ", " |
| + IndexColumns.SUMMARY |
| + ", " |
| + IndexColumns.SCREENTITLE |
| + ", " |
| + IndexColumns.KEYWORDS |
| + ", " |
| + IndexColumns.ICON_RESOURCE |
| + ", " |
| + IndexColumns.FRAGMENT |
| + ", " |
| + IndexColumns.CONTROLLER |
| + ", " |
| + IndexColumns.SLICE_TYPE |
| + ", " |
| + IndexColumns.UNAVAILABLE_SLICE_SUBTITLE |
| + ", " |
| + IndexColumns.PUBLIC_SLICE |
| + ", " |
| + IndexColumns.HIGHLIGHT_MENU_RESOURCE |
| + ", " |
| + IndexColumns.USER_RESTRICTION |
| + " INTEGER DEFAULT 0 " |
| + ");"; |
| |
| private final Context mContext; |
| |
| private static SlicesDatabaseHelper sSingleton; |
| |
| public static synchronized SlicesDatabaseHelper getInstance(Context context) { |
| if (sSingleton == null) { |
| sSingleton = new SlicesDatabaseHelper(context.getApplicationContext()); |
| } |
| return sSingleton; |
| } |
| |
| private SlicesDatabaseHelper(Context context) { |
| super(context, DATABASE_NAME, null /* CursorFactor */, DATABASE_VERSION); |
| mContext = context; |
| } |
| |
| @Override |
| public void onCreate(SQLiteDatabase db) { |
| createDatabases(db); |
| } |
| |
| @Override |
| public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
| if (oldVersion < DATABASE_VERSION) { |
| Log.d(TAG, "Reconstructing DB from " + oldVersion + " to " + newVersion); |
| reconstruct(db); |
| } |
| } |
| |
| /** |
| * Drops the currently stored databases rebuilds them. |
| * Also un-marks the state of the data such that any subsequent call to |
| * {@link#isNewIndexingState(Context)} will return {@code true}. |
| */ |
| void reconstruct(SQLiteDatabase db) { |
| mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) |
| .edit() |
| .clear() |
| .apply(); |
| dropTables(db); |
| createDatabases(db); |
| } |
| |
| /** |
| * Marks the current state of the device for the validity of the data. Should be called after |
| * a full index of the TABLE_SLICES_INDEX. |
| */ |
| public void setIndexedState() { |
| setBuildIndexed(); |
| setLocaleIndexed(); |
| } |
| |
| /** |
| * Indicates if the indexed slice data reflects the current state of the phone. |
| * |
| * @return {@code true} if database should be rebuilt, {@code false} otherwise. |
| */ |
| public boolean isSliceDataIndexed() { |
| return isBuildIndexed() && isLocaleIndexed(); |
| } |
| |
| private void createDatabases(SQLiteDatabase db) { |
| db.execSQL(CREATE_SLICES_TABLE); |
| Log.d(TAG, "Created databases"); |
| } |
| |
| private void dropTables(SQLiteDatabase db) { |
| db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SLICES_INDEX); |
| } |
| |
| private void setBuildIndexed() { |
| mContext.getSharedPreferences(SHARED_PREFS_TAG, 0 /* mode */) |
| .edit() |
| .putBoolean(getBuildTag(), true /* value */) |
| .apply(); |
| } |
| |
| private void setLocaleIndexed() { |
| mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) |
| .edit() |
| .putBoolean(Locale.getDefault().toString(), true /* value */) |
| .apply(); |
| } |
| |
| private boolean isBuildIndexed() { |
| return mContext.getSharedPreferences(SHARED_PREFS_TAG, |
| Context.MODE_PRIVATE) |
| .getBoolean(getBuildTag(), false /* default */); |
| } |
| |
| private boolean isLocaleIndexed() { |
| return mContext.getSharedPreferences(SHARED_PREFS_TAG, |
| Context.MODE_PRIVATE) |
| .getBoolean(Locale.getDefault().toString(), false /* default */); |
| } |
| |
| @VisibleForTesting |
| String getBuildTag() { |
| return Build.FINGERPRINT; |
| } |
| } |