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