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                         ContentResolver cr = mAppContext.getContentResolver();
    109 
    110                         // Update alarm
    111                         Alarm.updateAlarm(cr, alarm);
    112 
    113                         if (minorUpdate) {
    114                             // just update the instance in the database and update notifications.
    115                             final List<AlarmInstance> instanceList =
    116                                     AlarmInstance.getInstancesByAlarmId(cr, alarm.id);
    117                             for (AlarmInstance instance : instanceList) {
    118                                 // Make a copy of the existing instance
    119                                 final AlarmInstance newInstance = new AlarmInstance(instance);
    120                                 // Copy over minor change data to the instance; we don't know
    121                                 // exactly which minor field changed, so just copy them all.
    122                                 newInstance.mVibrate = alarm.vibrate;
    123                                 newInstance.mRingtone = alarm.alert;
    124                                 newInstance.mLabel = alarm.label;
    125                                 // Since we copied the mId of the old instance and the mId is used
    126                                 // as the primary key in the AlarmInstance table, this will replace
    127                                 // the existing instance.
    128                                 AlarmInstance.updateInstance(cr, newInstance);
    129                                 // Update the notification for this instance.
    130                                 AlarmNotifications.updateNotification(mAppContext, newInstance);
    131                             }
    132                             return null;
    133                         }
    134                         // Otherwise, this is a major update and we're going to re-create the alarm
    135                         AlarmStateManager.deleteAllInstances(mAppContext, alarm.id);
    136 
    137                         return alarm.enabled ? setupAlarmInstance(alarm) : null;
    138                     }
    139 
    140                     @Override
    141                     protected void onPostExecute(AlarmInstance instance) {
    142                         if (popToast && instance != null) {
    143                             AlarmUtils.popAlarmSetSnackbar(
    144                                     mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis());
    145                         }
    146                     }
    147                 };
    148         updateTask.execute();
    149     }
    150 
    151     /**
    152      * Deletes an alarm on the background.
    153      *
    154      * @param alarm The alarm to be deleted.
    155      */
    156     public void asyncDeleteAlarm(final Alarm alarm) {
    157         final AsyncTask<Void, Void, Boolean> deleteTask = new AsyncTask<Void, Void, Boolean>() {
    158             @Override
    159             protected Boolean doInBackground(Void... parameters) {
    160                 // Activity may be closed at this point , make sure data is still valid
    161                 if (alarm == null) {
    162                     // Nothing to do here, just return.
    163                     return false;
    164                 }
    165                 AlarmStateManager.deleteAllInstances(mAppContext, alarm.id);
    166                 return Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id);
    167             }
    168 
    169             @Override
    170             protected void onPostExecute(Boolean deleted) {
    171                 if (deleted) {
    172                     mDeletedAlarm = alarm;
    173                     showUndoBar();
    174                 }
    175             }
    176         };
    177         deleteTask.execute();
    178     }
    179 
    180     /**
    181      * Show a toast when an alarm is predismissed.
    182      *
    183      * @param instance Instance being predismissed.
    184      */
    185     public void showPredismissToast(AlarmInstance instance) {
    186         final String time = DateFormat.getTimeFormat(mAppContext).format(
    187                 instance.getAlarmTime().getTime());
    188         final String text = mAppContext.getString(R.string.alarm_is_dismissed, time);
    189         SnackbarManager.show(Snackbar.make(mSnackbarAnchor, text, Snackbar.LENGTH_SHORT));
    190     }
    191 
    192     /**
    193      * Hides any undo toast.
    194      */
    195     public void hideUndoBar() {
    196         mDeletedAlarm = null;
    197         SnackbarManager.dismiss();
    198     }
    199 
    200     private void showUndoBar() {
    201         final Alarm deletedAlarm = mDeletedAlarm;
    202         final Snackbar snackbar = Snackbar.make(mSnackbarAnchor,
    203                 mAppContext.getString(R.string.alarm_deleted), Snackbar.LENGTH_LONG)
    204                 .setAction(R.string.alarm_undo, new View.OnClickListener() {
    205                     @Override
    206                     public void onClick(View v) {
    207                         mDeletedAlarm = null;
    208                         asyncAddAlarm(deletedAlarm);
    209                     }
    210                 });
    211         SnackbarManager.show(snackbar);
    212     }
    213 
    214     private AlarmInstance setupAlarmInstance(Alarm alarm) {
    215         final ContentResolver cr = mAppContext.getContentResolver();
    216         AlarmInstance newInstance = alarm.createInstanceAfter(Calendar.getInstance());
    217         newInstance = AlarmInstance.addInstance(cr, newInstance);
    218         // Register instance to state manager
    219         AlarmStateManager.registerInstance(mAppContext, newInstance, true);
    220         return newInstance;
    221     }
    222 }
    223