• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.slices;
18 
19 import android.content.Context;
20 import android.database.sqlite.SQLiteDatabase;
21 import android.database.sqlite.SQLiteOpenHelper;
22 import android.os.Build;
23 import android.util.Log;
24 
25 import androidx.annotation.VisibleForTesting;
26 
27 import java.util.Locale;
28 
29 /**
30  * Defines the schema for the Slices database.
31  */
32 public class SlicesDatabaseHelper extends SQLiteOpenHelper {
33 
34     private static final String TAG = "SlicesDatabaseHelper";
35 
36     private static final String DATABASE_NAME = "slices_index.db";
37     private static final String SHARED_PREFS_TAG = "slices_shared_prefs";
38 
39     private static final int DATABASE_VERSION = 8;
40 
41     public interface Tables {
42         String TABLE_SLICES_INDEX = "slices_index";
43     }
44 
45     public interface IndexColumns {
46         /**
47          * Primary key of the DB. Preference key from preference controllers.
48          */
49         String KEY = "key";
50 
51         /**
52          * Title of the Setting.
53          */
54         String TITLE = "title";
55 
56         /**
57          * Summary / Subtitle for the setting.
58          */
59         String SUMMARY = "summary";
60 
61         /**
62          * Title of the Setting screen on which the Setting lives.
63          */
64         String SCREENTITLE = "screentitle";
65 
66         /**
67          * String with a comma separated list of keywords relating to the Slice.
68          */
69         String KEYWORDS = "keywords";
70 
71         /**
72          * Resource ID for the icon of the setting. Should be 0 for no icon.
73          */
74         String ICON_RESOURCE = "icon";
75 
76         /**
77          * Classname of the fragment name of the page that hosts the setting.
78          */
79         String FRAGMENT = "fragment";
80 
81         /**
82          * Class name of the controller backing the setting. Must be a
83          * {@link com.android.settings.core.BasePreferenceController}.
84          */
85         String CONTROLLER = "controller";
86 
87         /**
88          * {@link SliceData.SliceType} representing the inline type of the result.
89          */
90         String SLICE_TYPE = "slice_type";
91 
92         /**
93          * Customized subtitle if it's a unavailable slice
94          */
95         String UNAVAILABLE_SLICE_SUBTITLE = "unavailable_slice_subtitle";
96 
97         /**
98          * The uri of slice.
99          */
100         String SLICE_URI = "slice_uri";
101 
102         /**
103          * Whether the slice should be exposed publicly.
104          */
105         String PUBLIC_SLICE = "public_slice";
106     }
107 
108     private static final String CREATE_SLICES_TABLE =
109             "CREATE VIRTUAL TABLE " + Tables.TABLE_SLICES_INDEX + " USING fts4" +
110                     "(" +
111                     IndexColumns.KEY +
112                     ", " +
113                     IndexColumns.SLICE_URI +
114                     ", " +
115                     IndexColumns.TITLE +
116                     ", " +
117                     IndexColumns.SUMMARY +
118                     ", " +
119                     IndexColumns.SCREENTITLE +
120                     ", " +
121                     IndexColumns.KEYWORDS +
122                     ", " +
123                     IndexColumns.ICON_RESOURCE +
124                     ", " +
125                     IndexColumns.FRAGMENT +
126                     ", " +
127                     IndexColumns.CONTROLLER +
128                     ", " +
129                     IndexColumns.SLICE_TYPE +
130                     ", " +
131                     IndexColumns.UNAVAILABLE_SLICE_SUBTITLE +
132                     ", "
133                     +
134                     IndexColumns.PUBLIC_SLICE
135                     +
136                     " INTEGER DEFAULT 0 "
137                     +
138                     ");";
139 
140     private final Context mContext;
141 
142     private static SlicesDatabaseHelper sSingleton;
143 
getInstance(Context context)144     public static synchronized SlicesDatabaseHelper getInstance(Context context) {
145         if (sSingleton == null) {
146             sSingleton = new SlicesDatabaseHelper(context.getApplicationContext());
147         }
148         return sSingleton;
149     }
150 
SlicesDatabaseHelper(Context context)151     private SlicesDatabaseHelper(Context context) {
152         super(context, DATABASE_NAME, null /* CursorFactor */, DATABASE_VERSION);
153         mContext = context;
154     }
155 
156     @Override
onCreate(SQLiteDatabase db)157     public void onCreate(SQLiteDatabase db) {
158         createDatabases(db);
159     }
160 
161     @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)162     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
163         if (oldVersion < DATABASE_VERSION) {
164             Log.d(TAG, "Reconstructing DB from " + oldVersion + " to " + newVersion);
165             reconstruct(db);
166         }
167     }
168 
169     /**
170      * Drops the currently stored databases rebuilds them.
171      * Also un-marks the state of the data such that any subsequent call to
172      * {@link#isNewIndexingState(Context)} will return {@code true}.
173      */
reconstruct(SQLiteDatabase db)174     void reconstruct(SQLiteDatabase db) {
175         mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
176                 .edit()
177                 .clear()
178                 .apply();
179         dropTables(db);
180         createDatabases(db);
181     }
182 
183     /**
184      * Marks the current state of the device for the validity of the data. Should be called after
185      * a full index of the TABLE_SLICES_INDEX.
186      */
setIndexedState()187     public void setIndexedState() {
188         setBuildIndexed();
189         setLocaleIndexed();
190     }
191 
192     /**
193      * Indicates if the indexed slice data reflects the current state of the phone.
194      *
195      * @return {@code true} if database should be rebuilt, {@code false} otherwise.
196      */
isSliceDataIndexed()197     public boolean isSliceDataIndexed() {
198         return isBuildIndexed() && isLocaleIndexed();
199     }
200 
createDatabases(SQLiteDatabase db)201     private void createDatabases(SQLiteDatabase db) {
202         db.execSQL(CREATE_SLICES_TABLE);
203         Log.d(TAG, "Created databases");
204     }
205 
dropTables(SQLiteDatabase db)206     private void dropTables(SQLiteDatabase db) {
207         db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SLICES_INDEX);
208     }
209 
setBuildIndexed()210     private void setBuildIndexed() {
211         mContext.getSharedPreferences(SHARED_PREFS_TAG, 0 /* mode */)
212                 .edit()
213                 .putBoolean(getBuildTag(), true /* value */)
214                 .apply();
215     }
216 
setLocaleIndexed()217     private void setLocaleIndexed() {
218         mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
219                 .edit()
220                 .putBoolean(Locale.getDefault().toString(), true /* value */)
221                 .apply();
222     }
223 
isBuildIndexed()224     private boolean isBuildIndexed() {
225         return mContext.getSharedPreferences(SHARED_PREFS_TAG,
226                 Context.MODE_PRIVATE)
227                 .getBoolean(getBuildTag(), false /* default */);
228     }
229 
isLocaleIndexed()230     private boolean isLocaleIndexed() {
231         return mContext.getSharedPreferences(SHARED_PREFS_TAG,
232                 Context.MODE_PRIVATE)
233                 .getBoolean(Locale.getDefault().toString(), false /* default */);
234     }
235 
236     @VisibleForTesting
getBuildTag()237     String getBuildTag() {
238         return Build.FINGERPRINT;
239     }
240 }