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