1 /* 2 * Copyright (C) 2013 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.deskclock.provider; 18 19 import android.content.ContentResolver; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.database.SQLException; 24 import android.database.sqlite.SQLiteDatabase; 25 import android.database.sqlite.SQLiteOpenHelper; 26 import android.media.RingtoneManager; 27 import android.net.Uri; 28 import android.text.TextUtils; 29 30 import com.android.deskclock.Log; 31 import com.android.deskclock.alarms.AlarmStateManager; 32 33 import java.util.Calendar; 34 35 /** 36 * Helper class for opening the database from multiple providers. Also provides 37 * some common functionality. 38 */ 39 class ClockDatabaseHelper extends SQLiteOpenHelper { 40 /** 41 * Original Clock Database. 42 **/ 43 private static final int VERSION_5 = 5; 44 45 /** 46 * Introduce: 47 * Added alarm_instances table 48 * Added selected_cities table 49 * Added DELETE_AFTER_USE column to alarms table 50 */ 51 private static final int VERSION_6 = 6; 52 53 /** 54 * Added alarm settings to instance table. 55 */ 56 private static final int VERSION_7 = 7; 57 58 // This creates a default alarm at 8:30 for every Mon,Tue,Wed,Thu,Fri 59 private static final String DEFAULT_ALARM_1 = "(8, 30, 31, 0, 0, '', NULL, 0);"; 60 61 // This creates a default alarm at 9:30 for every Sat,Sun 62 private static final String DEFAULT_ALARM_2 = "(9, 00, 96, 0, 0, '', NULL, 0);"; 63 64 // Database and table names 65 static final String DATABASE_NAME = "alarms.db"; 66 static final String OLD_ALARMS_TABLE_NAME = "alarms"; 67 static final String ALARMS_TABLE_NAME = "alarm_templates"; 68 static final String INSTANCES_TABLE_NAME = "alarm_instances"; 69 static final String CITIES_TABLE_NAME = "selected_cities"; 70 71 private static void createAlarmsTable(SQLiteDatabase db) { 72 db.execSQL("CREATE TABLE " + ALARMS_TABLE_NAME + " (" + 73 ClockContract.AlarmsColumns._ID + " INTEGER PRIMARY KEY," + 74 ClockContract.AlarmsColumns.HOUR + " INTEGER NOT NULL, " + 75 ClockContract.AlarmsColumns.MINUTES + " INTEGER NOT NULL, " + 76 ClockContract.AlarmsColumns.DAYS_OF_WEEK + " INTEGER NOT NULL, " + 77 ClockContract.AlarmsColumns.ENABLED + " INTEGER NOT NULL, " + 78 ClockContract.AlarmsColumns.VIBRATE + " INTEGER NOT NULL, " + 79 ClockContract.AlarmsColumns.LABEL + " TEXT NOT NULL, " + 80 ClockContract.AlarmsColumns.RINGTONE + " TEXT, " + 81 ClockContract.AlarmsColumns.DELETE_AFTER_USE + " INTEGER NOT NULL DEFAULT 0);"); 82 Log.i("Alarms Table created"); 83 } 84 85 private static void createInstanceTable(SQLiteDatabase db) { 86 db.execSQL("CREATE TABLE " + INSTANCES_TABLE_NAME + " (" + 87 ClockContract.InstancesColumns._ID + " INTEGER PRIMARY KEY," + 88 ClockContract.InstancesColumns.YEAR + " INTEGER NOT NULL, " + 89 ClockContract.InstancesColumns.MONTH + " INTEGER NOT NULL, " + 90 ClockContract.InstancesColumns.DAY + " INTEGER NOT NULL, " + 91 ClockContract.InstancesColumns.HOUR + " INTEGER NOT NULL, " + 92 ClockContract.InstancesColumns.MINUTES + " INTEGER NOT NULL, " + 93 ClockContract.InstancesColumns.VIBRATE + " INTEGER NOT NULL, " + 94 ClockContract.InstancesColumns.LABEL + " TEXT NOT NULL, " + 95 ClockContract.InstancesColumns.RINGTONE + " TEXT, " + 96 ClockContract.InstancesColumns.ALARM_STATE + " INTEGER NOT NULL, " + 97 ClockContract.InstancesColumns.ALARM_ID + " INTEGER REFERENCES " + 98 ALARMS_TABLE_NAME + "(" + ClockContract.AlarmsColumns._ID + ") " + 99 "ON UPDATE CASCADE ON DELETE CASCADE" + 100 ");"); 101 Log.i("Instance table created"); 102 } 103 104 private static void createCitiesTable(SQLiteDatabase db) { 105 db.execSQL("CREATE TABLE " + CITIES_TABLE_NAME + " (" + 106 ClockContract.CitiesColumns.CITY_ID + " TEXT PRIMARY KEY," + 107 ClockContract.CitiesColumns.CITY_NAME + " TEXT NOT NULL, " + 108 ClockContract.CitiesColumns.TIMEZONE_NAME + " TEXT NOT NULL, " + 109 ClockContract.CitiesColumns.TIMEZONE_OFFSET + " INTEGER NOT NULL);"); 110 Log.i("Cities table created"); 111 } 112 113 private Context mContext; 114 115 public ClockDatabaseHelper(Context context) { 116 super(context, DATABASE_NAME, null, VERSION_7); 117 mContext = context; 118 } 119 120 @Override 121 public void onCreate(SQLiteDatabase db) { 122 createAlarmsTable(db); 123 createInstanceTable(db); 124 createCitiesTable(db); 125 126 // insert default alarms 127 Log.i("Inserting default alarms"); 128 String cs = ", "; //comma and space 129 String insertMe = "INSERT INTO " + ALARMS_TABLE_NAME + " (" + 130 ClockContract.AlarmsColumns.HOUR + cs + 131 ClockContract.AlarmsColumns.MINUTES + cs + 132 ClockContract.AlarmsColumns.DAYS_OF_WEEK + cs + 133 ClockContract.AlarmsColumns.ENABLED + cs + 134 ClockContract.AlarmsColumns.VIBRATE + cs + 135 ClockContract.AlarmsColumns.LABEL + cs + 136 ClockContract.AlarmsColumns.RINGTONE + cs + 137 ClockContract.AlarmsColumns.DELETE_AFTER_USE + ") VALUES "; 138 db.execSQL(insertMe + DEFAULT_ALARM_1); 139 db.execSQL(insertMe + DEFAULT_ALARM_2); 140 } 141 142 @Override 143 public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { 144 if (Log.LOGV) { 145 Log.v("Upgrading alarms database from version " + oldVersion + " to " + currentVersion); 146 } 147 148 if (oldVersion <= VERSION_6) { 149 // These were not used in DB_VERSION_6, so we can just drop them. 150 db.execSQL("DROP TABLE IF EXISTS " + INSTANCES_TABLE_NAME + ";"); 151 db.execSQL("DROP TABLE IF EXISTS " + CITIES_TABLE_NAME + ";"); 152 153 // Create new alarms table and copy over the data 154 createAlarmsTable(db); 155 createInstanceTable(db); 156 createCitiesTable(db); 157 158 Log.i("Copying old alarms to new table"); 159 String[] OLD_TABLE_COLUMNS = { 160 "_id", 161 "hour", 162 "minutes", 163 "daysofweek", 164 "enabled", 165 "vibrate", 166 "message", 167 "alert", 168 }; 169 Cursor cursor = db.query(OLD_ALARMS_TABLE_NAME, OLD_TABLE_COLUMNS, 170 null, null, null, null, null); 171 Calendar currentTime = Calendar.getInstance(); 172 while (cursor.moveToNext()) { 173 Alarm alarm = new Alarm(); 174 alarm.id = cursor.getLong(0); 175 alarm.hour = cursor.getInt(1); 176 alarm.minutes = cursor.getInt(2); 177 alarm.daysOfWeek = new DaysOfWeek(cursor.getInt(3)); 178 alarm.enabled = cursor.getInt(4) == 1; 179 alarm.vibrate = cursor.getInt(5) == 1; 180 alarm.label = cursor.getString(6); 181 182 String alertString = cursor.getString(7); 183 if ("silent".equals(alertString)) { 184 alarm.alert = Alarm.NO_RINGTONE_URI; 185 } else { 186 alarm.alert = TextUtils.isEmpty(alertString) ? null : Uri.parse(alertString); 187 } 188 189 // Save new version of alarm and create alarminstance for it 190 db.insert(ALARMS_TABLE_NAME, null, Alarm.createContentValues(alarm)); 191 if (alarm.enabled) { 192 AlarmInstance newInstance = alarm.createInstanceAfter(currentTime); 193 db.insert(INSTANCES_TABLE_NAME, null, 194 AlarmInstance.createContentValues(newInstance)); 195 } 196 } 197 cursor.close(); 198 199 Log.i("Dropping old alarm table"); 200 db.execSQL("DROP TABLE IF EXISTS " + OLD_ALARMS_TABLE_NAME + ";"); 201 } 202 } 203 204 long fixAlarmInsert(ContentValues values) { 205 // Why are we doing this? Is this not a programming bug if we try to 206 // insert an already used id? 207 SQLiteDatabase db = getWritableDatabase(); 208 db.beginTransaction(); 209 long rowId = -1; 210 try { 211 // Check if we are trying to re-use an existing id. 212 Object value = values.get(ClockContract.AlarmsColumns._ID); 213 if (value != null) { 214 long id = (Long) value; 215 if (id > -1) { 216 final Cursor cursor = db.query(ALARMS_TABLE_NAME, 217 new String[]{ClockContract.AlarmsColumns._ID}, 218 ClockContract.AlarmsColumns._ID + " = ?", 219 new String[]{id + ""}, null, null, null); 220 if (cursor.moveToFirst()) { 221 // Record exists. Remove the id so sqlite can generate a new one. 222 values.putNull(ClockContract.AlarmsColumns._ID); 223 } 224 } 225 } 226 227 rowId = db.insert(ALARMS_TABLE_NAME, ClockContract.AlarmsColumns.RINGTONE, values); 228 db.setTransactionSuccessful(); 229 } finally { 230 db.endTransaction(); 231 } 232 if (rowId < 0) { 233 throw new SQLException("Failed to insert row"); 234 } 235 if (Log.LOGV) Log.v("Added alarm rowId = " + rowId); 236 237 return rowId; 238 } 239 } 240