1 /*
2  * Copyright (C) 2019 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.car.developeroptions.homepage.contextualcards;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.database.sqlite.SQLiteDatabase;
23 import android.database.sqlite.SQLiteOpenHelper;
24 import android.util.Log;
25 
26 import androidx.annotation.VisibleForTesting;
27 
28 /**
29  * Defines the schema for the Homepage Cards database.
30  */
31 public class CardDatabaseHelper extends SQLiteOpenHelper {
32     private static final String TAG = "CardDatabaseHelper";
33     private static final String DATABASE_NAME = "homepage_cards.db";
34     private static final int DATABASE_VERSION = 5;
35 
36     public static final String CARD_TABLE = "cards";
37 
38     public interface CardColumns {
39         /**
40          * Primary key. Name of the card.
41          */
42         String NAME = "name";
43 
44         /**
45          * Type of the card.
46          */
47         String TYPE = "type";
48 
49         /**
50          * Score of the card. Higher numbers have higher priorities.
51          */
52         String SCORE = "score";
53 
54         /**
55          * URI of the slice card.
56          */
57         String SLICE_URI = "slice_uri";
58 
59         /**
60          * Category of the card.
61          */
62         String CATEGORY = "category";
63 
64         /**
65          * Keep the card last display's locale.
66          */
67         String LOCALIZED_TO_LOCALE = "localized_to_locale";
68 
69         /**
70          * Package name for all card candidates.
71          */
72         String PACKAGE_NAME = "package_name";
73 
74         /**
75          * Application version of the package.
76          */
77         String APP_VERSION = "app_version";
78 
79         /**
80          * Title resource name of the package.
81          */
82         String TITLE_RES_NAME = "title_res_name";
83 
84         /**
85          * Title of the package to be shown.
86          */
87         String TITLE_TEXT = "title_text";
88 
89         /**
90          * Summary resource name of the package.
91          */
92         String SUMMARY_RES_NAME = "summary_res_name";
93 
94         /**
95          * Summary of the package to be shown.
96          */
97         String SUMMARY_TEXT = "summary_text";
98 
99         /**
100          * Icon resource name of the package.
101          */
102         String ICON_RES_NAME = "icon_res_name";
103 
104         /**
105          * Icon resource id of the package.
106          */
107         String ICON_RES_ID = "icon_res_id";
108 
109         /**
110          * Key value mapping to Intent in Settings. Do action when user presses card.
111          */
112         String CARD_ACTION = "card_action";
113 
114         /**
115          * Expire time of the card. The unit of the value is mini-second.
116          */
117         String EXPIRE_TIME_MS = "expire_time_ms";
118 
119         /**
120          * Decide the card display full-length width or half-width in screen.
121          */
122         String SUPPORT_HALF_WIDTH = "support_half_width";
123 
124         /**
125          * Decide the card is dismissed or not.
126          */
127         String CARD_DISMISSED = "card_dismissed";
128     }
129 
130     private static final String CREATE_CARD_TABLE =
131             "CREATE TABLE " + CARD_TABLE +
132                     "(" +
133                     CardColumns.NAME +
134                     " TEXT NOT NULL PRIMARY KEY, " +
135                     CardColumns.TYPE +
136                     " INTEGER NOT NULL, " +
137                     CardColumns.SCORE +
138                     " DOUBLE NOT NULL, " +
139                     CardColumns.SLICE_URI +
140                     " TEXT, " +
141                     CardColumns.CATEGORY +
142                     " INTEGER DEFAULT 0, " +
143                     CardColumns.LOCALIZED_TO_LOCALE +
144                     " TEXT, " +
145                     CardColumns.PACKAGE_NAME +
146                     " TEXT NOT NULL, " +
147                     CardColumns.APP_VERSION +
148                     " INTEGER NOT NULL, " +
149                     CardColumns.TITLE_RES_NAME +
150                     " TEXT, " +
151                     CardColumns.TITLE_TEXT +
152                     " TEXT, " +
153                     CardColumns.SUMMARY_RES_NAME +
154                     " TEXT, " +
155                     CardColumns.SUMMARY_TEXT +
156                     " TEXT, " +
157                     CardColumns.ICON_RES_NAME +
158                     " TEXT, " +
159                     CardColumns.ICON_RES_ID +
160                     " INTEGER DEFAULT 0, " +
161                     CardColumns.CARD_ACTION +
162                     " INTEGER, " +
163                     CardColumns.EXPIRE_TIME_MS +
164                     " INTEGER, " +
165                     CardColumns.SUPPORT_HALF_WIDTH +
166                     " INTEGER DEFAULT 0, " +
167                     CardColumns.CARD_DISMISSED +
168                     " INTEGER DEFAULT 0 " +
169                     ");";
170 
CardDatabaseHelper(Context context)171     public CardDatabaseHelper(Context context) {
172         super(context, DATABASE_NAME, null, DATABASE_VERSION);
173     }
174 
175     @Override
onCreate(SQLiteDatabase db)176     public void onCreate(SQLiteDatabase db) {
177         db.execSQL(CREATE_CARD_TABLE);
178     }
179 
180     @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)181     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
182         if (oldVersion < newVersion) {
183             Log.d(TAG, "Reconstructing DB from " + oldVersion + " to " + newVersion);
184             db.execSQL("DROP TABLE IF EXISTS " + CARD_TABLE);
185             onCreate(db);
186         }
187     }
188 
189     @VisibleForTesting
190     static CardDatabaseHelper sCardDatabaseHelper;
191 
getInstance(Context context)192     public static synchronized CardDatabaseHelper getInstance(Context context) {
193         if (sCardDatabaseHelper == null) {
194             sCardDatabaseHelper = new CardDatabaseHelper(context.getApplicationContext());
195         }
196         return sCardDatabaseHelper;
197     }
198 
getContextualCards()199     Cursor getContextualCards() {
200         final SQLiteDatabase db = getReadableDatabase();
201         final String selection = CardColumns.CARD_DISMISSED + "=0";
202         return db.query(CARD_TABLE, null /* columns */, selection,
203                 null /* selectionArgs */, null /* groupBy */, null /* having */,
204                 CardColumns.SCORE + " DESC" /* orderBy */);
205     }
206 
207     /**
208      * Mark a specific ContextualCard with dismissal flag in the database to indicate that the
209      * card has been dismissed.
210      *
211      * @param context  Context
212      * @param cardName The card name of the ContextualCard which is dismissed by user.
213      * @return The number of rows updated
214      */
markContextualCardAsDismissed(Context context, String cardName)215     public int markContextualCardAsDismissed(Context context, String cardName) {
216         final SQLiteDatabase database = getWritableDatabase();
217         final ContentValues values = new ContentValues();
218         values.put(CardColumns.CARD_DISMISSED, 1);
219         final String selection = CardColumns.NAME + "=?";
220         final String[] selectionArgs = {cardName};
221         final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs);
222         database.close();
223         context.getContentResolver().notifyChange(CardContentProvider.DELETE_CARD_URI, null);
224         return rowsUpdated;
225     }
226 }
227