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 }