Home | History | Annotate | Download | only in deskclock
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.TimePickerDialog;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.Intent;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.preference.CheckBoxPreference;
     27 import android.preference.EditTextPreference;
     28 import android.preference.Preference;
     29 import android.preference.PreferenceActivity;
     30 import android.preference.PreferenceScreen;
     31 import android.text.format.DateFormat;
     32 import android.view.LayoutInflater;
     33 import android.view.Menu;
     34 import android.view.MenuItem;
     35 import android.view.View;
     36 import android.view.ViewGroup.LayoutParams;
     37 import android.widget.Button;
     38 import android.widget.FrameLayout;
     39 import android.widget.LinearLayout;
     40 import android.widget.ListView;
     41 import android.widget.TimePicker;
     42 import android.widget.Toast;
     43 
     44 /**
     45  * Manages each alarm
     46  */
     47 public class SetAlarm extends PreferenceActivity
     48         implements TimePickerDialog.OnTimeSetListener,
     49         Preference.OnPreferenceChangeListener {
     50 
     51     private EditTextPreference mLabel;
     52     private CheckBoxPreference mEnabledPref;
     53     private Preference mTimePref;
     54     private AlarmPreference mAlarmPref;
     55     private CheckBoxPreference mVibratePref;
     56     private RepeatPreference mRepeatPref;
     57     private MenuItem mTestAlarmItem;
     58 
     59     private int     mId;
     60     private int     mHour;
     61     private int     mMinutes;
     62     private boolean mTimePickerCancelled;
     63     private Alarm   mOriginalAlarm;
     64 
     65     /**
     66      * Set an alarm.  Requires an Alarms.ALARM_ID to be passed in as an
     67      * extra. FIXME: Pass an Alarm object like every other Activity.
     68      */
     69     @Override
     70     protected void onCreate(Bundle icicle) {
     71         super.onCreate(icicle);
     72 
     73         // Override the default content view.
     74         setContentView(R.layout.set_alarm);
     75 
     76         addPreferencesFromResource(R.xml.alarm_prefs);
     77 
     78         // Get each preference so we can retrieve the value later.
     79         mLabel = (EditTextPreference) findPreference("label");
     80         mLabel.setOnPreferenceChangeListener(
     81                 new Preference.OnPreferenceChangeListener() {
     82                     public boolean onPreferenceChange(Preference p,
     83                             Object newValue) {
     84                         String val = (String) newValue;
     85                         // Set the summary based on the new label.
     86                         p.setSummary(val);
     87                         if (val != null && !val.equals(mLabel.getText())) {
     88                             // Call through to the generic listener.
     89                             return SetAlarm.this.onPreferenceChange(p,
     90                                 newValue);
     91                         }
     92                         return true;
     93                     }
     94                 });
     95         mEnabledPref = (CheckBoxPreference) findPreference("enabled");
     96         mEnabledPref.setOnPreferenceChangeListener(
     97                 new Preference.OnPreferenceChangeListener() {
     98                     public boolean onPreferenceChange(Preference p,
     99                             Object newValue) {
    100                         // Pop a toast when enabling alarms.
    101                         if (!mEnabledPref.isChecked()) {
    102                             popAlarmSetToast(SetAlarm.this, mHour, mMinutes,
    103                                 mRepeatPref.getDaysOfWeek());
    104                         }
    105                         return SetAlarm.this.onPreferenceChange(p, newValue);
    106                     }
    107                 });
    108         mTimePref = findPreference("time");
    109         mAlarmPref = (AlarmPreference) findPreference("alarm");
    110         mAlarmPref.setOnPreferenceChangeListener(this);
    111         mVibratePref = (CheckBoxPreference) findPreference("vibrate");
    112         mVibratePref.setOnPreferenceChangeListener(this);
    113         mRepeatPref = (RepeatPreference) findPreference("setRepeat");
    114         mRepeatPref.setOnPreferenceChangeListener(this);
    115 
    116         Intent i = getIntent();
    117         mId = i.getIntExtra(Alarms.ALARM_ID, -1);
    118         if (Log.LOGV) {
    119             Log.v("In SetAlarm, alarm id = " + mId);
    120         }
    121 
    122         Alarm alarm = null;
    123         if (mId == -1) {
    124             // No alarm id means create a new alarm.
    125             alarm = new Alarm();
    126         } else {
    127             /* load alarm details from database */
    128             alarm = Alarms.getAlarm(getContentResolver(), mId);
    129             // Bad alarm, bail to avoid a NPE.
    130             if (alarm == null) {
    131                 finish();
    132                 return;
    133             }
    134         }
    135         mOriginalAlarm = alarm;
    136 
    137         updatePrefs(mOriginalAlarm);
    138 
    139         // We have to do this to get the save/cancel buttons to highlight on
    140         // their own.
    141         getListView().setItemsCanFocus(true);
    142 
    143         // Attach actions to each button.
    144         Button b = (Button) findViewById(R.id.alarm_save);
    145         b.setOnClickListener(new View.OnClickListener() {
    146                 public void onClick(View v) {
    147                     saveAlarm();
    148                     finish();
    149                 }
    150         });
    151         final Button revert = (Button) findViewById(R.id.alarm_revert);
    152         revert.setEnabled(false);
    153         revert.setOnClickListener(new View.OnClickListener() {
    154                 public void onClick(View v) {
    155                     int newId = mId;
    156                     updatePrefs(mOriginalAlarm);
    157                     // "Revert" on a newly created alarm should delete it.
    158                     if (mOriginalAlarm.id == -1) {
    159                         Alarms.deleteAlarm(SetAlarm.this, newId);
    160                     } else {
    161                         saveAlarm();
    162                     }
    163                     revert.setEnabled(false);
    164                 }
    165         });
    166         b = (Button) findViewById(R.id.alarm_delete);
    167         if (mId == -1) {
    168             b.setEnabled(false);
    169         } else {
    170             b.setOnClickListener(new View.OnClickListener() {
    171                 public void onClick(View v) {
    172                     deleteAlarm();
    173                 }
    174             });
    175         }
    176 
    177         // The last thing we do is pop the time picker if this is a new alarm.
    178         if (mId == -1) {
    179             // Assume the user hit cancel
    180             mTimePickerCancelled = true;
    181             showTimePicker();
    182         }
    183     }
    184 
    185     // Used to post runnables asynchronously.
    186     private static final Handler sHandler = new Handler();
    187 
    188     public boolean onPreferenceChange(final Preference p, Object newValue) {
    189         // Asynchronously save the alarm since this method is called _before_
    190         // the value of the preference has changed.
    191         sHandler.post(new Runnable() {
    192             public void run() {
    193                 // Editing any preference (except enable) enables the alarm.
    194                 if (p != mEnabledPref) {
    195                     mEnabledPref.setChecked(true);
    196                 }
    197                 saveAlarmAndEnableRevert();
    198             }
    199         });
    200         return true;
    201     }
    202 
    203     private void updatePrefs(Alarm alarm) {
    204         mId = alarm.id;
    205         mEnabledPref.setChecked(alarm.enabled);
    206         mLabel.setText(alarm.label);
    207         mLabel.setSummary(alarm.label);
    208         mHour = alarm.hour;
    209         mMinutes = alarm.minutes;
    210         mRepeatPref.setDaysOfWeek(alarm.daysOfWeek);
    211         mVibratePref.setChecked(alarm.vibrate);
    212         // Give the alert uri to the preference.
    213         mAlarmPref.setAlert(alarm.alert);
    214         updateTime();
    215     }
    216 
    217     @Override
    218     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
    219             Preference preference) {
    220         if (preference == mTimePref) {
    221             showTimePicker();
    222         }
    223 
    224         return super.onPreferenceTreeClick(preferenceScreen, preference);
    225     }
    226 
    227     @Override
    228     public void onBackPressed() {
    229         // In the usual case of viewing an alarm, mTimePickerCancelled is
    230         // initialized to false. When creating a new alarm, this value is
    231         // assumed true until the user changes the time.
    232         if (!mTimePickerCancelled) {
    233             saveAlarm();
    234         }
    235         finish();
    236     }
    237 
    238     private void showTimePicker() {
    239         new TimePickerDialog(this, this, mHour, mMinutes,
    240                 DateFormat.is24HourFormat(this)).show();
    241     }
    242 
    243     public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
    244         // onTimeSet is called when the user clicks "Set"
    245         mTimePickerCancelled = false;
    246         mHour = hourOfDay;
    247         mMinutes = minute;
    248         updateTime();
    249         // If the time has been changed, enable the alarm.
    250         mEnabledPref.setChecked(true);
    251         // Save the alarm and pop a toast.
    252         popAlarmSetToast(this, saveAlarmAndEnableRevert());
    253     }
    254 
    255     private void updateTime() {
    256         if (Log.LOGV) {
    257             Log.v("updateTime " + mId);
    258         }
    259         mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes,
    260                 mRepeatPref.getDaysOfWeek()));
    261     }
    262 
    263     private long saveAlarmAndEnableRevert() {
    264         // Enable "Revert" to go back to the original Alarm.
    265         final Button revert = (Button) findViewById(R.id.alarm_revert);
    266         revert.setEnabled(true);
    267         return saveAlarm();
    268     }
    269 
    270     private long saveAlarm() {
    271         Alarm alarm = new Alarm();
    272         alarm.id = mId;
    273         alarm.enabled = mEnabledPref.isChecked();
    274         alarm.hour = mHour;
    275         alarm.minutes = mMinutes;
    276         alarm.daysOfWeek = mRepeatPref.getDaysOfWeek();
    277         alarm.vibrate = mVibratePref.isChecked();
    278         alarm.label = mLabel.getText();
    279         alarm.alert = mAlarmPref.getAlert();
    280 
    281         long time;
    282         if (alarm.id == -1) {
    283             time = Alarms.addAlarm(this, alarm);
    284             // addAlarm populates the alarm with the new id. Update mId so that
    285             // changes to other preferences update the new alarm.
    286             mId = alarm.id;
    287         } else {
    288             time = Alarms.setAlarm(this, alarm);
    289         }
    290         return time;
    291     }
    292 
    293     private void deleteAlarm() {
    294         new AlertDialog.Builder(this)
    295                 .setTitle(getString(R.string.delete_alarm))
    296                 .setMessage(getString(R.string.delete_alarm_confirm))
    297                 .setPositiveButton(android.R.string.ok,
    298                         new DialogInterface.OnClickListener() {
    299                             public void onClick(DialogInterface d, int w) {
    300                                 Alarms.deleteAlarm(SetAlarm.this, mId);
    301                                 finish();
    302                             }
    303                         })
    304                 .setNegativeButton(android.R.string.cancel, null)
    305                 .show();
    306     }
    307 
    308     /**
    309      * Display a toast that tells the user how long until the alarm
    310      * goes off.  This helps prevent "am/pm" mistakes.
    311      */
    312     static void popAlarmSetToast(Context context, int hour, int minute,
    313                                  Alarm.DaysOfWeek daysOfWeek) {
    314         popAlarmSetToast(context,
    315                 Alarms.calculateAlarm(hour, minute, daysOfWeek)
    316                 .getTimeInMillis());
    317     }
    318 
    319     static void popAlarmSetToast(Context context, long timeInMillis) {
    320         String toastText = formatToast(context, timeInMillis);
    321         Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
    322         ToastMaster.setToast(toast);
    323         toast.show();
    324     }
    325 
    326     /**
    327      * format "Alarm set for 2 days 7 hours and 53 minutes from
    328      * now"
    329      */
    330     static String formatToast(Context context, long timeInMillis) {
    331         long delta = timeInMillis - System.currentTimeMillis();
    332         long hours = delta / (1000 * 60 * 60);
    333         long minutes = delta / (1000 * 60) % 60;
    334         long days = hours / 24;
    335         hours = hours % 24;
    336 
    337         String daySeq = (days == 0) ? "" :
    338                 (days == 1) ? context.getString(R.string.day) :
    339                 context.getString(R.string.days, Long.toString(days));
    340 
    341         String minSeq = (minutes == 0) ? "" :
    342                 (minutes == 1) ? context.getString(R.string.minute) :
    343                 context.getString(R.string.minutes, Long.toString(minutes));
    344 
    345         String hourSeq = (hours == 0) ? "" :
    346                 (hours == 1) ? context.getString(R.string.hour) :
    347                 context.getString(R.string.hours, Long.toString(hours));
    348 
    349         boolean dispDays = days > 0;
    350         boolean dispHour = hours > 0;
    351         boolean dispMinute = minutes > 0;
    352 
    353         int index = (dispDays ? 1 : 0) |
    354                     (dispHour ? 2 : 0) |
    355                     (dispMinute ? 4 : 0);
    356 
    357         String[] formats = context.getResources().getStringArray(R.array.alarm_set);
    358         return String.format(formats[index], daySeq, hourSeq, minSeq);
    359     }
    360 }
    361