AlarmUpdateHandler.java revision 595e6b8c56a6810d0d3bd09669ae314fe0d5a902
1/* 2 * Copyright (C) 2015 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 17package com.android.deskclock.alarms; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.os.AsyncTask; 22import android.text.format.DateFormat; 23import android.view.MotionEvent; 24import android.view.View; 25import android.view.ViewGroup; 26import android.widget.Toast; 27 28import com.android.deskclock.AlarmUtils; 29import com.android.deskclock.R; 30import com.android.deskclock.ToastMaster; 31import com.android.deskclock.events.Events; 32import com.android.deskclock.provider.Alarm; 33import com.android.deskclock.provider.AlarmInstance; 34import com.android.deskclock.widget.ActionableToastBar; 35 36import java.util.Calendar; 37import java.util.List; 38 39/** 40 * API for asynchronously mutating a single alarm. 41 */ 42public final class AlarmUpdateHandler implements View.OnTouchListener { 43 44 private final Context mAppContext; 45 private final ScrollHandler mScrollHandler; 46 private final ViewGroup mUndoFrame; 47 48 // For undo 49 private Alarm mDeletedAlarm; 50 private ActionableToastBar mUndoBar; 51 52 public AlarmUpdateHandler(Context context, ScrollHandler scrollHandler, ViewGroup undoFrame) { 53 mAppContext = context.getApplicationContext(); 54 mScrollHandler = scrollHandler; 55 mUndoFrame = undoFrame; 56 mUndoFrame.setOnTouchListener(this); 57 mUndoBar = (ActionableToastBar) mUndoFrame.findViewById(R.id.undo_bar); 58 } 59 60 @Override 61 public boolean onTouch(View v, MotionEvent event) { 62 hideUndoBar(true, event); 63 return false; 64 } 65 66 /** 67 * Adds a new alarm on the background. 68 * 69 * @param alarm The alarm to be added. 70 */ 71 public void asyncAddAlarm(final Alarm alarm) { 72 final AsyncTask<Void, Void, AlarmInstance> updateTask = 73 new AsyncTask<Void, Void, AlarmInstance>() { 74 @Override 75 protected AlarmInstance doInBackground(Void... parameters) { 76 if (alarm != null) { 77 Events.sendAlarmEvent(R.string.action_create, R.string.label_deskclock); 78 ContentResolver cr = mAppContext.getContentResolver(); 79 80 // Add alarm to db 81 Alarm newAlarm = Alarm.addAlarm(cr, alarm); 82 83 // Be ready to scroll to this alarm on UI later. 84 mScrollHandler.setSmoothScrollStableId(newAlarm.id); 85 86 // Create and add instance to db 87 if (newAlarm.enabled) { 88 return setupAlarmInstance(newAlarm); 89 } 90 } 91 return null; 92 } 93 94 @Override 95 protected void onPostExecute(AlarmInstance instance) { 96 if (instance != null) { 97 AlarmUtils.popAlarmSetToast( 98 mAppContext, instance.getAlarmTime().getTimeInMillis()); 99 } 100 } 101 }; 102 updateTask.execute(); 103 } 104 105 /** 106 * Modifies an alarm on the background, and optionally show a toast when done. 107 * 108 * @param alarm The alarm to be modified. 109 * @param popToast whether or not a toast should be displayed when done. 110 * @param minorUpdate if true, don't affect any currently snoozed instances. 111 */ 112 public void asyncUpdateAlarm(final Alarm alarm, final boolean popToast, 113 final boolean minorUpdate) { 114 final AsyncTask<Void, Void, AlarmInstance> updateTask = 115 new AsyncTask<Void, Void, AlarmInstance>() { 116 @Override 117 protected AlarmInstance doInBackground(Void... parameters) { 118 Events.sendAlarmEvent(R.string.action_update, R.string.label_deskclock); 119 ContentResolver cr = mAppContext.getContentResolver(); 120 121 // Update alarm 122 Alarm.updateAlarm(cr, alarm); 123 124 if (minorUpdate) { 125 // just update the instance in the database and update notifications. 126 final List<AlarmInstance> instanceList = 127 AlarmInstance.getInstancesByAlarmId(cr, alarm.id); 128 for (AlarmInstance instance : instanceList) { 129 // Make a copy of the existing instance 130 final AlarmInstance newInstance = new AlarmInstance(instance); 131 // Copy over minor change data to the instance; we don't know 132 // exactly which minor field changed, so just copy them all. 133 newInstance.mVibrate = alarm.vibrate; 134 newInstance.mRingtone = alarm.alert; 135 newInstance.mLabel = alarm.label; 136 // Since we copied the mId of the old instance and the mId is used 137 // as the primary key in the AlarmInstance table, this will replace 138 // the existing instance. 139 AlarmInstance.updateInstance(cr, newInstance); 140 // Update the notification for this instance. 141 AlarmNotifications.updateNotification(mAppContext, newInstance); 142 } 143 return null; 144 } 145 // Otherwise, this is a major update and we're going to re-create the alarm 146 AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); 147 148 return alarm.enabled ? setupAlarmInstance(alarm) : null; 149 } 150 151 @Override 152 protected void onPostExecute(AlarmInstance instance) { 153 if (popToast && instance != null) { 154 AlarmUtils.popAlarmSetToast( 155 mAppContext, instance.getAlarmTime().getTimeInMillis()); 156 } 157 } 158 }; 159 updateTask.execute(); 160 } 161 162 /** 163 * Deletes an alarm on the background. 164 * 165 * @param alarm The alarm to be deleted. 166 */ 167 public void asyncDeleteAlarm(final Alarm alarm) { 168 final AsyncTask<Void, Void, Boolean> deleteTask = new AsyncTask<Void, Void, Boolean>() { 169 @Override 170 protected Boolean doInBackground(Void... parameters) { 171 // Activity may be closed at this point , make sure data is still valid 172 if (alarm == null) { 173 // Nothing to do here, just return. 174 return false; 175 } 176 Events.sendAlarmEvent(R.string.action_delete, R.string.label_deskclock); 177 AlarmStateManager.deleteAllInstances(mAppContext, alarm.id); 178 return Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id); 179 } 180 181 @Override 182 protected void onPostExecute(Boolean deleted) { 183 if (deleted) { 184 mDeletedAlarm = alarm; 185 showUndoBar(); 186 } 187 } 188 }; 189 deleteTask.execute(); 190 } 191 192 /** 193 * Show a toast when an alarm is predismissed. 194 * 195 * @param instance Instance being predismissed. 196 */ 197 public void showPredismissToast(AlarmInstance instance) { 198 final String time = DateFormat.getTimeFormat(mAppContext).format( 199 instance.getAlarmTime().getTime()); 200 final String text = mAppContext.getString(R.string.alarm_is_dismissed, time); 201 Toast toast = Toast.makeText(mAppContext, text, Toast.LENGTH_LONG); 202 ToastMaster.setToast(toast); 203 toast.show(); 204 } 205 206 /** 207 * Hides any undo toast. 208 */ 209 public void hideUndoBar(boolean animate, MotionEvent event) { 210 if (mUndoBar != null) { 211 mUndoFrame.setVisibility(View.GONE); 212 if (event != null && mUndoBar.isEventInToastBar(event)) { 213 // Avoid touches inside the undo bar. 214 return; 215 } 216 mUndoBar.hide(animate); 217 } 218 mDeletedAlarm = null; 219 } 220 221 private void showUndoBar() { 222 final Alarm deletedAlarm = mDeletedAlarm; 223 mUndoFrame.setVisibility(View.VISIBLE); 224 mUndoBar.show(new ActionableToastBar.ActionClickedListener() { 225 @Override 226 public void onActionClicked() { 227 mDeletedAlarm = null; 228 asyncAddAlarm(deletedAlarm); 229 } 230 }, 0, mAppContext.getString(R.string.alarm_deleted), 231 true, R.string.alarm_undo, true); 232 } 233 234 private AlarmInstance setupAlarmInstance(Alarm alarm) { 235 final ContentResolver cr = mAppContext.getContentResolver(); 236 AlarmInstance newInstance = alarm.createInstanceAfter(Calendar.getInstance()); 237 newInstance = AlarmInstance.addInstance(cr, newInstance); 238 // Register instance to state manager 239 AlarmStateManager.registerInstance(mAppContext, newInstance, true); 240 return newInstance; 241 } 242} 243