2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.alarmclock;
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;
44 public class SetAlarm extends PreferenceActivity
45 implements Alarms.AlarmSettings, TimePickerDialog.OnTimeSetListener {
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;
60 private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek();
62 private boolean mReportAlarmCalled;
64 private static final int DIALOG_TIMEPICKER = 0;
66 private class RingtoneChangedListener implements AlarmPreference.IRingtoneChangedListener {
67 public void onRingtoneChanged(Uri ringtoneUri) {
72 private class OnRepeatChangedObserver implements RepeatPreference.OnRepeatChangedObserver {
73 public void onRepeatChanged(Alarms.DaysOfWeek daysOfWeek) {
74 if (!mDaysOfWeek.equals(daysOfWeek)) {
75 mDaysOfWeek.set(daysOfWeek);
79 public Alarms.DaysOfWeek getDaysOfWeek() {
84 private class AlarmsChangeObserver extends ContentObserver {
85 public AlarmsChangeObserver() {
89 public void onChange(boolean selfChange) {
90 Alarms.getAlarm(getContentResolver(), SetAlarm.this, mId);
95 * Set an alarm. Requires an Alarms.ID to be passed in as an
99 protected void onCreate(Bundle icicle) {
100 super.onCreate(icicle);
102 addPreferencesFromResource(R.xml.alarm_prefs);
103 mLabel = (EditTextPreference) findPreference("label");
104 mLabel.setOnPreferenceChangeListener(
105 new Preference.OnPreferenceChangeListener() {
106 public boolean onPreferenceChange(Preference p,
108 p.setSummary((String) newValue);
109 saveAlarm(false, (String) newValue);
113 mAlarmOnPref = (CheckBoxPreference)findPreference("on");
114 mTimePref = findPreference("time");
115 mAlarmPref = (AlarmPreference) findPreference("alarm");
116 mVibratePref = (CheckBoxPreference) findPreference("vibrate");
117 mRepeatPref = (RepeatPreference) findPreference("setRepeat");
119 Intent i = getIntent();
120 mId = i.getIntExtra(Alarms.ID, -1);
121 if (Log.LOGV) Log.v("In SetAlarm, alarm id = " + mId);
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!");
134 mAlarmsChangeObserver = new AlarmsChangeObserver();
135 getContentResolver().registerContentObserver(
136 Alarms.AlarmColumns.CONTENT_URI, true, mAlarmsChangeObserver);
138 mAlarmPref.setRingtoneChangedListener(new RingtoneChangedListener());
139 mRepeatPref.setOnRepeatChangedObserver(new OnRepeatChangedObserver());
143 protected void onDestroy() {
145 getContentResolver().unregisterContentObserver(mAlarmsChangeObserver);
149 protected Dialog onCreateDialog(int id) {
153 case DIALOG_TIMEPICKER:
154 d = new TimePickerDialog(
159 DateFormat.is24HourFormat(SetAlarm.this));
160 d.setTitle(getResources().getString(R.string.time));
170 protected void onPrepareDialog(int id, Dialog dialog) {
171 super.onPrepareDialog(id, dialog);
174 case DIALOG_TIMEPICKER:
175 TimePickerDialog timePicker = (TimePickerDialog)dialog;
176 timePicker.updateTime(mHour, mMinutes);
182 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
184 if (preference == mTimePref) {
185 showDialog(DIALOG_TIMEPICKER);
186 } else if (preference == mAlarmOnPref) {
188 } else if (preference == mVibratePref) {
192 return super.onPreferenceTreeClick(preferenceScreen, preference);
195 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
198 mAlarmOnPref.setChecked(true);
203 * Alarms.AlarmSettings implementation. Database feeds current
204 * settings in through this call
206 public void reportAlarm(
207 int idx, boolean enabled, int hour, int minutes,
208 Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label,
211 if (label == null || label.length() == 0) {
212 label = getString(R.string.default_label);
214 mLabel.setText(label);
215 mLabel.setSummary(label);
218 mAlarmOnPref.setChecked(enabled);
219 mDaysOfWeek.set(daysOfWeek);
220 mVibratePref.setChecked(vibrate);
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");
229 mAlarmPref.mAlert = Uri.parse(alert);
230 if (mAlarmPref.mAlert == null) {
231 Log.e("****** Parsed null alarm. URI: " + alert);
234 if (Log.LOGV) Log.v("****** reportAlarm uri " + alert + " alert " +
238 updateAlarm(mAlarmPref.mAlert);
240 mReportAlarmCalled = true;
244 * picks the first alarm available
246 private Uri getDefaultAlarm() {
247 RingtoneManager ringtoneManager = new RingtoneManager(this);
248 ringtoneManager.setType(RingtoneManager.TYPE_ALARM);
249 return ringtoneManager.getRingtoneUri(0);
252 private void updateTime() {
253 if (Log.LOGV) Log.v("updateTime " + mId);
254 mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes, mDaysOfWeek));
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));
265 private void updateRepeat() {
266 if (Log.LOGV) Log.v("updateRepeat " + mId);
267 mRepeatPref.setSummary(mDaysOfWeek.toString(this, true));
270 private void saveAlarm(boolean popToast) {
271 saveAlarm(popToast, mLabel.getText());
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).
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,
288 * Write alarm out to persistent store and pops toast if alarm
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);
298 // Fix alert string first
299 Alarms.setAlarm(context, id, enabled, hour, minute, daysOfWeek, vibrate,
302 if (enabled && popToast) {
303 popAlarmSetToast(context, hour, minute, daysOfWeek);
308 * Display a toast that tells the user how long until the alarm
309 * goes off. This helps prevent "am/pm" mistakes.
311 static void popAlarmSetToast(Context context, int hour, int minute,
312 Alarms.DaysOfWeek daysOfWeek) {
314 String toastText = formatToast(context, hour, minute, daysOfWeek);
315 Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
316 ToastMaster.setToast(toast);
321 * format "Alarm set for 2 days 7 hours and 53 minutes from
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;
334 String daySeq = (days == 0) ? "" :
335 (days == 1) ? context.getString(R.string.day) :
336 context.getString(R.string.days, Long.toString(days));
338 String minSeq = (minutes == 0) ? "" :
339 (minutes == 1) ? context.getString(R.string.minute) :
340 context.getString(R.string.minutes, Long.toString(minutes));
342 String hourSeq = (hours == 0) ? "" :
343 (hours == 1) ? context.getString(R.string.hour) :
344 context.getString(R.string.hours, Long.toString(hours));
346 boolean dispDays = days > 0;
347 boolean dispHour = hours > 0;
348 boolean dispMinute = minutes > 0;
351 if (!(dispDays || dispHour || dispMinute)) {
352 ret = context.getString(R.string.subminute);
354 String parts[] = new String[5];
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);
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); */
372 public boolean onCreateOptionsMenu(Menu menu) {
373 super.onCreateOptionsMenu(menu);
375 mDeleteAlarmItem = menu.add(0, 0, 0, R.string.delete_alarm);
376 mDeleteAlarmItem.setIcon(android.R.drawable.ic_menu_delete);
378 if (AlarmClock.DEBUG) {
379 mTestAlarmItem = menu.add(0, 0, 0, "test alarm");
386 public boolean onOptionsItemSelected(MenuItem item) {
387 if (item == mDeleteAlarmItem) {
388 Alarms.deleteAlarm(this, mId);
392 if (AlarmClock.DEBUG) {
393 if (item == mTestAlarmItem) {
404 * Test code: this is disabled for production build. Sets
405 * this alarm to go off on the next minute
407 void setTestAlarm() {
410 java.util.Calendar c = java.util.Calendar.getInstance();
411 c.setTimeInMillis(System.currentTimeMillis());
413 int nowHour = c.get(java.util.Calendar.HOUR_OF_DAY);
414 int nowMinute = c.get(java.util.Calendar.MINUTE);
416 int minutes = (nowMinute + 1) % 60;
417 int hour = nowHour + (nowMinute == 0? 1 : 0);
419 saveAlarm(this, mId, true, hour, minutes, mDaysOfWeek, true,
420 mLabel.getText(), mAlarmPref.mAlert.toString(), true);