• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ActionBar;
20 import android.app.AlertDialog;
21 import android.app.TimePickerDialog;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.DialogInterface.OnCancelListener;
25 import android.content.Intent;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Vibrator;
29 import android.preference.CheckBoxPreference;
30 import android.preference.Preference;
31 import android.preference.PreferenceActivity;
32 import android.preference.PreferenceScreen;
33 import android.text.format.DateFormat;
34 import android.view.LayoutInflater;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.view.View;
38 import android.view.View.OnClickListener;
39 import android.widget.Button;
40 import android.widget.EditText;
41 import android.widget.ListView;
42 import android.widget.TimePicker;
43 import android.widget.Toast;
44 
45 /**
46  * Manages each alarm
47  */
48 public class SetAlarm extends PreferenceActivity implements Preference.OnPreferenceChangeListener,
49         TimePickerDialog.OnTimeSetListener, OnCancelListener {
50 
51     private static final String KEY_CURRENT_ALARM = "currentAlarm";
52     private static final String KEY_ORIGINAL_ALARM = "originalAlarm";
53     private static final String KEY_TIME_PICKER_BUNDLE = "timePickerBundle";
54 
55     private EditText mLabel;
56     private CheckBoxPreference mEnabledPref;
57     private Preference mTimePref;
58     private AlarmPreference mAlarmPref;
59     private CheckBoxPreference mVibratePref;
60     private RepeatPreference mRepeatPref;
61 
62     private int     mId;
63     private int     mHour;
64     private int     mMinute;
65     private TimePickerDialog mTimePickerDialog;
66     private Alarm   mOriginalAlarm;
67 
68     @Override
onCreate(Bundle icicle)69     protected void onCreate(Bundle icicle) {
70         super.onCreate(icicle);
71 
72         // Override the default content view.
73         setContentView(R.layout.set_alarm);
74 
75         EditText label = (EditText) getLayoutInflater().inflate(R.layout.alarm_label, null);
76         ListView list = (ListView) findViewById(android.R.id.list);
77         list.addFooterView(label);
78 
79         // TODO Stop using preferences for this view. Save on done, not after
80         // each change.
81         addPreferencesFromResource(R.xml.alarm_prefs);
82 
83         // Get each preference so we can retrieve the value later.
84         mLabel = label;
85         mEnabledPref = (CheckBoxPreference) findPreference("enabled");
86         mEnabledPref.setOnPreferenceChangeListener(this);
87         mTimePref = findPreference("time");
88         mAlarmPref = (AlarmPreference) findPreference("alarm");
89         mAlarmPref.setOnPreferenceChangeListener(this);
90         mVibratePref = (CheckBoxPreference) findPreference("vibrate");
91         mVibratePref.setOnPreferenceChangeListener(this);
92         Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
93         if (!v.hasVibrator()) {
94             getPreferenceScreen().removePreference(mVibratePref);
95         }
96         mRepeatPref = (RepeatPreference) findPreference("setRepeat");
97         mRepeatPref.setOnPreferenceChangeListener(this);
98 
99         Intent i = getIntent();
100         Alarm alarm = i.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
101 
102         if (alarm == null) {
103             // No alarm means create a new alarm.
104             alarm = new Alarm();
105         }
106         mOriginalAlarm = alarm;
107 
108         // Populate the prefs with the original alarm data.  updatePrefs also
109         // sets mId so it must be called before checking mId below.
110         updatePrefs(mOriginalAlarm);
111 
112         // We have to do this to get the save/cancel buttons to highlight on
113         // their own.
114         getListView().setItemsCanFocus(true);
115 
116         ActionBar actionBar = getActionBar();
117         if (actionBar != null) {
118             actionBar.setDisplayOptions(
119                     0, ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
120             LayoutInflater inflater = (LayoutInflater) getSystemService
121                     (Context.LAYOUT_INFLATER_SERVICE);
122             View customActionBarView = inflater.inflate(R.layout.set_alarm_action_bar, null);
123             actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
124                     ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME |
125                     ActionBar.DISPLAY_SHOW_TITLE);
126             actionBar.setCustomView(customActionBarView);
127             View saveMenuItem = customActionBarView.findViewById(R.id.save_menu_item);
128             saveMenuItem.setOnClickListener(new OnClickListener() {
129                 @Override
130                 public void onClick(View v) {
131                     saveAndExit();
132                 }
133             });
134         }
135 
136         // Attach actions to each button.
137         Button b = (Button) findViewById(R.id.alarm_save);
138         if (b != null) {
139             b.setOnClickListener(new View.OnClickListener() {
140                     public void onClick(View v) {
141                         long time = saveAlarm(null);
142                         if(mEnabledPref.isChecked()) {
143                             popAlarmSetToast(SetAlarm.this, time);
144                         }
145                         finish();
146                     }
147             });
148         }
149         b = (Button) findViewById(R.id.alarm_revert);
150         if (b != null) {
151             b.setOnClickListener(new View.OnClickListener() {
152                 public void onClick(View v) {
153                     revert();
154                     finish();
155                 }
156             });
157         }
158         b = (Button) findViewById(R.id.alarm_delete);
159         if (b != null) {
160             if (mId == -1) {
161                 b.setEnabled(false);
162                 b.setVisibility(View.GONE);
163             } else {
164                 b.setVisibility(View.VISIBLE);
165                 b.setOnClickListener(new View.OnClickListener() {
166                     public void onClick(View v) {
167                         deleteAlarm();
168                     }
169                 });
170             }
171         }
172 }
173 
174     @Override
onOptionsItemSelected(MenuItem item)175     public boolean onOptionsItemSelected(MenuItem item) {
176         if (item.getItemId() == R.id.menu_delete) {
177             deleteAlarm();
178             return true;
179         }
180         return super.onOptionsItemSelected(item);
181     }
182 
183     @Override
onCreateOptionsMenu(Menu menu)184     public boolean onCreateOptionsMenu(Menu menu) {
185         getMenuInflater().inflate(R.menu.set_alarm_context, menu);
186         return true;
187     }
188 
189     @Override
onSaveInstanceState(Bundle outState)190     protected void onSaveInstanceState(Bundle outState) {
191         super.onSaveInstanceState(outState);
192         outState.putParcelable(KEY_ORIGINAL_ALARM, mOriginalAlarm);
193         outState.putParcelable(KEY_CURRENT_ALARM, buildAlarmFromUi());
194         if (mTimePickerDialog != null) {
195             if (mTimePickerDialog.isShowing()) {
196                 outState.putParcelable(KEY_TIME_PICKER_BUNDLE, mTimePickerDialog
197                         .onSaveInstanceState());
198                 mTimePickerDialog.dismiss();
199             }
200             mTimePickerDialog = null;
201         }
202     }
203 
204     @Override
onRestoreInstanceState(Bundle state)205     protected void onRestoreInstanceState(Bundle state) {
206         super.onRestoreInstanceState(state);
207 
208         Alarm alarmFromBundle = state.getParcelable(KEY_ORIGINAL_ALARM);
209         if (alarmFromBundle != null) {
210             mOriginalAlarm = alarmFromBundle;
211         }
212 
213         alarmFromBundle = state.getParcelable(KEY_CURRENT_ALARM);
214         if (alarmFromBundle != null) {
215             updatePrefs(alarmFromBundle);
216         }
217 
218         Bundle b = state.getParcelable(KEY_TIME_PICKER_BUNDLE);
219         if (b != null) {
220             showTimePicker();
221             mTimePickerDialog.onRestoreInstanceState(b);
222         }
223     }
224 
225     // Used to post runnables asynchronously.
226     private static final Handler sHandler = new Handler();
227 
onPreferenceChange(final Preference p, Object newValue)228     public boolean onPreferenceChange(final Preference p, Object newValue) {
229         // Asynchronously save the alarm since this method is called _before_
230         // the value of the preference has changed.
231         sHandler.post(new Runnable() {
232             public void run() {
233                 // Editing any preference (except enable) enables the alarm.
234                 if (p != mEnabledPref) {
235                     mEnabledPref.setChecked(true);
236                 }
237                 saveAlarm(null);
238             }
239         });
240         return true;
241     }
242 
updatePrefs(Alarm alarm)243     private void updatePrefs(Alarm alarm) {
244         mId = alarm.id;
245         mEnabledPref.setChecked(alarm.enabled);
246         mLabel.setText(alarm.label);
247         mHour = alarm.hour;
248         mMinute = alarm.minutes;
249         mRepeatPref.setDaysOfWeek(alarm.daysOfWeek);
250         mVibratePref.setChecked(alarm.vibrate);
251         // Give the alert uri to the preference.
252         mAlarmPref.setAlert(alarm.alert);
253         updateTime();
254     }
255 
256     @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)257     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
258             Preference preference) {
259         if (preference == mTimePref) {
260             showTimePicker();
261         }
262 
263         return super.onPreferenceTreeClick(preferenceScreen, preference);
264     }
265 
266     @Override
onBackPressed()267     public void onBackPressed() {
268         saveAndExit();
269     }
270 
showTimePicker()271     private void showTimePicker() {
272         if (mTimePickerDialog != null) {
273             if (mTimePickerDialog.isShowing()) {
274                 Log.e("mTimePickerDialog is already showing.");
275                 mTimePickerDialog.dismiss();
276             } else {
277                 Log.e("mTimePickerDialog is not null");
278             }
279             mTimePickerDialog = null;
280         }
281 
282         mTimePickerDialog = new TimePickerDialog(this, this, mHour, mMinute,
283                 DateFormat.is24HourFormat(this));
284         mTimePickerDialog.setOnCancelListener(this);
285         mTimePickerDialog.show();
286     }
287 
onTimeSet(TimePicker view, int hourOfDay, int minute)288     public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
289         // onTimeSet is called when the user clicks "Set"
290         mTimePickerDialog = null;
291         mHour = hourOfDay;
292         mMinute = minute;
293         updateTime();
294         // If the time has been changed, enable the alarm.
295         mEnabledPref.setChecked(true);
296         saveAlarm(null);
297     }
298 
299     @Override
onCancel(DialogInterface dialog)300     public void onCancel(DialogInterface dialog) {
301         mTimePickerDialog = null;
302     }
303 
updateTime()304     private void updateTime() {
305         mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinute,
306                 mRepeatPref.getDaysOfWeek()));
307     }
308 
saveAlarm(Alarm alarm)309     private long saveAlarm(Alarm alarm) {
310         if (alarm == null) {
311             alarm = buildAlarmFromUi();
312         }
313 
314         long time;
315         if (alarm.id == -1) {
316             time = Alarms.addAlarm(this, alarm);
317             // addAlarm populates the alarm with the new id. Update mId so that
318             // changes to other preferences update the new alarm.
319             mId = alarm.id;
320         } else {
321             time = Alarms.setAlarm(this, alarm);
322         }
323         return time;
324     }
325 
buildAlarmFromUi()326     private Alarm buildAlarmFromUi() {
327         Alarm alarm = new Alarm();
328         alarm.id = mId;
329         alarm.enabled = mEnabledPref.isChecked();
330         alarm.hour = mHour;
331         alarm.minutes = mMinute;
332         alarm.daysOfWeek = mRepeatPref.getDaysOfWeek();
333         alarm.vibrate = mVibratePref.isChecked();
334         alarm.label = mLabel.getText().toString();
335         alarm.alert = mAlarmPref.getAlert();
336         return alarm;
337     }
338 
deleteAlarm()339     private void deleteAlarm() {
340         if (mId == -1) {
341             // Unedited, newly created alarms don't require confirmation
342             finish();
343         } else {
344             new AlertDialog.Builder(this)
345                     .setTitle(getString(R.string.delete_alarm))
346                     .setMessage(getString(R.string.delete_alarm_confirm))
347                     .setPositiveButton(android.R.string.ok,
348                             new DialogInterface.OnClickListener() {
349                                 public void onClick(DialogInterface d, int w) {
350                                     Alarms.deleteAlarm(SetAlarm.this, mId);
351                                     finish();
352                                 }
353                             })
354                     .setNegativeButton(android.R.string.cancel, null)
355                     .show();
356         }
357     }
358 
revert()359     private void revert() {
360         int newId = mId;
361         // "Revert" on a newly created alarm should delete it.
362         if (mOriginalAlarm.id == -1) {
363             Alarms.deleteAlarm(SetAlarm.this, newId);
364         } else {
365             saveAlarm(mOriginalAlarm);
366         }
367     }
368 
369     /**
370      * Store any changes to the alarm and exit the activity.
371      * Show a toast if the alarm is enabled with the time remaining until alarm
372      */
saveAndExit()373     private void saveAndExit() {
374         long time = saveAlarm(null);
375         if(mEnabledPref.isChecked()) {
376             popAlarmSetToast(SetAlarm.this, time);
377         }
378         finish();
379     }
380 
381     /**
382      * Display a toast that tells the user how long until the alarm
383      * goes off.  This helps prevent "am/pm" mistakes.
384      */
popAlarmSetToast(Context context, int hour, int minute, Alarm.DaysOfWeek daysOfWeek)385     static void popAlarmSetToast(Context context, int hour, int minute,
386                                  Alarm.DaysOfWeek daysOfWeek) {
387         popAlarmSetToast(context,
388                 Alarms.calculateAlarm(hour, minute, daysOfWeek)
389                 .getTimeInMillis());
390     }
391 
popAlarmSetToast(Context context, long timeInMillis)392     static void popAlarmSetToast(Context context, long timeInMillis) {
393         String toastText = formatToast(context, timeInMillis);
394         Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
395         ToastMaster.setToast(toast);
396         toast.show();
397     }
398 
399     /**
400      * format "Alarm set for 2 days 7 hours and 53 minutes from
401      * now"
402      */
formatToast(Context context, long timeInMillis)403     static String formatToast(Context context, long timeInMillis) {
404         long delta = timeInMillis - System.currentTimeMillis();
405         long hours = delta / (1000 * 60 * 60);
406         long minutes = delta / (1000 * 60) % 60;
407         long days = hours / 24;
408         hours = hours % 24;
409 
410         String daySeq = (days == 0) ? "" :
411                 (days == 1) ? context.getString(R.string.day) :
412                 context.getString(R.string.days, Long.toString(days));
413 
414         String minSeq = (minutes == 0) ? "" :
415                 (minutes == 1) ? context.getString(R.string.minute) :
416                 context.getString(R.string.minutes, Long.toString(minutes));
417 
418         String hourSeq = (hours == 0) ? "" :
419                 (hours == 1) ? context.getString(R.string.hour) :
420                 context.getString(R.string.hours, Long.toString(hours));
421 
422         boolean dispDays = days > 0;
423         boolean dispHour = hours > 0;
424         boolean dispMinute = minutes > 0;
425 
426         int index = (dispDays ? 1 : 0) |
427                     (dispHour ? 2 : 0) |
428                     (dispMinute ? 4 : 0);
429 
430         String[] formats = context.getResources().getStringArray(R.array.alarm_set);
431         return String.format(formats[index], daySeq, hourSeq, minSeq);
432     }
433 }
434