| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.alarmclock; |
| |
| import android.app.Activity; |
| import android.app.Dialog; |
| import android.app.TimePickerDialog; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.ContentObserver; |
| import android.media.Ringtone; |
| import android.media.RingtoneManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.preference.CheckBoxPreference; |
| import android.preference.EditTextPreference; |
| import android.preference.Preference; |
| import android.preference.PreferenceActivity; |
| import android.preference.PreferenceScreen; |
| import android.text.format.DateFormat; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.widget.TimePicker; |
| import android.widget.Toast; |
| |
| /** |
| * Manages each alarm |
| */ |
| public class SetAlarm extends PreferenceActivity |
| implements Alarms.AlarmSettings, TimePickerDialog.OnTimeSetListener { |
| |
| private EditTextPreference mLabel; |
| private CheckBoxPreference mAlarmOnPref; |
| private Preference mTimePref; |
| private AlarmPreference mAlarmPref; |
| private CheckBoxPreference mVibratePref; |
| private RepeatPreference mRepeatPref; |
| private ContentObserver mAlarmsChangeObserver; |
| private MenuItem mDeleteAlarmItem; |
| private MenuItem mTestAlarmItem; |
| |
| private int mId; |
| private int mHour; |
| private int mMinutes; |
| private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek(); |
| |
| private boolean mReportAlarmCalled; |
| |
| private static final int DIALOG_TIMEPICKER = 0; |
| |
| private class RingtoneChangedListener implements AlarmPreference.IRingtoneChangedListener { |
| public void onRingtoneChanged(Uri ringtoneUri) { |
| saveAlarm(false); |
| } |
| } |
| |
| private class OnRepeatChangedObserver implements RepeatPreference.OnRepeatChangedObserver { |
| public void onRepeatChanged(Alarms.DaysOfWeek daysOfWeek) { |
| if (!mDaysOfWeek.equals(daysOfWeek)) { |
| mDaysOfWeek.set(daysOfWeek); |
| saveAlarm(true); |
| } |
| } |
| public Alarms.DaysOfWeek getDaysOfWeek() { |
| return mDaysOfWeek; |
| } |
| } |
| |
| private class AlarmsChangeObserver extends ContentObserver { |
| public AlarmsChangeObserver() { |
| super(new Handler()); |
| } |
| @Override |
| public void onChange(boolean selfChange) { |
| Alarms.getAlarm(getContentResolver(), SetAlarm.this, mId); |
| } |
| } |
| |
| /** |
| * Set an alarm. Requires an Alarms.ID to be passed in as an |
| * extra |
| */ |
| @Override |
| protected void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| |
| addPreferencesFromResource(R.xml.alarm_prefs); |
| mLabel = (EditTextPreference) findPreference("label"); |
| mLabel.setOnPreferenceChangeListener( |
| new Preference.OnPreferenceChangeListener() { |
| public boolean onPreferenceChange(Preference p, |
| Object newValue) { |
| p.setSummary((String) newValue); |
| saveAlarm(false, (String) newValue); |
| return true; |
| } |
| }); |
| mAlarmOnPref = (CheckBoxPreference)findPreference("on"); |
| mTimePref = findPreference("time"); |
| mAlarmPref = (AlarmPreference) findPreference("alarm"); |
| mVibratePref = (CheckBoxPreference) findPreference("vibrate"); |
| mRepeatPref = (RepeatPreference) findPreference("setRepeat"); |
| |
| Intent i = getIntent(); |
| mId = i.getIntExtra(Alarms.ID, -1); |
| if (Log.LOGV) Log.v("In SetAlarm, alarm id = " + mId); |
| |
| mReportAlarmCalled = false; |
| /* load alarm details from database */ |
| Alarms.getAlarm(getContentResolver(), this, mId); |
| /* This should never happen, but does occasionally with the monkey. |
| * I believe it's a race condition where a deleted alarm is opened |
| * before the alarm list is refreshed. */ |
| if (!mReportAlarmCalled) { |
| Log.e("reportAlarm never called!"); |
| finish(); |
| } |
| |
| mAlarmsChangeObserver = new AlarmsChangeObserver(); |
| getContentResolver().registerContentObserver( |
| Alarms.AlarmColumns.CONTENT_URI, true, mAlarmsChangeObserver); |
| |
| mAlarmPref.setRingtoneChangedListener(new RingtoneChangedListener()); |
| mRepeatPref.setOnRepeatChangedObserver(new OnRepeatChangedObserver()); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| getContentResolver().unregisterContentObserver(mAlarmsChangeObserver); |
| } |
| |
| @Override |
| protected Dialog onCreateDialog(int id) { |
| Dialog d; |
| |
| switch (id) { |
| case DIALOG_TIMEPICKER: |
| d = new TimePickerDialog( |
| SetAlarm.this, |
| this, |
| 0, |
| 0, |
| DateFormat.is24HourFormat(SetAlarm.this)); |
| d.setTitle(getResources().getString(R.string.time)); |
| break; |
| default: |
| d = null; |
| } |
| |
| return d; |
| } |
| |
| @Override |
| protected void onPrepareDialog(int id, Dialog dialog) { |
| super.onPrepareDialog(id, dialog); |
| |
| switch (id) { |
| case DIALOG_TIMEPICKER: |
| TimePickerDialog timePicker = (TimePickerDialog)dialog; |
| timePicker.updateTime(mHour, mMinutes); |
| break; |
| } |
| } |
| |
| @Override |
| public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { |
| |
| if (preference == mTimePref) { |
| showDialog(DIALOG_TIMEPICKER); |
| } else if (preference == mAlarmOnPref) { |
| saveAlarm(true); |
| } else if (preference == mVibratePref) { |
| saveAlarm(false); |
| } |
| |
| return super.onPreferenceTreeClick(preferenceScreen, preference); |
| } |
| |
| public void onTimeSet(TimePicker view, int hourOfDay, int minute) { |
| mHour = hourOfDay; |
| mMinutes = minute; |
| mAlarmOnPref.setChecked(true); |
| saveAlarm(true); |
| } |
| |
| /** |
| * Alarms.AlarmSettings implementation. Database feeds current |
| * settings in through this call |
| */ |
| public void reportAlarm( |
| int idx, boolean enabled, int hour, int minutes, |
| Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label, |
| String alert) { |
| |
| if (label == null || label.length() == 0) { |
| label = getString(R.string.default_label); |
| } |
| mLabel.setText(label); |
| mLabel.setSummary(label); |
| mHour = hour; |
| mMinutes = minutes; |
| mAlarmOnPref.setChecked(enabled); |
| mDaysOfWeek.set(daysOfWeek); |
| mVibratePref.setChecked(vibrate); |
| |
| if (alert == null || alert.length() == 0) { |
| if (Log.LOGV) Log.v("****** reportAlarm null or 0-length alert"); |
| mAlarmPref.mAlert = getDefaultAlarm(); |
| if (mAlarmPref.mAlert == null) { |
| Log.e("****** Default Alarm null"); |
| } |
| } else { |
| mAlarmPref.mAlert = Uri.parse(alert); |
| if (mAlarmPref.mAlert == null) { |
| Log.e("****** Parsed null alarm. URI: " + alert); |
| } |
| } |
| if (Log.LOGV) Log.v("****** reportAlarm uri " + alert + " alert " + |
| mAlarmPref.mAlert); |
| updateTime(); |
| updateRepeat(); |
| updateAlarm(mAlarmPref.mAlert); |
| |
| mReportAlarmCalled = true; |
| } |
| |
| /** |
| * picks the first alarm available |
| */ |
| private Uri getDefaultAlarm() { |
| RingtoneManager ringtoneManager = new RingtoneManager(this); |
| ringtoneManager.setType(RingtoneManager.TYPE_ALARM); |
| return ringtoneManager.getRingtoneUri(0); |
| } |
| |
| private void updateTime() { |
| if (Log.LOGV) Log.v("updateTime " + mId); |
| mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes, mDaysOfWeek)); |
| } |
| |
| private void updateAlarm(Uri ringtoneUri) { |
| if (Log.LOGV) Log.v("updateAlarm " + mId); |
| Ringtone ringtone = RingtoneManager.getRingtone(SetAlarm.this, ringtoneUri); |
| if (ringtone != null) { |
| mAlarmPref.setSummary(ringtone.getTitle(SetAlarm.this)); |
| } |
| } |
| |
| private void updateRepeat() { |
| if (Log.LOGV) Log.v("updateRepeat " + mId); |
| mRepeatPref.setSummary(mDaysOfWeek.toString(this, true)); |
| } |
| |
| private void saveAlarm(boolean popToast) { |
| saveAlarm(popToast, mLabel.getText()); |
| } |
| |
| /** |
| * This version of saveAlarm uses the passed in label since mLabel may |
| * contain the old value (i.e. during the preference value change). |
| */ |
| private void saveAlarm(boolean popToast, String label) { |
| if (mReportAlarmCalled && mAlarmPref.mAlert != null) { |
| String alertString = mAlarmPref.mAlert.toString(); |
| saveAlarm(this, mId, mAlarmOnPref.isChecked(), mHour, mMinutes, |
| mDaysOfWeek, mVibratePref.isChecked(), label, alertString, |
| popToast); |
| } |
| } |
| |
| /** |
| * Write alarm out to persistent store and pops toast if alarm |
| * enabled |
| */ |
| private static void saveAlarm( |
| Context context, int id, boolean enabled, int hour, int minute, |
| Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label, |
| String alert, boolean popToast) { |
| if (Log.LOGV) Log.v("** saveAlarm " + id + " " + label + " " + enabled |
| + " " + hour + " " + minute + " vibe " + vibrate); |
| |
| // Fix alert string first |
| Alarms.setAlarm(context, id, enabled, hour, minute, daysOfWeek, vibrate, |
| label, alert); |
| |
| if (enabled && popToast) { |
| popAlarmSetToast(context, hour, minute, daysOfWeek); |
| } |
| } |
| |
| /** |
| * Display a toast that tells the user how long until the alarm |
| * goes off. This helps prevent "am/pm" mistakes. |
| */ |
| static void popAlarmSetToast(Context context, int hour, int minute, |
| Alarms.DaysOfWeek daysOfWeek) { |
| |
| String toastText = formatToast(context, hour, minute, daysOfWeek); |
| Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG); |
| ToastMaster.setToast(toast); |
| toast.show(); |
| } |
| |
| /** |
| * format "Alarm set for 2 days 7 hours and 53 minutes from |
| * now" |
| */ |
| static String formatToast(Context context, int hour, int minute, |
| Alarms.DaysOfWeek daysOfWeek) { |
| long alarm = Alarms.calculateAlarm(hour, minute, |
| daysOfWeek).getTimeInMillis(); |
| long delta = alarm - System.currentTimeMillis();; |
| long hours = delta / (1000 * 60 * 60); |
| long minutes = delta / (1000 * 60) % 60; |
| long days = hours / 24; |
| hours = hours % 24; |
| |
| String daySeq = (days == 0) ? "" : |
| (days == 1) ? context.getString(R.string.day) : |
| context.getString(R.string.days, Long.toString(days)); |
| |
| String minSeq = (minutes == 0) ? "" : |
| (minutes == 1) ? context.getString(R.string.minute) : |
| context.getString(R.string.minutes, Long.toString(minutes)); |
| |
| String hourSeq = (hours == 0) ? "" : |
| (hours == 1) ? context.getString(R.string.hour) : |
| context.getString(R.string.hours, Long.toString(hours)); |
| |
| boolean dispDays = days > 0; |
| boolean dispHour = hours > 0; |
| boolean dispMinute = minutes > 0; |
| |
| String ret; |
| if (!(dispDays || dispHour || dispMinute)) { |
| ret = context.getString(R.string.subminute); |
| } else { |
| String parts[] = new String[5]; |
| parts[0] = daySeq; |
| parts[1] = !dispDays ? "" : |
| dispHour && dispMinute ? context.getString(R.string.space) : |
| !dispHour && !dispMinute ? "" : |
| context.getString(R.string.and); |
| parts[2] = dispHour ? hourSeq : ""; |
| parts[3] = dispHour && dispMinute ? context.getString(R.string.and) : ""; |
| parts[4] = dispMinute ? minSeq : ""; |
| ret = context.getString(R.string.combiner, (Object[])parts); |
| } |
| |
| ret = context.getString(R.string.alarm_set, ret); |
| /* if (Log.LOGV) Log.v("** TOAST daySeq " + daySeq + " hourSeq " + hourSeq + |
| " minSeq " + minSeq + " ret " + ret); */ |
| return ret; |
| } |
| |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| |
| mDeleteAlarmItem = menu.add(0, 0, 0, R.string.delete_alarm); |
| mDeleteAlarmItem.setIcon(android.R.drawable.ic_menu_delete); |
| |
| if (AlarmClock.DEBUG) { |
| mTestAlarmItem = menu.add(0, 0, 0, "test alarm"); |
| } |
| |
| |
| return true; |
| } |
| |
| public boolean onOptionsItemSelected(MenuItem item) { |
| if (item == mDeleteAlarmItem) { |
| Alarms.deleteAlarm(this, mId); |
| finish(); |
| return true; |
| } |
| if (AlarmClock.DEBUG) { |
| if (item == mTestAlarmItem) { |
| setTestAlarm(); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| /** |
| * Test code: this is disabled for production build. Sets |
| * this alarm to go off on the next minute |
| */ |
| void setTestAlarm() { |
| |
| // start with now |
| java.util.Calendar c = java.util.Calendar.getInstance(); |
| c.setTimeInMillis(System.currentTimeMillis()); |
| |
| int nowHour = c.get(java.util.Calendar.HOUR_OF_DAY); |
| int nowMinute = c.get(java.util.Calendar.MINUTE); |
| |
| int minutes = (nowMinute + 1) % 60; |
| int hour = nowHour + (nowMinute == 0? 1 : 0); |
| |
| saveAlarm(this, mId, true, hour, minutes, mDaysOfWeek, true, |
| mLabel.getText(), mAlarmPref.mAlert.toString(), true); |
| } |
| |
| } |