Home | History | Annotate | Download | only in provider
      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