• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.alarms
18 
19 import android.content.ContentResolver
20 import android.content.Context
21 import android.os.AsyncTask
22 import android.text.format.DateFormat
23 import android.view.ViewGroup
24 
25 import com.android.deskclock.AlarmUtils
26 import com.android.deskclock.R
27 import com.android.deskclock.events.Events
28 import com.android.deskclock.provider.Alarm
29 import com.android.deskclock.provider.AlarmInstance
30 import com.android.deskclock.widget.toast.SnackbarManager
31 
32 import com.google.android.material.snackbar.Snackbar
33 
34 import java.util.Calendar
35 
36 /**
37  * API for asynchronously mutating a single alarm.
38  */
39 // TODO(b/165664115) Replace deprecated AsyncTask calls
40 class AlarmUpdateHandler(
41     context: Context,
42     private val mScrollHandler: ScrollHandler?,
43     private val mSnackbarAnchor: ViewGroup?
44 ) {
45 
46     private val mAppContext: Context = context.getApplicationContext()
47 
48     // For undo
49     private var mDeletedAlarm: Alarm? = null
50 
51     /**
52      * Adds a new alarm on the background.
53      *
54      * @param alarm The alarm to be added.
55      */
56     fun asyncAddAlarm(alarm: Alarm?) {
57         val updateTask: AsyncTask<Void, Void, AlarmInstance> =
58                 object : AsyncTask<Void, Void, AlarmInstance>() {
59             override fun doInBackground(vararg parameters: Void): AlarmInstance? {
60                 if (alarm != null) {
61                     Events.sendAlarmEvent(R.string.action_create, R.string.label_deskclock)
62                     val cr: ContentResolver = mAppContext.getContentResolver()
63 
64                     // Add alarm to db
65                     val newAlarm = Alarm.addAlarm(cr, alarm)
66 
67                     // Be ready to scroll to this alarm on UI later.
68                     mScrollHandler?.setSmoothScrollStableId(newAlarm.id)
69 
70                     // Create and add instance to db
71                     if (newAlarm.enabled) {
72                         return setupAlarmInstance(newAlarm)
73                     }
74                 }
75                 return null
76             }
77 
78             override fun onPostExecute(instance: AlarmInstance?) {
79                 if (instance != null) {
80                     AlarmUtils.popAlarmSetSnackbar(mSnackbarAnchor!!,
81                             instance.alarmTime.timeInMillis)
82                 }
83             }
84         }
85         updateTask.execute()
86     }
87 
88     /**
89      * Modifies an alarm on the background, and optionally show a toast when done.
90      *
91      * @param alarm The alarm to be modified.
92      * @param popToast whether or not a toast should be displayed when done.
93      * @param minorUpdate if true, don't affect any currently snoozed instances.
94      */
95     fun asyncUpdateAlarm(
96         alarm: Alarm,
97         popToast: Boolean,
98         minorUpdate: Boolean
99     ) {
100         val updateTask: AsyncTask<Void, Void, AlarmInstance> =
101                 object : AsyncTask<Void, Void, AlarmInstance>() {
102             override fun doInBackground(vararg parameters: Void): AlarmInstance? {
103                 val cr: ContentResolver = mAppContext.getContentResolver()
104 
105                 // Update alarm
106                 Alarm.updateAlarm(cr, alarm)
107                 if (minorUpdate) {
108                     // just update the instance in the database and update notifications.
109                     val instanceList = AlarmInstance.getInstancesByAlarmId(cr, alarm.id)
110                     for (instance in instanceList) {
111                         // Make a copy of the existing instance
112                         val newInstance = AlarmInstance(instance)
113                         // Copy over minor change data to the instance; we don't know
114                         // exactly which minor field changed, so just copy them all.
115                         newInstance.mVibrate = alarm.vibrate
116                         newInstance.mRingtone = alarm.alert
117                         newInstance.mLabel = alarm.label
118                         // Since we copied the mId of the old instance and the mId is used
119                         // as the primary key in the AlarmInstance table, this will replace
120                         // the existing instance.
121                         AlarmInstance.updateInstance(cr, newInstance)
122                         // Update the notification for this instance.
123                         AlarmNotifications.updateNotification(mAppContext, newInstance)
124                     }
125                     return null
126                 }
127                 // Otherwise, this is a major update and we're going to re-create the alarm
128                 AlarmStateManager.deleteAllInstances(mAppContext, alarm.id)
129 
130                 return if (alarm.enabled) setupAlarmInstance(alarm) else null
131             }
132 
133             override fun onPostExecute(instance: AlarmInstance?) {
134                 if (popToast && instance != null) {
135                     AlarmUtils.popAlarmSetSnackbar(
136                             mSnackbarAnchor!!, instance.alarmTime.timeInMillis)
137                 }
138             }
139         }
140         updateTask.execute()
141     }
142 
143     /**
144      * Deletes an alarm on the background.
145      *
146      * @param alarm The alarm to be deleted.
147      */
148     fun asyncDeleteAlarm(alarm: Alarm?) {
149         val deleteTask: AsyncTask<Void, Void, Boolean> = object : AsyncTask<Void, Void, Boolean>() {
150             override fun doInBackground(vararg parameters: Void): Boolean {
151                 // Activity may be closed at this point , make sure data is still valid
152                 if (alarm == null) {
153                     // Nothing to do here, just return.
154                     return false
155                 }
156                 AlarmStateManager.deleteAllInstances(mAppContext, alarm.id)
157                 return Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id)
158             }
159 
160             override fun onPostExecute(deleted: Boolean) {
161                 if (deleted) {
162                     mDeletedAlarm = alarm
163                     showUndoBar()
164                 }
165             }
166         }
167         deleteTask.execute()
168     }
169 
170     /**
171      * Show a toast when an alarm is predismissed.
172      *
173      * @param instance Instance being predismissed.
174      */
175     fun showPredismissToast(instance: AlarmInstance) {
176         val time: String = DateFormat.getTimeFormat(mAppContext).format(instance.alarmTime.time)
177         val text: String = mAppContext.getString(R.string.alarm_is_dismissed, time)
178         SnackbarManager.show(Snackbar.make(mSnackbarAnchor!!, text, Snackbar.LENGTH_SHORT))
179     }
180 
181     /**
182      * Hides any undo toast.
183      */
184     fun hideUndoBar() {
185         mDeletedAlarm = null
186         SnackbarManager.dismiss()
187     }
188 
189     private fun showUndoBar() {
190         val deletedAlarm = mDeletedAlarm
191         val snackbar: Snackbar = Snackbar.make(mSnackbarAnchor!!,
192                 mAppContext.getString(R.string.alarm_deleted), Snackbar.LENGTH_LONG)
193                 .setAction(R.string.alarm_undo, { _ ->
194                     mDeletedAlarm = null
195                     asyncAddAlarm(deletedAlarm)
196                 })
197         SnackbarManager.show(snackbar)
198     }
199 
200     private fun setupAlarmInstance(alarm: Alarm): AlarmInstance {
201         val cr: ContentResolver = mAppContext.getContentResolver()
202         var newInstance = alarm.createInstanceAfter(Calendar.getInstance())
203         newInstance = AlarmInstance.addInstance(cr, newInstance)
204         // Register instance to state manager
205         AlarmStateManager.registerInstance(mAppContext, newInstance, true)
206         return newInstance
207     }
208 }