• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
createAlarmsTable(SQLiteDatabase db)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 
createInstanceTable(SQLiteDatabase db)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 
createCitiesTable(SQLiteDatabase db)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 
ClockDatabaseHelper(Context context)115     public ClockDatabaseHelper(Context context) {
116         super(context, DATABASE_NAME, null, VERSION_7);
117         mContext = context;
118     }
119 
120     @Override
onCreate(SQLiteDatabase db)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
onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion)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 
fixAlarmInsert(ContentValues values)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