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