1effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang/* 2effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Copyright (C) 2015 The Android Open Source Project 3effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * 4effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Licensed under the Apache License, Version 2.0 (the "License"); 5effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * you may not use this file except in compliance with the License. 6effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * You may obtain a copy of the License at 7effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * 8effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * http://www.apache.org/licenses/LICENSE-2.0 9effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * 10effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Unless required by applicable law or agreed to in writing, software 11effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * distributed under the License is distributed on an "AS IS" BASIS, 12effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * See the License for the specific language governing permissions and 14effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * limitations under the License. 15effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */ 16effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 17effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangpackage com.android.deskclock.alarms; 18effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 19effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.content.ContentResolver; 20effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.content.Context; 21effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.os.AsyncTask; 227c955e743119c9101b753d151a7a6deba227ddf9Fan Zhangimport android.support.design.widget.Snackbar; 23c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chinimport android.text.format.DateFormat; 24effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.view.View; 25effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.view.ViewGroup; 26effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 27effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.AlarmUtils; 28effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.R; 29effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.events.Events; 30effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.provider.Alarm; 31effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.provider.AlarmInstance; 327c955e743119c9101b753d151a7a6deba227ddf9Fan Zhangimport com.android.deskclock.widget.toast.SnackbarManager; 33effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 34effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport java.util.Calendar; 35595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chinimport java.util.List; 36effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 37effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang/** 38effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * API for asynchronously mutating a single alarm. 39effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */ 407c955e743119c9101b753d151a7a6deba227ddf9Fan Zhangpublic final class AlarmUpdateHandler { 41effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 42effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang private final Context mAppContext; 43effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang private final ScrollHandler mScrollHandler; 447c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang private final View mSnackbarAnchor; 45effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 46effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // For undo 47effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang private Alarm mDeletedAlarm; 48effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 497c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang public AlarmUpdateHandler(Context context, ScrollHandler scrollHandler, 507c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang ViewGroup snackbarAnchor) { 51effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang mAppContext = context.getApplicationContext(); 52effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang mScrollHandler = scrollHandler; 537c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang mSnackbarAnchor = snackbarAnchor; 54effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 55effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 56effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang /** 57effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Adds a new alarm on the background. 58effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * 59effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * @param alarm The alarm to be added. 60effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */ 61effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang public void asyncAddAlarm(final Alarm alarm) { 62effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang final AsyncTask<Void, Void, AlarmInstance> updateTask = 63effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang new AsyncTask<Void, Void, AlarmInstance>() { 64effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang @Override 65effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang protected AlarmInstance doInBackground(Void... parameters) { 66effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang if (alarm != null) { 67effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang Events.sendAlarmEvent(R.string.action_create, R.string.label_deskclock); 68effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang ContentResolver cr = mAppContext.getContentResolver(); 69effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 70effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Add alarm to db 71effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang Alarm newAlarm = Alarm.addAlarm(cr, alarm); 72effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 73effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Be ready to scroll to this alarm on UI later. 74effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang mScrollHandler.setSmoothScrollStableId(newAlarm.id); 75effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 76effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Create and add instance to db 77effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang if (newAlarm.enabled) { 78effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang return setupAlarmInstance(newAlarm); 79effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 80effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 81effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang return null; 82effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 83effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 84effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang @Override 85effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang protected void onPostExecute(AlarmInstance instance) { 86effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang if (instance != null) { 877c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang AlarmUtils.popAlarmSetSnackbar( 887c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis()); 89effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 90effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 91effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang }; 92effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang updateTask.execute(); 93effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 94effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 95effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang /** 96effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Modifies an alarm on the background, and optionally show a toast when done. 97effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * 98effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * @param alarm The alarm to be modified. 99effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * @param popToast whether or not a toast should be displayed when done. 100effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * @param minorUpdate if true, don't affect any currently snoozed instances. 101effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */ 102effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang public void asyncUpdateAlarm(final Alarm alarm, final boolean popToast, 103effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang final boolean minorUpdate) { 104effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang final AsyncTask<Void, Void, AlarmInstance> updateTask = 105effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang new AsyncTask<Void, Void, AlarmInstance>() { 106effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang @Override 107effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang protected AlarmInstance doInBackground(Void... parameters) { 108effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang Events.sendAlarmEvent(R.string.action_update, R.string.label_deskclock); 109effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang ContentResolver cr = mAppContext.getContentResolver(); 110effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 111effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Update alarm 112effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang Alarm.updateAlarm(cr, alarm); 113595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin 114595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin if (minorUpdate) { 115595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // just update the instance in the database and update notifications. 116595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin final List<AlarmInstance> instanceList = 117595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin AlarmInstance.getInstancesByAlarmId(cr, alarm.id); 118595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin for (AlarmInstance instance : instanceList) { 119595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // Make a copy of the existing instance 120595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin final AlarmInstance newInstance = new AlarmInstance(instance); 121595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // Copy over minor change data to the instance; we don't know 122595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // exactly which minor field changed, so just copy them all. 123595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin newInstance.mVibrate = alarm.vibrate; 124595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin newInstance.mRingtone = alarm.alert; 125595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin newInstance.mLabel = alarm.label; 126595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // Since we copied the mId of the old instance and the mId is used 127595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // as the primary key in the AlarmInstance table, this will replace 128595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // the existing instance. 129595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin AlarmInstance.updateInstance(cr, newInstance); 130595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // Update the notification for this instance. 131595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin AlarmNotifications.updateNotification(mAppContext, newInstance); 132595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin } 133595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin return null; 134effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 135595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin // Otherwise, this is a major update and we're going to re-create the alarm 136595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); 137effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 138595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin return alarm.enabled ? setupAlarmInstance(alarm) : null; 139effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 140effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 141effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang @Override 142effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang protected void onPostExecute(AlarmInstance instance) { 143effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang if (popToast && instance != null) { 1447c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang AlarmUtils.popAlarmSetSnackbar( 1457c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis()); 146effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 147effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 148effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang }; 149effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang updateTask.execute(); 150effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 151effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 152effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang /** 153effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Deletes an alarm on the background. 154effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * 155effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * @param alarm The alarm to be deleted. 156effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */ 157effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang public void asyncDeleteAlarm(final Alarm alarm) { 158effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang final AsyncTask<Void, Void, Boolean> deleteTask = new AsyncTask<Void, Void, Boolean>() { 159effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang @Override 160effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang protected Boolean doInBackground(Void... parameters) { 161effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Activity may be closed at this point , make sure data is still valid 162effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang if (alarm == null) { 163effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Nothing to do here, just return. 164effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang return false; 165effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 166effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang Events.sendAlarmEvent(R.string.action_delete, R.string.label_deskclock); 167effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); 168effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang return Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id); 169effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 170effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 171effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang @Override 172effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang protected void onPostExecute(Boolean deleted) { 173effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang if (deleted) { 174effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang mDeletedAlarm = alarm; 175effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang showUndoBar(); 176effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 177effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 178effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang }; 179effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang deleteTask.execute(); 180effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 181effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 182effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang /** 183c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin * Show a toast when an alarm is predismissed. 184c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin * 185c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin * @param instance Instance being predismissed. 186c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin */ 187c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin public void showPredismissToast(AlarmInstance instance) { 188c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin final String time = DateFormat.getTimeFormat(mAppContext).format( 189c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin instance.getAlarmTime().getTime()); 190c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin final String text = mAppContext.getString(R.string.alarm_is_dismissed, time); 1917c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang SnackbarManager.show(Snackbar.make(mSnackbarAnchor, text, Snackbar.LENGTH_SHORT)); 192c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin } 193c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin 194c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin /** 195effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Hides any undo toast. 196effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */ 1977c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang public void hideUndoBar() { 198effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang mDeletedAlarm = null; 1997c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang SnackbarManager.dismiss(); 200effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 201effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 202effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang private void showUndoBar() { 203effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang final Alarm deletedAlarm = mDeletedAlarm; 2047c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang final Snackbar snackbar = Snackbar.make(mSnackbarAnchor, 2057c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang mAppContext.getString(R.string.alarm_deleted), Snackbar.LENGTH_LONG) 2067c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang .setAction(R.string.alarm_undo, new View.OnClickListener() { 2077c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang @Override 2087c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang public void onClick(View v) { 2097c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang mDeletedAlarm = null; 2107c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang asyncAddAlarm(deletedAlarm); 2117c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang } 2127c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang }); 2137c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang SnackbarManager.show(snackbar); 214effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 215effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang 216effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang private AlarmInstance setupAlarmInstance(Alarm alarm) { 217effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang final ContentResolver cr = mAppContext.getContentResolver(); 218effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang AlarmInstance newInstance = alarm.createInstanceAfter(Calendar.getInstance()); 219effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang newInstance = AlarmInstance.addInstance(cr, newInstance); 220effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang // Register instance to state manager 221effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang AlarmStateManager.registerInstance(mAppContext, newInstance, true); 222effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang return newInstance; 223effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang } 224effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang} 225