OSDN Git Service

Merge commit 'remotes/korg/cupcake' into merge
[android-x86/packages-apps-AlarmClock.git] / src / com / android / alarmclock / SetAlarm.java
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.alarmclock;
18
19 import android.app.Activity;
20 import android.app.Dialog;
21 import android.app.TimePickerDialog;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.database.ContentObserver;
25 import android.media.Ringtone;
26 import android.media.RingtoneManager;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.preference.CheckBoxPreference;
31 import android.preference.EditTextPreference;
32 import android.preference.Preference;
33 import android.preference.PreferenceActivity;
34 import android.preference.PreferenceScreen;
35 import android.text.format.DateFormat;
36 import android.view.Menu;
37 import android.view.MenuItem;
38 import android.widget.TimePicker;
39 import android.widget.Toast;
40
41 /**
42  * Manages each alarm
43  */
44 public class SetAlarm extends PreferenceActivity
45         implements Alarms.AlarmSettings, TimePickerDialog.OnTimeSetListener {
46
47     private EditTextPreference mLabel;
48     private CheckBoxPreference mAlarmOnPref;
49     private Preference mTimePref;
50     private AlarmPreference mAlarmPref;
51     private CheckBoxPreference mVibratePref;
52     private RepeatPreference mRepeatPref;
53     private ContentObserver mAlarmsChangeObserver;
54     private MenuItem mDeleteAlarmItem;
55     private MenuItem mTestAlarmItem;
56
57     private int mId;
58     private int mHour;
59     private int mMinutes;
60     private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek();
61
62     private boolean mReportAlarmCalled;
63
64     private static final int DIALOG_TIMEPICKER = 0;
65
66     private class RingtoneChangedListener implements AlarmPreference.IRingtoneChangedListener {
67         public void onRingtoneChanged(Uri ringtoneUri) {
68             saveAlarm(false);
69         }
70     }
71
72     private class OnRepeatChangedObserver implements RepeatPreference.OnRepeatChangedObserver {
73         public void onRepeatChanged(Alarms.DaysOfWeek daysOfWeek) {
74             if (!mDaysOfWeek.equals(daysOfWeek)) {
75                 mDaysOfWeek.set(daysOfWeek);
76                 saveAlarm(true);
77             }
78         }
79         public Alarms.DaysOfWeek getDaysOfWeek() {
80             return mDaysOfWeek;
81         }
82     }
83
84     private class AlarmsChangeObserver extends ContentObserver {
85         public AlarmsChangeObserver() {
86             super(new Handler());
87         }
88         @Override
89         public void onChange(boolean selfChange) {
90             Alarms.getAlarm(getContentResolver(), SetAlarm.this, mId);
91         }
92     }
93
94     /**
95      * Set an alarm.  Requires an Alarms.ID to be passed in as an
96      * extra
97      */
98     @Override
99     protected void onCreate(Bundle icicle) {
100         super.onCreate(icicle);
101
102         addPreferencesFromResource(R.xml.alarm_prefs);
103         mLabel = (EditTextPreference) findPreference("label");
104         mLabel.setOnPreferenceChangeListener(
105                 new Preference.OnPreferenceChangeListener() {
106                     public boolean onPreferenceChange(Preference p,
107                             Object newValue) {
108                         p.setSummary((String) newValue);
109                         saveAlarm(false, (String) newValue);
110                         return true;
111                     }
112                 });
113         mAlarmOnPref = (CheckBoxPreference)findPreference("on");
114         mTimePref = findPreference("time");
115         mAlarmPref = (AlarmPreference) findPreference("alarm");
116         mVibratePref = (CheckBoxPreference) findPreference("vibrate");
117         mRepeatPref = (RepeatPreference) findPreference("setRepeat");
118
119         Intent i = getIntent();
120         mId = i.getIntExtra(Alarms.ID, -1);
121         if (Log.LOGV) Log.v("In SetAlarm, alarm id = " + mId);
122
123         mReportAlarmCalled = false;
124         /* load alarm details from database */
125         Alarms.getAlarm(getContentResolver(), this, mId);
126         /* This should never happen, but does occasionally with the monkey.
127          * I believe it's a race condition where a deleted alarm is opened
128          * before the alarm list is refreshed. */
129         if (!mReportAlarmCalled) {
130             Log.e("reportAlarm never called!");
131             finish();
132         }
133
134         mAlarmsChangeObserver = new AlarmsChangeObserver();
135         getContentResolver().registerContentObserver(
136                 Alarms.AlarmColumns.CONTENT_URI, true, mAlarmsChangeObserver);
137
138         mAlarmPref.setRingtoneChangedListener(new RingtoneChangedListener());
139         mRepeatPref.setOnRepeatChangedObserver(new OnRepeatChangedObserver());
140     }
141
142     @Override
143     protected void onDestroy() {
144         super.onDestroy();
145         getContentResolver().unregisterContentObserver(mAlarmsChangeObserver);
146     }
147
148     @Override
149     protected Dialog onCreateDialog(int id) {
150         Dialog d;
151
152         switch (id) {
153         case DIALOG_TIMEPICKER:
154             d = new TimePickerDialog(
155                     SetAlarm.this,
156                     this,
157                     0,
158                     0,
159                     DateFormat.is24HourFormat(SetAlarm.this));
160             d.setTitle(getResources().getString(R.string.time));
161             break;
162         default:
163             d = null;
164         }
165
166         return d;
167     }
168
169     @Override
170     protected void onPrepareDialog(int id, Dialog dialog) {
171         super.onPrepareDialog(id, dialog);
172
173         switch (id) {
174         case DIALOG_TIMEPICKER:
175             TimePickerDialog timePicker = (TimePickerDialog)dialog;
176             timePicker.updateTime(mHour, mMinutes);
177             break;
178         }
179     }
180
181     @Override
182     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
183
184         if (preference == mTimePref) {
185             showDialog(DIALOG_TIMEPICKER);
186         } else if (preference == mAlarmOnPref) {
187             saveAlarm(true);
188         } else if (preference == mVibratePref) {
189             saveAlarm(false);
190         }
191
192         return super.onPreferenceTreeClick(preferenceScreen, preference);
193     }
194
195     public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
196         mHour = hourOfDay;
197         mMinutes = minute;
198         mAlarmOnPref.setChecked(true);
199         saveAlarm(true);
200     }
201
202     /**
203      * Alarms.AlarmSettings implementation.  Database feeds current
204      * settings in through this call
205      */
206     public void reportAlarm(
207             int idx, boolean enabled, int hour, int minutes,
208             Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label,
209             String alert) {
210
211         if (label == null || label.length() == 0) {
212             label = getString(R.string.default_label);
213         }
214         mLabel.setText(label);
215         mLabel.setSummary(label);
216         mHour = hour;
217         mMinutes = minutes;
218         mAlarmOnPref.setChecked(enabled);
219         mDaysOfWeek.set(daysOfWeek);
220         mVibratePref.setChecked(vibrate);
221
222         if (alert == null || alert.length() == 0) {
223             if (Log.LOGV) Log.v("****** reportAlarm null or 0-length alert");
224             mAlarmPref.mAlert = getDefaultAlarm();
225             if (mAlarmPref.mAlert == null) {
226                 Log.e("****** Default Alarm null");
227             }
228         } else {
229             mAlarmPref.mAlert = Uri.parse(alert);
230             if (mAlarmPref.mAlert == null) {
231                 Log.e("****** Parsed null alarm. URI: " + alert);
232             }
233         }
234         if (Log.LOGV) Log.v("****** reportAlarm uri " + alert + " alert " +
235                             mAlarmPref.mAlert);
236         updateTime();
237         updateRepeat();
238         updateAlarm(mAlarmPref.mAlert);
239
240         mReportAlarmCalled = true;
241     }
242
243     /**
244      * picks the first alarm available
245      */
246     private Uri getDefaultAlarm() {
247         RingtoneManager ringtoneManager = new RingtoneManager(this);
248         ringtoneManager.setType(RingtoneManager.TYPE_ALARM);
249         return ringtoneManager.getRingtoneUri(0);
250     }
251
252     private void updateTime() {
253         if (Log.LOGV) Log.v("updateTime " + mId);
254         mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes, mDaysOfWeek));
255     }
256
257     private void updateAlarm(Uri ringtoneUri) {
258         if (Log.LOGV) Log.v("updateAlarm " + mId);
259         Ringtone ringtone = RingtoneManager.getRingtone(SetAlarm.this, ringtoneUri);
260         if (ringtone != null) {
261             mAlarmPref.setSummary(ringtone.getTitle(SetAlarm.this));
262         }
263     }
264
265     private void updateRepeat() {
266         if (Log.LOGV) Log.v("updateRepeat " + mId);
267         mRepeatPref.setSummary(mDaysOfWeek.toString(this, true));
268     }
269
270     private void saveAlarm(boolean popToast) {
271         saveAlarm(popToast, mLabel.getText());
272     }
273
274     /**
275      * This version of saveAlarm uses the passed in label since mLabel may
276      * contain the old value (i.e. during the preference value change).
277      */
278     private void saveAlarm(boolean popToast, String label) {
279         if (mReportAlarmCalled && mAlarmPref.mAlert != null) {
280             String alertString = mAlarmPref.mAlert.toString();
281             saveAlarm(this, mId, mAlarmOnPref.isChecked(), mHour, mMinutes,
282                       mDaysOfWeek, mVibratePref.isChecked(), label, alertString,
283                       popToast);
284         }
285     }
286
287     /**
288      * Write alarm out to persistent store and pops toast if alarm
289      * enabled
290      */
291     private static void saveAlarm(
292             Context context, int id, boolean enabled, int hour, int minute,
293             Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label,
294             String alert, boolean popToast) {
295         if (Log.LOGV) Log.v("** saveAlarm " + id + " " + label + " " + enabled
296                 + " " + hour + " " + minute + " vibe " + vibrate);
297
298         // Fix alert string first
299         Alarms.setAlarm(context, id, enabled, hour, minute, daysOfWeek, vibrate,
300                 label, alert);
301
302         if (enabled && popToast) {
303             popAlarmSetToast(context, hour, minute, daysOfWeek);
304         }
305     }
306
307     /**
308      * Display a toast that tells the user how long until the alarm
309      * goes off.  This helps prevent "am/pm" mistakes.
310      */
311     static void popAlarmSetToast(Context context, int hour, int minute,
312                                  Alarms.DaysOfWeek daysOfWeek) {
313
314         String toastText = formatToast(context, hour, minute, daysOfWeek);
315         Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
316         ToastMaster.setToast(toast);
317         toast.show();
318     }
319
320     /**
321      * format "Alarm set for 2 days 7 hours and 53 minutes from
322      * now"
323      */
324     static String formatToast(Context context, int hour, int minute,
325                               Alarms.DaysOfWeek daysOfWeek) {
326         long alarm = Alarms.calculateAlarm(hour, minute,
327                                            daysOfWeek).getTimeInMillis();
328         long delta = alarm - System.currentTimeMillis();;
329         long hours = delta / (1000 * 60 * 60);
330         long minutes = delta / (1000 * 60) % 60;
331         long days = hours / 24;
332         hours = hours % 24;
333
334         String daySeq = (days == 0) ? "" :
335                 (days == 1) ? context.getString(R.string.day) :
336                 context.getString(R.string.days, Long.toString(days));
337
338         String minSeq = (minutes == 0) ? "" :
339                 (minutes == 1) ? context.getString(R.string.minute) :
340                 context.getString(R.string.minutes, Long.toString(minutes));
341
342         String hourSeq = (hours == 0) ? "" :
343                 (hours == 1) ? context.getString(R.string.hour) :
344                 context.getString(R.string.hours, Long.toString(hours));
345
346         boolean dispDays = days > 0;
347         boolean dispHour = hours > 0;
348         boolean dispMinute = minutes > 0;
349
350         String ret;
351         if (!(dispDays || dispHour || dispMinute)) {
352             ret = context.getString(R.string.subminute);
353         } else {
354             String parts[] = new String[5];
355             parts[0] = daySeq;
356             parts[1] = !dispDays ? "" :
357                     dispHour && dispMinute ? context.getString(R.string.space) :
358                     !dispHour && !dispMinute ? "" :
359                     context.getString(R.string.and);
360             parts[2] = dispHour ? hourSeq : "";
361             parts[3] = dispHour && dispMinute ? context.getString(R.string.and) : "";
362             parts[4] = dispMinute ? minSeq : "";
363             ret = context.getString(R.string.combiner, (Object[])parts);
364         }
365
366         ret = context.getString(R.string.alarm_set, ret);
367         /* if (Log.LOGV) Log.v("** TOAST daySeq " + daySeq + " hourSeq " + hourSeq +
368            " minSeq " + minSeq + " ret " + ret); */
369         return ret;
370     }
371
372     public boolean onCreateOptionsMenu(Menu menu) {
373         super.onCreateOptionsMenu(menu);
374
375         mDeleteAlarmItem = menu.add(0, 0, 0, R.string.delete_alarm);
376         mDeleteAlarmItem.setIcon(android.R.drawable.ic_menu_delete);
377
378         if (AlarmClock.DEBUG) {
379             mTestAlarmItem = menu.add(0, 0, 0, "test alarm");
380         }
381
382
383         return true;
384     }
385
386     public boolean onOptionsItemSelected(MenuItem item) {
387         if (item == mDeleteAlarmItem) {
388             Alarms.deleteAlarm(this, mId);
389             finish();
390             return true;
391         }
392         if (AlarmClock.DEBUG) {
393             if (item == mTestAlarmItem) {
394                 setTestAlarm();
395                 return true;
396             }
397         }
398
399         return false;
400     }
401
402
403     /**
404      * Test code: this is disabled for production build.  Sets
405      * this alarm to go off on the next minute
406      */
407     void setTestAlarm() {
408
409         // start with now
410         java.util.Calendar c = java.util.Calendar.getInstance();
411         c.setTimeInMillis(System.currentTimeMillis());
412
413         int nowHour = c.get(java.util.Calendar.HOUR_OF_DAY);
414         int nowMinute = c.get(java.util.Calendar.MINUTE);
415
416         int minutes = (nowMinute + 1) % 60;
417         int hour = nowHour + (nowMinute == 0? 1 : 0);
418
419         saveAlarm(this, mId, true, hour, minutes, mDaysOfWeek, true,
420                 mLabel.getText(), mAlarmPref.mAlert.toString(), true);
421     }
422
423 }