1 /* 2 * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps; 18 19 import android.content.ComponentName; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.database.sqlite.SQLiteCursor; 24 import android.database.sqlite.SQLiteCursorDriver; 25 import android.database.sqlite.SQLiteDatabase; 26 import android.database.sqlite.SQLiteOpenHelper; 27 import android.database.sqlite.SQLiteQuery; 28 import android.os.Trace; 29 import androidx.annotation.Nullable; 30 import androidx.annotation.WorkerThread; 31 import android.util.ArrayMap; 32 33 import java.util.Date; 34 import java.util.Map; 35 36 @WorkerThread 37 public class LaunchItemsDbHelper extends SQLiteOpenHelper { 38 39 // Increment whenever schema changes 40 private static final int DATABASE_VERSION = 1; 41 // Database name 42 private static final String DATABASE_NAME = "launch_items"; 43 44 private static final String CREATE_APP_TABLE = 45 "CREATE TABLE IF NOT EXISTS " + AppDbItem.TABLE_NAME + "(" 46 + AppDbItem.COLUMN_COMPONENT + " TEXT NOT NULL PRIMARY KEY , " 47 + AppDbItem.COLUMN_ORDER_PRIORITY + " INTEGER NOT NULL DEFAULT 0 , " 48 + AppDbItem.COLUMN_LAST_OPEN + " INTEGER " 49 + " )"; 50 LaunchItemsDbHelper(Context context)51 public LaunchItemsDbHelper(Context context) { 52 super(context, DATABASE_NAME, new SQLiteDatabase.CursorFactory() { 53 @Override 54 public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, 55 String editTable, SQLiteQuery query) { 56 return new SQLiteCursor(masterQuery, editTable, query); 57 } 58 }, DATABASE_VERSION); 59 } 60 61 @Override onCreate(SQLiteDatabase db)62 public void onCreate(SQLiteDatabase db) { 63 db.execSQL(CREATE_APP_TABLE); 64 } 65 66 @Override onConfigure(SQLiteDatabase db)67 public void onConfigure(SQLiteDatabase db) { 68 super.onConfigure(db); 69 setWriteAheadLoggingEnabled(true); 70 } 71 72 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)73 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 74 // DO NOT USE CONSTANTS FOR DB UPGRADE STEPS, USE ONLY LITERAL SQL STRINGS! 75 } 76 77 @Override getWritableDatabase()78 public SQLiteDatabase getWritableDatabase() { 79 Trace.beginSection("getWritableDatabase"); 80 try { 81 return super.getWritableDatabase(); 82 } finally { 83 Trace.endSection(); 84 } 85 } 86 87 @Override getReadableDatabase()88 public SQLiteDatabase getReadableDatabase() { 89 Trace.beginSection("getReadableDatabase"); 90 try { 91 return super.getReadableDatabase(); 92 } finally { 93 Trace.endSection(); 94 } 95 } 96 97 @Nullable readLastOpens()98 public Map<ComponentName, Date> readLastOpens() { 99 Trace.beginSection("readLastOpens"); 100 try { 101 final SQLiteDatabase db = getReadableDatabase(); 102 103 try (Cursor c = db.query( 104 AppDbItem.TABLE_NAME, 105 new String[] {AppDbItem.COLUMN_COMPONENT, AppDbItem.COLUMN_LAST_OPEN}, 106 null, null, null, null, null, null)) { 107 108 final int componentIndex = c.getColumnIndex(AppDbItem.COLUMN_COMPONENT); 109 final int lastOpenIndex = c.getColumnIndex(AppDbItem.COLUMN_LAST_OPEN); 110 111 final Map<ComponentName, Date> map = new ArrayMap<>(c.getCount()); 112 while (c.moveToNext()) { 113 final ComponentName componentName = 114 ComponentName.unflattenFromString(c.getString(componentIndex)); 115 if (c.isNull(lastOpenIndex)) { 116 map.put(componentName, null); 117 } else { 118 map.put(componentName, new Date(c.getLong(lastOpenIndex))); 119 } 120 } 121 return map; 122 123 } 124 } finally { 125 Trace.endSection(); 126 } 127 } 128 writeLastOpen(ComponentName componentName, Date lastOpen)129 public void writeLastOpen(ComponentName componentName, Date lastOpen) { 130 Trace.beginSection("writeLastOpen"); 131 try { 132 final SQLiteDatabase db = getWritableDatabase(); 133 134 db.beginTransactionNonExclusive(); 135 try { 136 final ContentValues contentValues = new ContentValues(2); 137 contentValues.put(AppDbItem.COLUMN_LAST_OPEN, lastOpen.getTime()); 138 139 final int cnt = db.update( 140 AppDbItem.TABLE_NAME, 141 contentValues, 142 AppDbItem.COLUMN_COMPONENT + "=?", 143 new String[]{componentName.flattenToString()}); 144 145 if (cnt < 1) { 146 contentValues.put(AppDbItem.COLUMN_COMPONENT, componentName.flattenToString()); 147 db.insert(AppDbItem.TABLE_NAME, AppDbItem.COLUMN_COMPONENT, contentValues); 148 } 149 db.setTransactionSuccessful(); 150 } finally { 151 db.endTransaction(); 152 } 153 } finally { 154 Trace.endSection(); 155 } 156 } 157 readOrderPriorities()158 public Map<ComponentName, Long> readOrderPriorities() { 159 Trace.beginSection("readOrderPriorities"); 160 try { 161 final SQLiteDatabase db = getReadableDatabase(); 162 163 try (Cursor c = db.query( 164 AppDbItem.TABLE_NAME, 165 new String[] {AppDbItem.COLUMN_COMPONENT, AppDbItem.COLUMN_ORDER_PRIORITY}, 166 null, null, null, null, null, null)) { 167 168 final int componentIndex = c.getColumnIndex(AppDbItem.COLUMN_COMPONENT); 169 final int priorityIndex = c.getColumnIndex(AppDbItem.COLUMN_ORDER_PRIORITY); 170 171 final Map<ComponentName, Long> map = new ArrayMap<>(c.getCount()); 172 while (c.moveToNext()) { 173 final ComponentName componentName = 174 ComponentName.unflattenFromString(c.getString(componentIndex)); 175 map.put(componentName, c.getLong(priorityIndex)); 176 } 177 return map; 178 } 179 } finally { 180 Trace.endSection(); 181 } 182 } 183 184 /** 185 * Writes the order priority field, and does not touch other items' priorities 186 * @param componentName {@link ComponentName} of item to update 187 * @param priority New priority 188 */ writeOrderPriority(ComponentName componentName, long priority)189 public void writeOrderPriority(ComponentName componentName, long priority) { 190 Trace.beginSection("writeOrderPriority"); 191 try { 192 final SQLiteDatabase db = getWritableDatabase(); 193 194 db.beginTransactionNonExclusive(); 195 try { 196 final ContentValues contentValues = new ContentValues(2); 197 contentValues.put(AppDbItem.COLUMN_ORDER_PRIORITY, priority); 198 199 final int cnt = db.update( 200 AppDbItem.TABLE_NAME, 201 contentValues, 202 AppDbItem.COLUMN_COMPONENT + "=?", 203 new String[]{componentName.flattenToString()}); 204 205 if (cnt < 1) { 206 contentValues.put(AppDbItem.COLUMN_COMPONENT, componentName.flattenToString()); 207 db.insert(AppDbItem.TABLE_NAME, AppDbItem.COLUMN_COMPONENT, contentValues); 208 } 209 db.setTransactionSuccessful(); 210 } finally { 211 db.endTransaction(); 212 } 213 } finally { 214 Trace.endSection(); 215 } 216 } 217 218 /** 219 * Writes the order priority field, and increments all priorities equal or greater if the new 220 * priority is greater than 0 221 * @param componentName {@link ComponentName} of item to update 222 * @param priority New priority 223 */ writeOrderPriorityAndShift(ComponentName componentName, int priority)224 public void writeOrderPriorityAndShift(ComponentName componentName, int priority) { 225 Trace.beginSection("writeOrderPriorityAndShift"); 226 try { 227 final SQLiteDatabase db = getWritableDatabase(); 228 229 db.beginTransactionNonExclusive(); 230 try { 231 if (priority > 0) { 232 db.execSQL("UPDATE " + AppDbItem.TABLE_NAME 233 + " SET " + AppDbItem.COLUMN_ORDER_PRIORITY + "=" 234 + AppDbItem.COLUMN_ORDER_PRIORITY + "+1" 235 + " WHERE " + AppDbItem.COLUMN_ORDER_PRIORITY + ">=?", 236 new String[] {Integer.toString(priority)}); 237 } 238 239 writeOrderPriority(componentName, priority); 240 241 db.setTransactionSuccessful(); 242 } finally { 243 db.endTransaction(); 244 } 245 } finally { 246 Trace.endSection(); 247 } 248 } 249 250 private static class AppDbItem { 251 public static final String TABLE_NAME = "app_items"; 252 253 // Primary key 254 public static final String COLUMN_COMPONENT = "component"; 255 // Ordering priority: higher numbers are higher in list 256 public static final String COLUMN_ORDER_PRIORITY = "order_priority"; 257 // Recency date 258 public static final String COLUMN_LAST_OPEN = "last_open"; 259 } 260 } 261