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