<receiver android:name="AlarmReceiver">
<intent-filter>
<action android:name="com.android.alarmclock.ALARM_ALERT" />
+ <action android:name="alarm_killed" />
+ <action android:name="cancel_snooze" />
</intent-filter>
</receiver>
+ <!-- This service receives the same intent as AlarmReceiver but it does
+ not respond to the same broadcast. The AlarmReceiver will receive
+ the alert broadcast and will start this service with the same
+ intent. The service plays the alarm alert and vibrates the device.
+ This allows the alert to continue playing even if another activity
+ causes the AlarmAlert activity to pause. -->
+ <service android:name="AlarmKlaxon">
+ <intent-filter>
+ <action android:name="com.android.alarmclock.ALARM_ALERT" />
+ </intent-filter>
+ </service>
+
<receiver android:name="AlarmInitReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</LinearLayout>
- <TextView android:id="@+id/silencedText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="2dp"
- android:paddingBottom="2dp"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:visibility="gone"
- android:gravity="center"
- android:textColor="@color/red"/>
-
</LinearLayout>
+
</LinearLayout>
<string name="alarm_repeat">"Opakovat"</string>
<string name="alert">"Vyzváněcí tón"</string>
<string name="time">"Čas"</string>
- <string name="alert_title">"Upozornění"</string>
<string name="alarm_alert_dismiss_text">"Zavřít"</string>
<string name="alarm_alert_alert_silenced">"Upozornění ztišeno po uplynutí <xliff:g id="MINUTES">%d</xliff:g> min."</string>
<string name="alarm_alert_snooze_text">"Odložit"</string>
<string name="alarm_alert_snooze_set">"Upozornění odloženo o <xliff:g id="MINUTES">%d</xliff:g> min."</string>
- <string name="alarm_alert_snooze_not_set">"Upozornění nebylo odloženo – další upozornění v <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="alarm_set">"Tento budík bude aktivován za: <xliff:g id="TIME_DELTA">%s</xliff:g>."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 den"</string>
<string name="alarm_repeat">"Wiederholen"</string>
<string name="alert">"Klingelton"</string>
<string name="time">"Uhrzeit"</string>
- <string name="alert_title">"Wecker"</string>
<string name="alarm_alert_dismiss_text">"Verwerfen"</string>
<string name="alarm_alert_alert_silenced">"Der Wecker verstummte nach <xliff:g id="MINUTES">%d</xliff:g> Minuten"</string>
<string name="alarm_alert_snooze_text">"Snooze-Funktion"</string>
<string name="alarm_alert_snooze_set">"Snooze-Funktion aktiviert für <xliff:g id="MINUTES">%d</xliff:g> Minuten"</string>
- <string name="alarm_alert_snooze_not_set">"Snooze-Funktion nicht aktiviert - nächster Wecker klingelt <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="alarm_set">"Dieser Wecker klingelt in <xliff:g id="TIME_DELTA">%s</xliff:g>."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 Tag"</string>
<string name="alarm_repeat">"Repetir"</string>
<string name="alert">"Timbre"</string>
<string name="time">"Hora"</string>
- <string name="alert_title">"Alarma"</string>
<string name="alarm_alert_dismiss_text">"Descartar"</string>
<string name="alarm_alert_alert_silenced">"Alarma silenciada después de <xliff:g id="MINUTES">%d</xliff:g> minutos"</string>
<string name="alarm_alert_snooze_text">"Recurrente"</string>
<string name="alarm_alert_snooze_set">"Recurrente a los <xliff:g id="MINUTES">%d</xliff:g> minutos."</string>
- <string name="alarm_alert_snooze_not_set">"Alarma recurrente no activada. Próxima alarma fijada a las <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Esta alarma se activará en <xliff:g id="TIME_DELTA">%s</xliff:g> a partir de ahora."</string>
<string name="combiner">"Segmento <xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"un día"</string>
<string name="alarm_repeat">"Repetir"</string>
<string name="alert">"Tono"</string>
<string name="time">"Hora"</string>
- <string name="alert_title">"Alarma"</string>
<string name="alarm_alert_dismiss_text">"Descartar"</string>
<string name="alarm_alert_alert_silenced">"La alarma se ha silenciado después de <xliff:g id="MINUTES">%d</xliff:g> minutos."</string>
<string name="alarm_alert_snooze_text">"Posponer"</string>
<string name="alarm_alert_snooze_set">"La alarma volverá a sonar en <xliff:g id="MINUTES">%d</xliff:g> minutos."</string>
- <string name="alarm_alert_snooze_not_set">"No se ha pospuesto la alarma, ya que la siguiente alarma sonará a las <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="alarm_set">"La alarma sonará en <xliff:g id="TIME_DELTA">%s</xliff:g>."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 día"</string>
<string name="alarm_repeat">"Répéter"</string>
<string name="alert">"Sonnerie"</string>
<string name="time">"Heure"</string>
- <string name="alert_title">"Alarme"</string>
<string name="alarm_alert_dismiss_text">"Quitter"</string>
<string name="alarm_alert_alert_silenced">"Alarme interrompue après <xliff:g id="MINUTES">%d</xliff:g> minutes"</string>
<string name="alarm_alert_snooze_text">"Répéter"</string>
<string name="alarm_alert_snooze_set">"Répétition dans <xliff:g id="MINUTES">%d</xliff:g> minutes."</string>
- <string name="alarm_alert_snooze_not_set">"Répétition non activée : prochaine alarme à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Prochaine alarme dans <xliff:g id="TIME_DELTA">%s</xliff:g>."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 jour"</string>
<string name="alarm_repeat">"Ripeti"</string>
<string name="alert">"Suoneria"</string>
<string name="time">"Ora"</string>
- <string name="alert_title">"Allarme"</string>
<string name="alarm_alert_dismiss_text">"Spegni"</string>
<string name="alarm_alert_alert_silenced">"Tono allarme disattivato dopo <xliff:g id="MINUTES">%d</xliff:g> minuti"</string>
<string name="alarm_alert_snooze_text">"Posponi"</string>
<string name="alarm_alert_snooze_set">"Sospensione per <xliff:g id="MINUTES">%d</xliff:g> minuti."</string>
- <string name="alarm_alert_snooze_not_set">"Sospensione non impostata. Prossimo allarme alle <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"L\'allarme sarà attivato fra <xliff:g id="TIME_DELTA">%s</xliff:g>."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 giorno"</string>
<string name="alarm_repeat">"繰り返し"</string>
<string name="alert">"アラーム音"</string>
<string name="time">"時刻"</string>
- <string name="alert_title">"アラーム"</string>
<string name="alarm_alert_dismiss_text">"停止"</string>
<string name="alarm_alert_alert_silenced">"アラームは<xliff:g id="MINUTES">%d</xliff:g>分間鳴って止まりました"</string>
<string name="alarm_alert_snooze_text">"スヌーズ"</string>
<string name="alarm_alert_snooze_set">"<xliff:g id="MINUTES">%d</xliff:g>分後に再通知します"</string>
- <string name="alarm_alert_snooze_not_set">"スヌーズにできません: <xliff:g id="TIME">%s</xliff:g>に次のアラームが設定されています"</string>
<string name="alarm_set">"今から<xliff:g id="TIME_DELTA">%s</xliff:g>後に設定しました"</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1日"</string>
<string name="alarm_repeat">"반복"</string>
<string name="alert">"벨소리"</string>
<string name="time">"시간"</string>
- <string name="alert_title">"알람"</string>
<string name="alarm_alert_dismiss_text">"해제"</string>
<string name="alarm_alert_alert_silenced">"<xliff:g id="MINUTES">%d</xliff:g>분 후에 알람이 꺼집니다."</string>
<string name="alarm_alert_snooze_text">"스누즈"</string>
<string name="alarm_alert_snooze_set">"<xliff:g id="MINUTES">%d</xliff:g>분 동안 스누즈"</string>
- <string name="alarm_alert_snooze_not_set">"스누즈 설정되지 않음. <xliff:g id="TIME">%s</xliff:g>에 다음 알람이 설정되어 있음."</string>
<string name="alarm_set">"이 알람은 지금부터 <xliff:g id="TIME_DELTA">%s</xliff:g> 동안 설정됩니다."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1일"</string>
<string name="alarm_repeat">"Gjenta"</string>
<string name="alert">"Ringetone"</string>
<string name="time">"Tidspunkt"</string>
- <string name="alert_title">"Alarm"</string>
<string name="alarm_alert_dismiss_text">"OK"</string>
<string name="alarm_alert_alert_silenced">"Alarmen ble stilnet etter <xliff:g id="MINUTES">%d</xliff:g> minutter"</string>
<string name="alarm_alert_snooze_text">"Slumre"</string>
<string name="alarm_alert_snooze_set">"Slumrer i <xliff:g id="MINUTES">%d</xliff:g> minutter."</string>
- <string name="alarm_alert_snooze_not_set">"Slumrer ikke -- neste alarm vil gå av <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Alarmen vil gå av <xliff:g id="TIME_DELTA">%s</xliff:g> fra nå."</string>
<!-- no translation found for combiner (3170916241487451546) -->
<skip />
<string name="alarm_repeat">"Herhalen"</string>
<string name="alert">"Beltoon"</string>
<string name="time">"Tijd"</string>
- <string name="alert_title">"Alarm"</string>
<string name="alarm_alert_dismiss_text">"Negeren"</string>
<string name="alarm_alert_alert_silenced">"Alarm gaat uit na <xliff:g id="MINUTES">%d</xliff:g> minuten"</string>
<string name="alarm_alert_snooze_text">"Snooze"</string>
<string name="alarm_alert_snooze_set">"Snooze is ingesteld op <xliff:g id="MINUTES">%d</xliff:g> minuten."</string>
- <string name="alarm_alert_snooze_not_set">"Snooze is niet ingesteld - volgende alarm is ingesteld op <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Dit alarm is ingesteld op <xliff:g id="TIME_DELTA">%s</xliff:g> vanaf nu."</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 dag"</string>
<string name="alarm_repeat">"Powtarzanie"</string>
<string name="alert">"Dzwonek"</string>
<string name="time">"Godzina"</string>
- <string name="alert_title">"Alarm"</string>
<string name="alarm_alert_dismiss_text">"Zamknij"</string>
<string name="alarm_alert_alert_silenced">"Alarm jest wyciszany po <xliff:g id="MINUTES">%d</xliff:g> min."</string>
<string name="alarm_alert_snooze_text">"Drzemka"</string>
<string name="alarm_alert_snooze_set">"Drzemka przez <xliff:g id="MINUTES">%d</xliff:g> min."</string>
- <string name="alarm_alert_snooze_not_set">"Nie ustawiono drzemki – następny alarm: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Ustawiony alarm zadzwoni za <xliff:g id="TIME_DELTA">%s</xliff:g>"</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 dzień"</string>
<string name="alarm_repeat">"Repetir"</string>
<string name="alert">"Ringtone"</string>
<string name="time">"Hora"</string>
- <string name="alert_title">"Alarme"</string>
<string name="alarm_alert_dismiss_text">"Descartar"</string>
<string name="alarm_alert_alert_silenced">"Alarme silenciado após <xliff:g id="MINUTES">%d</xliff:g> minutos"</string>
<string name="alarm_alert_snooze_text">"Soneca"</string>
<string name="alarm_alert_snooze_set">"Soneca de <xliff:g id="MINUTES">%d</xliff:g> minutos."</string>
- <string name="alarm_alert_snooze_not_set">"Soneca não definida - próximo alarme definido para <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Este alarme está definido para <xliff:g id="TIME_DELTA">%s</xliff:g> a partir de agora."</string>
<!-- no translation found for combiner (3170916241487451546) -->
<skip />
<string name="alarm_repeat">"Повтор"</string>
<string name="alert">"Мелодия звонка"</string>
<string name="time">"Время"</string>
- <string name="alert_title">"Будильник"</string>
<string name="alarm_alert_dismiss_text">"Закрыть"</string>
<string name="alarm_alert_alert_silenced">"Будильник отключается через <xliff:g id="MINUTES">%d</xliff:g> минут(ы)"</string>
<string name="alarm_alert_snooze_text">"Отсрочка"</string>
<string name="alarm_alert_snooze_set">"Отсрочка на <xliff:g id="MINUTES">%d</xliff:g> минут(ы)."</string>
- <string name="alarm_alert_snooze_not_set">"Отсрочка не установлена – следующий будильник установлен на <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"Будильник прозвонит через <xliff:g id="TIME_DELTA">%s</xliff:g>"</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 день"</string>
<string name="alarm_repeat">"重复"</string>
<string name="alert">"铃声"</string>
<string name="time">"时间"</string>
- <string name="alert_title">"闹钟"</string>
<string name="alarm_alert_dismiss_text">"取消"</string>
<string name="alarm_alert_alert_silenced">"闹钟在 <xliff:g id="MINUTES">%d</xliff:g> 分钟后静音"</string>
<string name="alarm_alert_snooze_text">"再响"</string>
<string name="alarm_alert_snooze_set">"<xliff:g id="MINUTES">%d</xliff:g> 分钟后再响。"</string>
- <string name="alarm_alert_snooze_not_set">"未设置再响 - 下次闹钟设置在 <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"该闹钟设置为从现在起 <xliff:g id="TIME_DELTA">%s</xliff:g> 后再提醒。"</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 天"</string>
<string name="alarm_repeat">"重複"</string>
<string name="alert">"鈴聲"</string>
<string name="time">"時間"</string>
- <string name="alert_title">"鬧鐘"</string>
<string name="alarm_alert_dismiss_text">"關閉"</string>
<string name="alarm_alert_alert_silenced">"鬧鐘 <xliff:g id="MINUTES">%d</xliff:g> 分鐘後靜音"</string>
<string name="alarm_alert_snooze_text">"貪睡"</string>
<string name="alarm_alert_snooze_set">"再貪睡 <xliff:g id="MINUTES">%d</xliff:g> 分鐘。"</string>
- <string name="alarm_alert_snooze_not_set">"未設定貪睡鬧鐘 -- 下一次鬧鐘時間為 <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="alarm_set">"還有 <xliff:g id="TIME_DELTA">%s</xliff:g> 會啟動鬧鐘。"</string>
<string name="combiner">"<xliff:g id="XXX_0">%1$s</xliff:g><xliff:g id="XXX_1">%2$s</xliff:g><xliff:g id="XXX_2">%3$s</xliff:g><xliff:g id="XXX_3">%4$s</xliff:g><xliff:g id="XXX_4">%5$s</xliff:g>"</string>
<string name="day">"1 天"</string>
<!-- Setting labels on Set alarm screen: Set time -->
<string name="time">Time</string>
-
- <!-- Title of the alarm alert -->
- <string name="alert_title">Alarm</string>
-
<!-- Button labels on the alarm dialog: Dismiss -->
<string name="alarm_alert_dismiss_text">Dismiss</string>
dialog. Says the alarm will snooze for xxx minutes. -->
<string name="alarm_alert_snooze_set">Snoozing for <xliff:g id="minutes">%d</xliff:g> minutes.</string>
- <!-- Toast that appears after Alarm is snoozed from the Alarm
- dialog. Shown if snooze cannot be set because the next alarm
- would fire before the snooze alrm. "Snooze not set - next
- alarm set for xxx." -->
- <string name="alarm_alert_snooze_not_set">Snooze not set -- next alarm set for <xliff:g id="time">%s</xliff:g></string>
-
-
<!-- Alarm confirmation toast: Describes how long from now until
alarm fires -->
<string name="alarm_set">This alarm is set for <xliff:g id="time_delta">%s</xliff:g> from now.</string>
<!-- Summary for the alarm preference when silent is chosen. -->
<string name="silent_alarm_summary">Silent</string>
+
+ <!-- Text to display in the small text of the notification -->
+ <string name="alarm_notify_text">Select to snooze or dismiss this alarm.</string>
+
+ <!-- Text to display in the notification ticker and label -->
+ <string name="alarm_notify_snooze_label"><xliff:g id="label">%s</xliff:g>
+ (snoozed)</string>
+
+ <!-- Text to display in the notification when the alarm has been snoozed -->
+ <string name="alarm_notify_snooze_text">Alarm set for <xliff:g
+ id="time">%s</xliff:g>. Click to cancel.</string>
</resources>
--- /dev/null
+/*
+ * Copyright (C) 2009 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.content.Context;
+import android.database.Cursor;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.BaseColumns;
+import android.text.format.DateFormat;
+
+import java.text.DateFormatSymbols;
+import java.util.Calendar;
+
+public final class Alarm implements Parcelable {
+
+ //////////////////////////////
+ // Parcelable apis
+ //////////////////////////////
+ public static final Parcelable.Creator<Alarm> CREATOR
+ = new Parcelable.Creator<Alarm>() {
+ public Alarm createFromParcel(Parcel p) {
+ return new Alarm(p);
+ }
+
+ public Alarm[] newArray(int size) {
+ return new Alarm[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel p, int flags) {
+ p.writeInt(id);
+ p.writeInt(enabled ? 1 : 0);
+ p.writeInt(hour);
+ p.writeInt(minutes);
+ p.writeInt(daysOfWeek.getCoded());
+ p.writeLong(time);
+ p.writeInt(vibrate ? 1 : 0);
+ p.writeString(label);
+ p.writeParcelable(alert, flags);
+ p.writeInt(silent ? 1 : 0);
+ }
+ //////////////////////////////
+ // end Parcelable apis
+ //////////////////////////////
+
+ //////////////////////////////
+ // Column definitions
+ //////////////////////////////
+ public static class Columns implements BaseColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://com.android.alarmclock/alarm");
+
+ /**
+ * Hour in 24-hour localtime 0 - 23.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String HOUR = "hour";
+
+ /**
+ * Minutes in localtime 0 - 59
+ * <P>Type: INTEGER</P>
+ */
+ public static final String MINUTES = "minutes";
+
+ /**
+ * Days of week coded as integer
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DAYS_OF_WEEK = "daysofweek";
+
+ /**
+ * Alarm time in UTC milliseconds from the epoch.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String ALARM_TIME = "alarmtime";
+
+ /**
+ * True if alarm is active
+ * <P>Type: BOOLEAN</P>
+ */
+ public static final String ENABLED = "enabled";
+
+ /**
+ * True if alarm should vibrate
+ * <P>Type: BOOLEAN</P>
+ */
+ public static final String VIBRATE = "vibrate";
+
+ /**
+ * Message to show when alarm triggers
+ * Note: not currently used
+ * <P>Type: STRING</P>
+ */
+ public static final String MESSAGE = "message";
+
+ /**
+ * Audio alert to play when alarm triggers
+ * <P>Type: STRING</P>
+ */
+ public static final String ALERT = "alert";
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = _ID + " ASC";
+
+ // Used when filtering enabled alarms.
+ public static final String WHERE_ENABLED = ENABLED + "=1";
+
+ static final String[] ALARM_QUERY_COLUMNS = {
+ _ID, HOUR, MINUTES, DAYS_OF_WEEK, ALARM_TIME,
+ ENABLED, VIBRATE, MESSAGE, ALERT };
+
+ /**
+ * These save calls to cursor.getColumnIndexOrThrow()
+ * THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY COLUMNS
+ */
+ public static final int ALARM_ID_INDEX = 0;
+ public static final int ALARM_HOUR_INDEX = 1;
+ public static final int ALARM_MINUTES_INDEX = 2;
+ public static final int ALARM_DAYS_OF_WEEK_INDEX = 3;
+ public static final int ALARM_TIME_INDEX = 4;
+ public static final int ALARM_ENABLED_INDEX = 5;
+ public static final int ALARM_VIBRATE_INDEX = 6;
+ public static final int ALARM_MESSAGE_INDEX = 7;
+ public static final int ALARM_ALERT_INDEX = 8;
+ }
+ //////////////////////////////
+ // End column definitions
+ //////////////////////////////
+
+ // Public fields
+ public int id;
+ public boolean enabled;
+ public int hour;
+ public int minutes;
+ public DaysOfWeek daysOfWeek;
+ public long time;
+ public boolean vibrate;
+ public String label;
+ public Uri alert;
+ public boolean silent;
+
+ public Alarm(Cursor c) {
+ id = c.getInt(Columns.ALARM_ID_INDEX);
+ enabled = c.getInt(Columns.ALARM_ENABLED_INDEX) == 1;
+ hour = c.getInt(Columns.ALARM_HOUR_INDEX);
+ minutes = c.getInt(Columns.ALARM_MINUTES_INDEX);
+ daysOfWeek = new DaysOfWeek(c.getInt(Columns.ALARM_DAYS_OF_WEEK_INDEX));
+ time = c.getLong(Columns.ALARM_TIME_INDEX);
+ vibrate = c.getInt(Columns.ALARM_VIBRATE_INDEX) == 1;
+ label = c.getString(Columns.ALARM_MESSAGE_INDEX);
+ String alertString = c.getString(Columns.ALARM_ALERT_INDEX);
+ if (Alarms.ALARM_ALERT_SILENT.equals(alertString)) {
+ if (Log.LOGV) {
+ Log.v("Alarm is marked as silent");
+ }
+ silent = true;
+ } else {
+ if (alertString != null && alertString.length() != 0) {
+ alert = Uri.parse(alertString);
+ }
+
+ // If the database alert is null or it failed to parse, use the
+ // default alert.
+ if (alert == null) {
+ alert = RingtoneManager.getDefaultUri(
+ RingtoneManager.TYPE_ALARM);
+ }
+ }
+ }
+
+ public Alarm(Parcel p) {
+ id = p.readInt();
+ enabled = p.readInt() == 1;
+ hour = p.readInt();
+ minutes = p.readInt();
+ daysOfWeek = new DaysOfWeek(p.readInt());
+ time = p.readLong();
+ vibrate = p.readInt() == 1;
+ label = p.readString();
+ alert = (Uri) p.readParcelable(null);
+ silent = p.readInt() == 1;
+ }
+
+ public String getLabelOrDefault(Context context) {
+ if (label == null || label.length() == 0) {
+ return context.getString(R.string.default_label);
+ }
+ return label;
+ }
+
+ /*
+ * Days of week code as a single int.
+ * 0x00: no day
+ * 0x01: Monday
+ * 0x02: Tuesday
+ * 0x04: Wednesday
+ * 0x08: Thursday
+ * 0x10: Friday
+ * 0x20: Saturday
+ * 0x40: Sunday
+ */
+ static final class DaysOfWeek {
+
+ private static int[] DAY_MAP = new int[] {
+ Calendar.MONDAY,
+ Calendar.TUESDAY,
+ Calendar.WEDNESDAY,
+ Calendar.THURSDAY,
+ Calendar.FRIDAY,
+ Calendar.SATURDAY,
+ Calendar.SUNDAY,
+ };
+
+ // Bitmask of all repeating days
+ private int mDays;
+
+ DaysOfWeek(int days) {
+ mDays = days;
+ }
+
+ public String toString(Context context, boolean showNever) {
+ StringBuilder ret = new StringBuilder();
+
+ // no days
+ if (mDays == 0) {
+ return showNever ?
+ context.getText(R.string.never).toString() : "";
+ }
+
+ // every day
+ if (mDays == 0x7f) {
+ return context.getText(R.string.every_day).toString();
+ }
+
+ // count selected days
+ int dayCount = 0, days = mDays;
+ while (days > 0) {
+ if ((days & 1) == 1) dayCount++;
+ days >>= 1;
+ }
+
+ // short or long form?
+ DateFormatSymbols dfs = new DateFormatSymbols();
+ String[] dayList = (dayCount > 1) ?
+ dfs.getShortWeekdays() :
+ dfs.getWeekdays();
+
+ // selected days
+ for (int i = 0; i < 7; i++) {
+ if ((mDays & (1 << i)) != 0) {
+ ret.append(dayList[DAY_MAP[i]]);
+ dayCount -= 1;
+ if (dayCount > 0) ret.append(
+ context.getText(R.string.day_concat));
+ }
+ }
+ return ret.toString();
+ }
+
+ private boolean isSet(int day) {
+ return ((mDays & (1 << day)) > 0);
+ }
+
+ public void set(int day, boolean set) {
+ if (set) {
+ mDays |= (1 << day);
+ } else {
+ mDays &= ~(1 << day);
+ }
+ }
+
+ public void set(DaysOfWeek dow) {
+ mDays = dow.mDays;
+ }
+
+ public int getCoded() {
+ return mDays;
+ }
+
+ // Returns days of week encoded in an array of booleans.
+ public boolean[] getBooleanArray() {
+ boolean[] ret = new boolean[7];
+ for (int i = 0; i < 7; i++) {
+ ret[i] = isSet(i);
+ }
+ return ret;
+ }
+
+ public boolean isRepeatSet() {
+ return mDays != 0;
+ }
+
+ /**
+ * returns number of days from today until next alarm
+ * @param c must be set to today
+ */
+ public int getNextAlarm(Calendar c) {
+ if (mDays == 0) {
+ return -1;
+ }
+
+ int today = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
+
+ int day = 0;
+ int dayCount = 0;
+ for (; dayCount < 7; dayCount++) {
+ day = (today + dayCount) % 7;
+ if (isSet(day)) {
+ break;
+ }
+ }
+ return dayCount;
+ }
+ }
+}
package com.android.alarmclock;
import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.BroadcastReceiver;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
public class AlarmAlert extends Activity {
private static final String DEFAULT_SNOOZE = "10";
- private static final int UNKNOWN = 0;
- private static final int SNOOZE = 1;
- private static final int DISMISS = 2;
- private static final int KILLED = 3;
- private Button mSnoozeButton;
- private int mState = UNKNOWN;
-
- private AlarmKlaxon mKlaxon;
- private int mAlarmId;
- private String mLabel;
+ private Alarm mAlarm;
+
+ // Receives the ALARM_KILLED action from the AlarmKlaxon.
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Alarm alarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
+ if (mAlarm.id == alarm.id) {
+ dismiss(true);
+ }
+ }
+ };
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- // Maintain a lock during the playback of the alarm. This lock may have
- // already been acquired in AlarmReceiver. If the process was killed,
- // the global wake lock is gone. Acquire again just to be sure.
- AlarmAlertWakeLock.acquireCpuWakeLock(this);
-
- /* FIXME Intentionally verbose: always log this until we've
- fully debugged the app failing to start up */
- Log.v("AlarmAlert.onCreate()");
-
- Intent i = getIntent();
- mAlarmId = i.getIntExtra(Alarms.ID, -1);
-
- mKlaxon = new AlarmKlaxon();
- mKlaxon.postPlay(this, mAlarmId);
-
- /* allow next alarm to trigger while this activity is
- active */
- Alarms.disableSnoozeAlert(AlarmAlert.this);
- Alarms.disableAlert(AlarmAlert.this, mAlarmId);
- Alarms.setNextAlert(this);
-
- mKlaxon.setKillerCallback(new AlarmKlaxon.KillerCallback() {
- public void onKilled() {
- if (Log.LOGV) Log.v("onKilled()");
- updateSilencedText();
-
- /* don't allow snooze */
- mSnoozeButton.setEnabled(false);
-
- // Dismiss the alarm but mark the state as killed so if the
- // config changes, we show the silenced message and disable
- // snooze.
- dismiss();
- mState = KILLED;
- }
- });
+ mAlarm = getIntent().getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
updateLayout();
- }
- private void setTitleFromIntent(Intent i) {
- mLabel = i.getStringExtra(Alarms.LABEL);
- if (mLabel == null || mLabel.length() == 0) {
- mLabel = getString(R.string.default_label);
- }
- TextView title = (TextView) findViewById(R.id.alertTitle);
- title.setText(mLabel);
+ // Register to get the alarm killed intent.
+ registerReceiver(mReceiver, new IntentFilter(Alarms.ALARM_KILLED));
}
- private void updateSilencedText() {
- TextView silenced = (TextView) findViewById(R.id.silencedText);
- silenced.setText(getString(R.string.alarm_alert_alert_silenced,
- AlarmKlaxon.ALARM_TIMEOUT_SECONDS / 60));
- silenced.setVisibility(View.VISIBLE);
+ private void setTitle() {
+ String label = mAlarm.getLabelOrDefault(this);
+ TextView title = (TextView) findViewById(R.id.alertTitle);
+ title.setText(label);
}
// This method is overwritten in AlarmAlertFullScreen in order to show a
/* snooze behavior: pop a snooze confirmation view, kick alarm
manager. */
- mSnoozeButton = (Button) findViewById(R.id.snooze);
- mSnoozeButton.requestFocus();
- // If this was a configuration change, keep the silenced text if the
- // alarm was killed.
- if (mState == KILLED) {
- updateSilencedText();
- mSnoozeButton.setEnabled(false);
- } else {
- mSnoozeButton.setOnClickListener(new Button.OnClickListener() {
- public void onClick(View v) {
- snooze();
- finish();
- }
- });
- }
+ Button snooze = (Button) findViewById(R.id.snooze);
+ snooze.requestFocus();
+ snooze.setOnClickListener(new Button.OnClickListener() {
+ public void onClick(View v) {
+ snooze();
+ }
+ });
/* dismiss button: close notification */
findViewById(R.id.dismiss).setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
- dismiss();
- finish();
+ dismiss(false);
}
});
- /* Set the title from the passed in label */
- setTitleFromIntent(getIntent());
+ /* Set the title from the passed in alarm */
+ setTitle();
}
// Attempt to snooze this alert.
private void snooze() {
- if (mState != UNKNOWN) {
- return;
- }
- // If the next alarm is set for sooner than the snooze interval, don't
- // snooze. Instead, toast the user that the snooze will not be set.
final String snooze =
PreferenceManager.getDefaultSharedPreferences(this)
.getString("snooze_duration", DEFAULT_SNOOZE);
final long snoozeTime = System.currentTimeMillis()
+ (1000 * 60 * snoozeMinutes);
- final long nextAlarm =
- Alarms.calculateNextAlert(AlarmAlert.this).getAlert();
- String displayTime = null;
- if (nextAlarm < snoozeTime) {
- final Calendar c = Calendar.getInstance();
- c.setTimeInMillis(nextAlarm);
- displayTime = getString(R.string.alarm_alert_snooze_not_set,
- Alarms.formatTime(AlarmAlert.this, c));
- mState = DISMISS;
- } else {
- Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarmId, snoozeTime,
- mLabel);
- Alarms.setNextAlert(AlarmAlert.this);
- displayTime = getString(R.string.alarm_alert_snooze_set,
- snoozeMinutes);
- mState = SNOOZE;
- }
+ Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarm.id, snoozeTime);
+
+ // Get the display time for the snooze and update the notification.
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(snoozeTime);
+
+ // Append (snoozed) to the label.
+ String label = mAlarm.getLabelOrDefault(this);
+ label = getString(R.string.alarm_notify_snooze_label, label);
+
+ // Notify the user that the alarm has been snoozed.
+ Intent cancelSnooze = new Intent(this, AlarmReceiver.class);
+ cancelSnooze.setAction(Alarms.CANCEL_SNOOZE);
+ cancelSnooze.putExtra(Alarms.ALARM_ID, mAlarm.id);
+ PendingIntent broadcast =
+ PendingIntent.getBroadcast(this, mAlarm.id, cancelSnooze, 0);
+ NotificationManager nm = getNotificationManager();
+ Notification n = new Notification(R.drawable.stat_notify_alarm,
+ label, 0);
+ n.setLatestEventInfo(this, label,
+ getString(R.string.alarm_notify_snooze_text,
+ Alarms.formatTime(this, c)), broadcast);
+ n.deleteIntent = broadcast;
+ n.flags |= Notification.FLAG_AUTO_CANCEL;
+ nm.notify(mAlarm.id, n);
+
+ String displayTime = getString(R.string.alarm_alert_snooze_set,
+ snoozeMinutes);
// Intentionally log the snooze time for debugging.
Log.v(displayTime);
+
// Display the snooze minutes in a toast.
Toast.makeText(AlarmAlert.this, displayTime, Toast.LENGTH_LONG).show();
- mKlaxon.stop(this, mState == SNOOZE);
- AlarmAlertWakeLock.release();
+ stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
+ finish();
+ }
+
+ private NotificationManager getNotificationManager() {
+ return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
// Dismiss the alarm.
- private void dismiss() {
- if (mState != UNKNOWN) {
- return;
+ private void dismiss(boolean killed) {
+ // The service told us that the alarm has been killed, do not modify
+ // the notification or stop the service.
+ if (!killed) {
+ // Cancel the notification and stop playing the alarm
+ NotificationManager nm = getNotificationManager();
+ nm.cancel(mAlarm.id);
+ stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
}
- mState = DISMISS;
- mKlaxon.stop(this, false);
- AlarmAlertWakeLock.release();
+ finish();
}
/**
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
- if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
- mState = UNKNOWN;
- mSnoozeButton.setEnabled(true);
-
- mAlarmId = intent.getIntExtra(Alarms.ID, -1);
- // Play the new alarm sound.
- mKlaxon.postPlay(this, mAlarmId);
- setTitleFromIntent(intent);
+ if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
- /* unset silenced message */
- TextView silenced = (TextView)findViewById(R.id.silencedText);
- silenced.setVisibility(View.GONE);
+ mAlarm = intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA);
- Alarms.setNextAlert(this);
- setIntent(intent);
+ setTitle();
}
@Override
- protected void onResume() {
+ protected void onStart() {
super.onResume();
- if (Log.LOGV) Log.v("AlarmAlert.onResume()");
+ if (Log.LOGV) Log.v("AlarmAlert.onStart()");
// Acquire a separate lock for the screen to stay on. This is necessary
// to avoid flashing the keyguard when the screen is locked.
AlarmAlertWakeLock.acquireScreenWakeLock(this);
@Override
protected void onStop() {
- super.onStop();
+ super.onPause();
if (Log.LOGV) Log.v("AlarmAlert.onStop()");
- // As a last resort, try to snooze if this activity is stopped.
- snooze();
- // We might have been killed by the KillerCallback so always release
- // the lock.
- AlarmAlertWakeLock.release();
+ AlarmAlertWakeLock.releaseScreenLock();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (Log.LOGV) Log.v("AlarmAlert.onDestroy()");
+ // No longer care about the alarm being killed.
+ unregisterReceiver(mReceiver);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- // Do this on key down to handle a few of the system keys. Only handle
- // the snooze and dismiss this alert if the state is unknown.
+ // Do this on key down to handle a few of the system keys.
boolean up = event.getAction() == KeyEvent.ACTION_UP;
- boolean dismiss = false;
switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- // Ignore ENDCALL because we do not receive the event if the screen
- // is on. However, we do receive the key up for ENDCALL if the
- // screen was off.
- case KeyEvent.KEYCODE_ENDCALL:
- break;
- // Volume keys dismiss the alarm
+ // Volume keys and camera keys dismiss the alarm
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_CAMERA:
case KeyEvent.KEYCODE_FOCUS:
- dismiss = true;
- // All other keys will snooze the alarm
- default:
- // Check for UNKNOWN here so that we intercept both key events
- // and prevent the volume keys from triggering their default
- // behavior.
- if (mState == UNKNOWN && up) {
- if (dismiss) {
- dismiss();
- } else {
- snooze();
- }
- finish();
+ if (up) {
+ dismiss(false);
}
return true;
+ default:
+ break;
}
return super.dispatchKeyEvent(event);
}
sScreenWakeLock.acquire();
}
- static void release() {
- Log.v("Releasing wake lock");
+ static void releaseCpuLock() {
+ Log.v("Releasing cpu wake lock");
if (sCpuWakeLock != null) {
sCpuWakeLock.release();
sCpuWakeLock = null;
}
+ }
+
+ static void releaseScreenLock() {
+ Log.v("Releasing screen lock");
if (sScreenWakeLock != null) {
sScreenWakeLock.release();
sScreenWakeLock = null;
}
public void bindView(View view, Context context, Cursor cursor) {
- final int id = cursor.getInt(Alarms.AlarmColumns.ALARM_ID_INDEX);
- final int hour = cursor.getInt(Alarms.AlarmColumns.ALARM_HOUR_INDEX);
- final int minutes = cursor.getInt(Alarms.AlarmColumns.ALARM_MINUTES_INDEX);
- final Alarms.DaysOfWeek daysOfWeek = new Alarms.DaysOfWeek(
- cursor.getInt(Alarms.AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
- final boolean enabled = cursor.getInt(Alarms.AlarmColumns.ALARM_ENABLED_INDEX) == 1;
- final String label =
- cursor.getString(Alarms.AlarmColumns.ALARM_MESSAGE_INDEX);
+ final Alarm alarm = new Alarm(cursor);
CheckBox onButton = (CheckBox)view.findViewById(R.id.alarmButton);
- onButton.setChecked(enabled);
+ onButton.setChecked(alarm.enabled);
onButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
boolean isChecked = ((CheckBox) v).isChecked();
- Alarms.enableAlarm(AlarmClock.this, id, isChecked);
+ Alarms.enableAlarm(AlarmClock.this, alarm.id,
+ isChecked);
if (isChecked) {
- SetAlarm.popAlarmSetToast(
- AlarmClock.this, hour, minutes, daysOfWeek);
+ SetAlarm.popAlarmSetToast(AlarmClock.this,
+ alarm.hour, alarm.minutes, alarm.daysOfWeek);
}
}
});
- DigitalClock digitalClock = (DigitalClock)view.findViewById(R.id.digitalClock);
- if (Log.LOGV) Log.v("bindView " + cursor.getPosition() + " " + id + " " + hour +
- ":" + minutes + " " + daysOfWeek.toString(context, true) + " dc " + digitalClock);
+ DigitalClock digitalClock =
+ (DigitalClock) view.findViewById(R.id.digitalClock);
// set the alarm text
final Calendar c = Calendar.getInstance();
- c.set(Calendar.HOUR_OF_DAY, hour);
- c.set(Calendar.MINUTE, minutes);
+ c.set(Calendar.HOUR_OF_DAY, alarm.hour);
+ c.set(Calendar.MINUTE, alarm.minutes);
digitalClock.updateTime(c);
// Set the repeat text or leave it blank if it does not repeat.
- TextView daysOfWeekView = (TextView) digitalClock.findViewById(R.id.daysOfWeek);
+ TextView daysOfWeekView =
+ (TextView) digitalClock.findViewById(R.id.daysOfWeek);
final String daysOfWeekStr =
- daysOfWeek.toString(AlarmClock.this, false);
+ alarm.daysOfWeek.toString(AlarmClock.this, false);
if (daysOfWeekStr != null && daysOfWeekStr.length() != 0) {
daysOfWeekView.setText(daysOfWeekStr);
daysOfWeekView.setVisibility(View.VISIBLE);
// Display the label
TextView labelView =
(TextView) digitalClock.findViewById(R.id.label);
- if (label != null && label.length() != 0) {
- labelView.setText(label);
+ if (alarm.label != null && alarm.label.length() != 0) {
+ labelView.setText(alarm.label);
labelView.setVisibility(View.VISIBLE);
} else {
labelView.setVisibility(View.GONE);
}
};
- private boolean isAlarmEnabled(final Cursor c) {
- return c.getInt(Alarms.AlarmColumns.ALARM_ENABLED_INDEX) == 1;
- }
-
@Override
public boolean onContextItemSelected(final MenuItem item) {
final AdapterContextMenuInfo info =
case R.id.enable_alarm:
final Cursor c = (Cursor) mAlarmsList.getAdapter()
.getItem(info.position);
- boolean enabled = isAlarmEnabled(c);
- Alarms.enableAlarm(this, id, !enabled);
- if (!enabled) {
- final int hour =
- c.getInt(Alarms.AlarmColumns.ALARM_HOUR_INDEX);
- final int minutes =
- c.getInt(Alarms.AlarmColumns.ALARM_MINUTES_INDEX);
- final Alarms.DaysOfWeek daysOfWeek = new Alarms.DaysOfWeek(
- c.getInt(Alarms.AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
- SetAlarm.popAlarmSetToast(this, hour, minutes, daysOfWeek);
+ final Alarm alarm = new Alarm(c);
+ Alarms.enableAlarm(this, alarm.id, !alarm.enabled);
+ if (!alarm.enabled) {
+ SetAlarm.popAlarmSetToast(this, alarm.hour, alarm.minutes,
+ alarm.daysOfWeek);
}
return true;
final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
final Cursor c =
(Cursor) mAlarmsList.getAdapter().getItem((int) info.position);
- final int hour = c.getInt(Alarms.AlarmColumns.ALARM_HOUR_INDEX);
- final int minutes = c.getInt(Alarms.AlarmColumns.ALARM_MINUTES_INDEX);
- final String label =
- c.getString(Alarms.AlarmColumns.ALARM_MESSAGE_INDEX);
+ final Alarm alarm = new Alarm(c);
// Construct the Calendar to compute the time.
final Calendar cal = Calendar.getInstance();
- cal.set(Calendar.HOUR_OF_DAY, hour);
- cal.set(Calendar.MINUTE, minutes);
+ cal.set(Calendar.HOUR_OF_DAY, alarm.hour);
+ cal.set(Calendar.MINUTE, alarm.minutes);
final String time = Alarms.formatTime(this, cal);
// Inflate the custom view and set each TextView's text.
TextView textView = (TextView) v.findViewById(R.id.header_time);
textView.setText(time);
textView = (TextView) v.findViewById(R.id.header_label);
- textView.setText(label);
+ textView.setText(alarm.label);
// Set the custom view on the menu.
menu.setHeaderView(v);
// Change the text to "disable" if the alarm is already enabled.
- if (isAlarmEnabled(c)) {
+ if (alarm.enabled) {
menu.findItem(R.id.enable_alarm).setTitle(R.string.disable_alarm);
}
}
public void onItemClick(AdapterView parent, View v, int pos, long id) {
Intent intent = new Intent(this, SetAlarm.class);
- intent.putExtra(Alarms.ID, (int) id);
+ intent.putExtra(Alarms.ALARM_ID, (int) id);
startActivity(intent);
}
Log.v("In AlarmClock, new alarm id = " + newId);
}
Intent intent = new Intent(this, SetAlarm.class);
- intent.putExtra(Alarms.ID, newId);
+ intent.putExtra(Alarms.ALARM_ID, newId);
startActivity(intent);
return true;
return;
}
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- Alarms.disableSnoozeAlert(context);
+ Alarms.saveSnoozeAlert(context, -1, -1);
Alarms.disableExpiredAlarms(context);
}
Alarms.setNextAlert(context);
package com.android.alarmclock;
-import android.content.ContentResolver;
+import android.app.Service;
import android.content.Context;
+import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.Vibrator;
import android.telephony.TelephonyManager;
* Manages alarms and vibe. Singleton, so it can be initiated in
* AlarmReceiver and shut down in the AlarmAlert activity
*/
-class AlarmKlaxon implements Alarms.AlarmSettings {
-
- interface KillerCallback {
- public void onKilled();
- }
+public class AlarmKlaxon extends Service {
/** Play alarm up to 10 minutes before silencing */
- final static int ALARM_TIMEOUT_SECONDS = 10 * 60;
+ private static final int ALARM_TIMEOUT_SECONDS = 10 * 60;
private static final long[] sVibratePattern = new long[] { 500, 500 };
- private int mAlarmId;
- private String mAlert;
- private Alarms.DaysOfWeek mDaysOfWeek;
- private boolean mVibrate;
private boolean mPlaying = false;
private Vibrator mVibrator;
private MediaPlayer mMediaPlayer;
- private KillerCallback mKillerCallback;
+ private Alarm mCurrentAlarm;
+ private long mStartTime;
// Internal messages
private static final int KILLER = 1000;
- private static final int PLAY = 1001;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
if (Log.LOGV) {
Log.v("*********** Alarm killer triggered ***********");
}
- if (mKillerCallback != null) {
- mKillerCallback.onKilled();
- }
- break;
- case PLAY:
- play((Context) msg.obj, msg.arg1);
+ sendKillBroadcast((Alarm) msg.obj);
+ stopSelf();
break;
}
}
};
- AlarmKlaxon() {
+ @Override
+ public void onCreate() {
mVibrator = new Vibrator();
+ AlarmAlertWakeLock.acquireCpuWakeLock(this);
}
- public void reportAlarm(
- int idx, boolean enabled, int hour, int minutes,
- Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String message,
- String alert) {
- if (Log.LOGV) Log.v("AlarmKlaxon.reportAlarm: " + idx + " " + hour +
- " " + minutes + " dow " + daysOfWeek);
- mAlert = alert;
- mDaysOfWeek = daysOfWeek;
- mVibrate = vibrate;
+ @Override
+ public void onDestroy() {
+ stop();
+ AlarmAlertWakeLock.releaseCpuLock();
}
- public void postPlay(final Context context, final int alarmId) {
- mHandler.sendMessage(mHandler.obtainMessage(PLAY, alarmId, 0, context));
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
}
- // Volume suggested by media team for in-call alarms.
- private static final float IN_CALL_VOLUME = 0.125f;
+ @Override
+ public void onStart(Intent intent, int startId) {
+ final Alarm alarm = intent.getParcelableExtra(
+ Alarms.ALARM_INTENT_EXTRA);
- private void play(Context context, int alarmId) {
- ContentResolver contentResolver = context.getContentResolver();
+ if (alarm == null) {
+ Log.v("AlarmKlaxon failed to parse the alarm from the intent");
+ return;
+ }
+
+ if (mCurrentAlarm != null) {
+ sendKillBroadcast(mCurrentAlarm);
+ }
- if (mPlaying) stop(context, false);
+ play(alarm);
+ mCurrentAlarm = alarm;
+ }
+
+ private void sendKillBroadcast(Alarm alarm) {
+ long millis = System.currentTimeMillis() - mStartTime;
+ int minutes = (int) Math.round(millis / 60000.0);
+ Intent alarmKilled = new Intent(Alarms.ALARM_KILLED);
+ alarmKilled.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+ alarmKilled.putExtra(Alarms.ALARM_KILLED_TIMEOUT, minutes);
+ sendBroadcast(alarmKilled);
+ }
- mAlarmId = alarmId;
+ // Volume suggested by media team for in-call alarms.
+ private static final float IN_CALL_VOLUME = 0.125f;
- /* this will call reportAlarm() callback */
- Alarms.getAlarm(contentResolver, this, mAlarmId);
+ private void play(Alarm alarm) {
+ // stop() checks to see if we are already playing.
+ stop();
- if (Log.LOGV) Log.v("AlarmKlaxon.play() " + mAlarmId + " alert " + mAlert);
+ if (Log.LOGV) {
+ Log.v("AlarmKlaxon.play() " + alarm.id + " alert " + alarm.alert);
+ }
- // Fall back on the default alarm if the database does not have an
- // alarm stored.
- Uri alert = null;
- boolean silent = false;
- if (mAlert == null) {
- alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
- if (Log.LOGV) {
- Log.v("Using default alarm: " + alert.toString());
+ if (!alarm.silent) {
+ Uri alert = alarm.alert;
+ // Fall back on the default alarm if the database does not have an
+ // alarm stored.
+ if (alert == null) {
+ alert = RingtoneManager.getDefaultUri(
+ RingtoneManager.TYPE_ALARM);
+ if (Log.LOGV) {
+ Log.v("Using default alarm: " + alert.toString());
+ }
}
- } else if (Alarms.ALARM_ALERT_SILENT.equals(mAlert)) {
- silent = true;
- } else {
- alert = Uri.parse(mAlert);
- }
- if (!silent) {
// TODO: Reuse mMediaPlayer instead of creating a new one and/or use
// RingtoneManager.
mMediaPlayer = new MediaPlayer();
try {
TelephonyManager tm =
- (TelephonyManager) context.getSystemService(
+ (TelephonyManager) getSystemService(
Context.TELEPHONY_SERVICE);
// Check if we are in a call. If we are, use the in-call alarm
// resource at a low volume to not disrupt the call.
if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
Log.v("Using the in-call alarm");
mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
- setDataSourceFromResource(context.getResources(),
- mMediaPlayer, R.raw.in_call_alarm);
+ setDataSourceFromResource(getResources(), mMediaPlayer,
+ R.raw.in_call_alarm);
} else {
- mMediaPlayer.setDataSource(context, alert);
+ mMediaPlayer.setDataSource(this, alert);
}
startAlarm(mMediaPlayer);
} catch (Exception ex) {
try {
// Must reset the media player to clear the error state.
mMediaPlayer.reset();
- setDataSourceFromResource(context.getResources(),
- mMediaPlayer,
+ setDataSourceFromResource(getResources(), mMediaPlayer,
com.android.internal.R.raw.fallbackring);
startAlarm(mMediaPlayer);
} catch (Exception ex2) {
}
/* Start the vibrator after everything is ok with the media player */
- if (mVibrate) {
+ if (alarm.vibrate) {
mVibrator.vibrate(sVibratePattern, 0);
} else {
mVibrator.cancel();
}
- enableKiller();
+ enableKiller(alarm);
mPlaying = true;
+ mStartTime = System.currentTimeMillis();
}
// Do the common stuff when starting the alarm.
* Stops alarm audio and disables alarm if it not snoozed and not
* repeating
*/
- public void stop(Context context, boolean snoozed) {
- if (Log.LOGV) Log.v("AlarmKlaxon.stop() " + mAlarmId);
+ public void stop() {
+ if (Log.LOGV) Log.v("AlarmKlaxon.stop()");
if (mPlaying) {
mPlaying = false;
// Stop vibrator
mVibrator.cancel();
-
- /* disable alarm only if it is not set to repeat */
- if (!snoozed && ((mDaysOfWeek == null || !mDaysOfWeek.isRepeatSet()))) {
- Alarms.enableAlarm(context, mAlarmId, false);
- }
}
disableKiller();
}
/**
- * This callback called when alarm killer times out unattended
- * alarm
- */
- public void setKillerCallback(KillerCallback killerCallback) {
- mKillerCallback = killerCallback;
- }
-
- /**
* Kills alarm audio after ALARM_TIMEOUT_SECONDS, so the alarm
* won't run all day.
*
* This just cancels the audio, but leaves the notification
* popped, so the user will know that the alarm tripped.
*/
- private void enableKiller() {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(KILLER),
+ private void enableKiller(Alarm alarm) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(KILLER, alarm),
1000 * ALARM_TIMEOUT_SECONDS);
}
else
values = new ContentValues();
- if (!values.containsKey(Alarms.AlarmColumns.HOUR))
- values.put(Alarms.AlarmColumns.HOUR, 0);
+ if (!values.containsKey(Alarm.Columns.HOUR))
+ values.put(Alarm.Columns.HOUR, 0);
- if (!values.containsKey(Alarms.AlarmColumns.MINUTES))
- values.put(Alarms.AlarmColumns.MINUTES, 0);
+ if (!values.containsKey(Alarm.Columns.MINUTES))
+ values.put(Alarm.Columns.MINUTES, 0);
- if (!values.containsKey(Alarms.AlarmColumns.DAYS_OF_WEEK))
- values.put(Alarms.AlarmColumns.DAYS_OF_WEEK, 0);
+ if (!values.containsKey(Alarm.Columns.DAYS_OF_WEEK))
+ values.put(Alarm.Columns.DAYS_OF_WEEK, 0);
- if (!values.containsKey(Alarms.AlarmColumns.ALARM_TIME))
- values.put(Alarms.AlarmColumns.ALARM_TIME, 0);
+ if (!values.containsKey(Alarm.Columns.ALARM_TIME))
+ values.put(Alarm.Columns.ALARM_TIME, 0);
- if (!values.containsKey(Alarms.AlarmColumns.ENABLED))
- values.put(Alarms.AlarmColumns.ENABLED, 0);
+ if (!values.containsKey(Alarm.Columns.ENABLED))
+ values.put(Alarm.Columns.ENABLED, 0);
- if (!values.containsKey(Alarms.AlarmColumns.VIBRATE))
- values.put(Alarms.AlarmColumns.VIBRATE, 1);
+ if (!values.containsKey(Alarm.Columns.VIBRATE))
+ values.put(Alarm.Columns.VIBRATE, 1);
- if (!values.containsKey(Alarms.AlarmColumns.MESSAGE))
- values.put(Alarms.AlarmColumns.MESSAGE, "");
+ if (!values.containsKey(Alarm.Columns.MESSAGE))
+ values.put(Alarm.Columns.MESSAGE, "");
- if (!values.containsKey(Alarms.AlarmColumns.ALERT))
- values.put(Alarms.AlarmColumns.ALERT, "");
+ if (!values.containsKey(Alarm.Columns.ALERT))
+ values.put(Alarm.Columns.ALERT, "");
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- long rowId = db.insert("alarms", Alarms.AlarmColumns.MESSAGE, values);
+ long rowId = db.insert("alarms", Alarm.Columns.MESSAGE, values);
if (rowId < 0) {
throw new SQLException("Failed to insert row into " + url);
}
if (Log.LOGV) Log.v("Added alarm rowId = " + rowId);
- Uri newUrl = ContentUris.withAppendedId(Alarms.AlarmColumns.CONTENT_URI, rowId);
+ Uri newUrl = ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(newUrl, null);
return newUrl;
}
package com.android.alarmclock;
import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
+import android.database.Cursor;
+import android.os.Parcel;
/**
* Glue class: connects AlarmAlert IntentReceiver to AlarmAlert
@Override
public void onReceive(Context context, Intent intent) {
- long now = System.currentTimeMillis();
- int id = intent.getIntExtra(Alarms.ID, 0);
- long setFor = intent.getLongExtra(Alarms.TIME, 0);
+ // Take care of the easy intents first.
+ if (Alarms.CLEAR_NOTIFICATION.equals(intent.getAction())) {
+ // If this is the "Clear All Notifications" intent, stop the alarm
+ // service and return.
+ context.stopService(new Intent(Alarms.ALARM_ALERT_ACTION));
+ return;
+ } else if (Alarms.ALARM_KILLED.equals(intent.getAction())) {
+ // The alarm has been killed, update the notification
+ updateNotification(context, (Alarm)
+ intent.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA),
+ intent.getIntExtra(Alarms.ALARM_KILLED_TIMEOUT, -1));
+ return;
+ } else if (Alarms.CANCEL_SNOOZE.equals(intent.getAction())) {
+ Alarms.saveSnoozeAlert(context, -1, -1);
+ return;
+ }
+
+ Alarm alarm = null;
+ // Grab the alarm from the intent. Since the remote AlarmManagerService
+ // fills in the Intent to add some extra data, it must unparcel the
+ // Alarm object. It throws a ClassNotFoundException when unparcelling.
+ // To avoid this, do the marshalling ourselves.
+ final byte[] data = intent.getByteArrayExtra(Alarms.ALARM_RAW_DATA);
+ if (data != null) {
+ Parcel in = Parcel.obtain();
+ in.unmarshall(data, 0, data.length);
+ in.setDataPosition(0);
+ alarm = Alarm.CREATOR.createFromParcel(in);
+ }
+ if (alarm == null) {
+ Log.v("AlarmReceiver failed to parse the alarm from the intent");
+ return;
+ }
+
+ long now = System.currentTimeMillis();
/* FIXME Intentionally verbose: always log this until we've
fully debugged the app failing to start up */
- Log.v("AlarmReceiver.onReceive() id " + id + " setFor " + setFor +
- " now " + now);
+ Log.v("AlarmReceiver.onReceive() id " + alarm.id + " setFor "
+ + alarm.time + " now " + now);
- if (now > setFor + STALE_WINDOW * 1000) {
- if (Log.LOGV) Log.v("AlarmReceiver ignoring stale alarm intent id"
- + id + " setFor " + setFor + " now " + now);
+ if (now > alarm.time + STALE_WINDOW * 1000) {
+ if (Log.LOGV) {
+ Log.v("AlarmReceiver ignoring stale alarm intent id" + alarm.id
+ + " setFor " + alarm.time + " now " + now);
+ }
return;
}
- /* Wake the device and stay awake until the AlarmAlert intent is
- * handled. */
- AlarmAlertWakeLock.acquireCpuWakeLock(context);
-
/* Close dialogs and window shade */
- Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- context.sendBroadcast(i);
+ Intent closeDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ context.sendBroadcast(closeDialogs);
// Decide which activity to start based on the state of the keyguard.
Class c = AlarmAlert.class;
/* launch UI, explicitly stating that this is not due to user action
* so that the current app's notification management is not disturbed */
- Intent fireAlarm = new Intent(context, c);
- fireAlarm.putExtra(Alarms.ID, id);
- fireAlarm.putExtra(Alarms.LABEL, intent.getStringExtra(Alarms.LABEL));
- fireAlarm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- context.startActivity(fireAlarm);
- }
+ Intent alarmAlert = new Intent(context, c);
+ alarmAlert.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+ alarmAlert.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ context.startActivity(alarmAlert);
+
+ // Disable the snooze alert if this alarm is the snooze.
+ Alarms.disableSnoozeAlert(context, alarm.id);
+ // Disable this alarm if it does not repeat.
+ if (!alarm.daysOfWeek.isRepeatSet()) {
+ Alarms.enableAlarm(context, alarm.id, false);
+ }
+ // Enable the next alert if there is one.
+ Alarms.setNextAlert(context);
+
+ // Play the alarm alert and vibrate the device.
+ Intent playAlarm = new Intent(Alarms.ALARM_ALERT_ACTION);
+ playAlarm.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+ context.startService(playAlarm);
+
+ // Trigger a notification that, when clicked, will show the alarm alert
+ // dialog. No need to check for fullscreen since this will always be
+ // launched from a user action.
+ Intent notify = new Intent(context, AlarmAlert.class);
+ notify.putExtra(Alarms.ALARM_INTENT_EXTRA, alarm);
+ PendingIntent pendingNotify = PendingIntent.getActivity(context,
+ alarm.id, notify, 0);
+
+ // Use the alarm's label or the default label as the ticker text and
+ // main text of the notification.
+ String label = alarm.getLabelOrDefault(context);
+ Notification n = new Notification(R.drawable.stat_notify_alarm,
+ label, alarm.time);
+ n.setLatestEventInfo(context, label,
+ context.getString(R.string.alarm_notify_text),
+ pendingNotify);
+ n.flags |= Notification.FLAG_SHOW_LIGHTS;
+ n.ledARGB = 0xFF00FF00;
+ n.ledOnMS = 500;
+ n.ledOffMS = 500;
+
+ // Set the deleteIntent for when the user clicks "Clear All
+ // Notifications"
+ Intent clearAll = new Intent(context, AlarmReceiver.class);
+ clearAll.setAction(Alarms.CLEAR_NOTIFICATION);
+ n.deleteIntent = PendingIntent.getBroadcast(context, 0, clearAll, 0);
+
+ // Send the notification using the alarm id to easily identify the
+ // correct notification.
+ NotificationManager nm = getNotificationManager(context);
+ nm.notify(alarm.id, n);
+
+ // Maintain a cpu wake lock until the AlarmAlert and AlarmKlaxon can
+ // pick it up.
+ AlarmAlertWakeLock.acquireCpuWakeLock(context);
+ }
+
+ private NotificationManager getNotificationManager(Context context) {
+ return (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ private void updateNotification(Context context, Alarm alarm, int timeout) {
+ NotificationManager nm = getNotificationManager(context);
+
+ // If the alarm is null, just cancel the notification.
+ if (alarm == null) {
+ if (Log.LOGV) {
+ Log.v("Cannot update notification for killer callback");
+ }
+ return;
+ }
+
+ // Launch SetAlarm when clicked.
+ Intent viewAlarm = new Intent(context, SetAlarm.class);
+ viewAlarm.putExtra(Alarms.ALARM_ID, alarm.id);
+ PendingIntent intent =
+ PendingIntent.getActivity(context, alarm.id, viewAlarm, 0);
+
+ // Update the notification to indicate that the alert has been
+ // silenced.
+ String label = alarm.getLabelOrDefault(context);
+ Notification n = new Notification(R.drawable.stat_notify_alarm,
+ label, alarm.time);
+ n.setLatestEventInfo(context, label,
+ context.getString(R.string.alarm_alert_alert_silenced, timeout),
+ intent);
+ n.flags |= Notification.FLAG_AUTO_CANCEL;
+ nm.notify(alarm.id, n);
+ }
}
package com.android.alarmclock;
import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
-import android.provider.BaseColumns;
+import android.os.Parcel;
import android.provider.Settings;
import android.text.format.DateFormat;
*/
public class Alarms {
- public final static String ALARM_ALERT_ACTION = "com.android.alarmclock.ALARM_ALERT";
- public final static String ID = "alarm_id";
- public final static String TIME = "alarm_time";
- public final static String LABEL = "alarm_label";
- public final static String ALARM_ALERT_SILENT = "silent";
+ // This action triggers the AlarmReceiver as well as the AlarmKlaxon. It
+ // is a public action used in the manifest for receiving Alarm broadcasts
+ // from the alarm manager.
+ public static final String ALARM_ALERT_ACTION = "com.android.alarmclock.ALARM_ALERT";
- final static String PREF_SNOOZE_ID = "snooze_id";
- final static String PREF_SNOOZE_TIME = "snooze_time";
- final static String PREF_SNOOZE_LABEL = "snooze_label";
-
- private final static String DM12 = "E h:mm aa";
- private final static String DM24 = "E k:mm";
-
- private final static String M12 = "h:mm aa";
- private final static String M24 = "k:mm";
-
- /**
- * Mapping from days in this application (where Monday is 0) to
- * days in DateFormatSymbols (where Monday is 2).
- */
- private static int[] DAY_MAP = new int[] {
- Calendar.MONDAY,
- Calendar.TUESDAY,
- Calendar.WEDNESDAY,
- Calendar.THURSDAY,
- Calendar.FRIDAY,
- Calendar.SATURDAY,
- Calendar.SUNDAY,
- };
-
- static class DaysOfWeek {
-
- int mDays;
-
- /**
- * Days of week coded as single int, convenient for DB
- * storage:
- *
- * 0x00: no day
- * 0x01: Monday
- * 0x02: Tuesday
- * 0x04: Wednesday
- * 0x08: Thursday
- * 0x10: Friday
- * 0x20: Saturday
- * 0x40: Sunday
- */
- DaysOfWeek() {
- this(0);
- }
-
- DaysOfWeek(int days) {
- mDays = days;
- }
-
- public String toString(Context context, boolean showNever) {
- StringBuilder ret = new StringBuilder();
-
- /* no days */
- if (mDays == 0) return showNever ? context.getText(
- R.string.never).toString() : "";
-
- /* every day */
- if (mDays == 0x7f) {
- return context.getText(R.string.every_day).toString();
- }
-
- /* count selected days */
- int dayCount = 0, days = mDays;
- while (days > 0) {
- if ((days & 1) == 1) dayCount++;
- days >>= 1;
- }
-
- /* short or long form? */
- DateFormatSymbols dfs = new DateFormatSymbols();
- String[] dayList = (dayCount > 1) ?
- dfs.getShortWeekdays() :
- dfs.getWeekdays();
-
- /* selected days */
- for (int i = 0; i < 7; i++) {
- if ((mDays & (1 << i)) != 0) {
- ret.append(dayList[DAY_MAP[i]]);
- dayCount -= 1;
- if (dayCount > 0) ret.append(
- context.getText(R.string.day_concat));
- }
- }
- return ret.toString();
- }
-
- /**
- * @param day Mon=0 ... Sun=6
- * @return true if given day is set
- */
- public boolean isSet(int day) {
- return ((mDays & (1 << day)) > 0);
- }
-
- public void set(int day, boolean set) {
- if (set) {
- mDays |= (1 << day);
- } else {
- mDays &= ~(1 << day);
- }
- }
+ // This is a private action used when the user clears all notifications.
+ public static final String CLEAR_NOTIFICATION = "clear_notification";
- public void set(DaysOfWeek dow) {
- mDays = dow.mDays;
- }
+ // This is a private action used by the AlarmKlaxon to update the UI to
+ // show the alarm has been killed.
+ public static final String ALARM_KILLED = "alarm_killed";
- public int getCoded() {
- return mDays;
- }
+ // Extra in the ALARM_KILLED intent to indicate to the user how long the
+ // alarm played before being killed.
+ public static final String ALARM_KILLED_TIMEOUT = "alarm_killed_timeout";
- public boolean equals(DaysOfWeek dow) {
- return mDays == dow.mDays;
- }
+ // This string is used to indicate a silent alarm in the db.
+ public static final String ALARM_ALERT_SILENT = "silent";
- // Returns days of week encoded in an array of booleans.
- public boolean[] getBooleanArray() {
- boolean[] ret = new boolean[7];
- for (int i = 0; i < 7; i++) {
- ret[i] = isSet(i);
- }
- return ret;
- }
+ // This intent is sent from the notification when the user cancels the
+ // snooze alert.
+ public static final String CANCEL_SNOOZE = "cancel_snooze";
- public void setCoded(int days) {
- mDays = days;
- }
+ // This string is used when passing an Alarm object through an intent.
+ public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm";
- /**
- * @return true if alarm is set to repeat
- */
- public boolean isRepeatSet() {
- return mDays != 0;
- }
+ // This extra is the raw Alarm object data. It is used in the
+ // AlarmManagerService to avoid a ClassNotFoundException when filling in
+ // the Intent extras.
+ public static final String ALARM_RAW_DATA = "intent.extra.alarm_raw";
- /**
- * @return true if alarm is set to repeat every day
- */
- public boolean isEveryDaySet() {
- return mDays == 0x7f;
- }
+ // This string is used to identify the alarm id passed to SetAlarm from the
+ // list of alarms.
+ public static final String ALARM_ID = "alarm_id";
+ final static String PREF_SNOOZE_ID = "snooze_id";
+ final static String PREF_SNOOZE_TIME = "snooze_time";
- /**
- * returns number of days from today until next alarm
- * @param c must be set to today
- */
- public int getNextAlarm(Calendar c) {
- if (mDays == 0) return -1;
- int today = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
-
- int day, dayCount;
- for (dayCount = 0; dayCount < 7; dayCount++) {
- day = (today + dayCount) % 7;
- if ((mDays & (1 << day)) > 0) {
- break;
- }
- }
- return dayCount;
- }
- }
-
- public static class AlarmColumns implements BaseColumns {
-
- /**
- * The content:// style URL for this table
- */
- public static final Uri CONTENT_URI =
- Uri.parse("content://com.android.alarmclock/alarm");
-
- public static final String _ID = "_id";
-
- /**
- * The default sort order for this table
- */
- public static final String DEFAULT_SORT_ORDER = "_id ASC";
-
- /**
- * Hour in 24-hour localtime 0 - 23.
- * <P>Type: INTEGER</P>
- */
- public static final String HOUR = "hour";
-
- /**
- * Minutes in localtime 0 - 59
- * <P>Type: INTEGER</P>
- */
- public static final String MINUTES = "minutes";
-
- /**
- * Days of week coded as integer
- * <P>Type: INTEGER</P>
- */
- public static final String DAYS_OF_WEEK = "daysofweek";
-
- /**
- * Alarm time in UTC milliseconds from the epoch.
- * <P>Type: INTEGER</P>
- */
- public static final String ALARM_TIME = "alarmtime";
-
- /**
- * True if alarm is active
- * <P>Type: BOOLEAN</P>
- */
- public static final String ENABLED = "enabled";
-
- /**
- * True if alarm should vibrate
- * <P>Type: BOOLEAN</P>
- */
- public static final String VIBRATE = "vibrate";
-
- /**
- * Message to show when alarm triggers
- * Note: not currently used
- * <P>Type: STRING</P>
- */
- public static final String MESSAGE = "message";
-
- /**
- * Audio alert to play when alarm triggers
- * <P>Type: STRING</P>
- */
- public static final String ALERT = "alert";
-
- static final String[] ALARM_QUERY_COLUMNS = {
- _ID, HOUR, MINUTES, DAYS_OF_WEEK, ALARM_TIME,
- ENABLED, VIBRATE, MESSAGE, ALERT};
-
- /**
- * These save calls to cursor.getColumnIndexOrThrow()
- * THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY COLUMNS
- */
- public static final int ALARM_ID_INDEX = 0;
- public static final int ALARM_HOUR_INDEX = 1;
- public static final int ALARM_MINUTES_INDEX = 2;
- public static final int ALARM_DAYS_OF_WEEK_INDEX = 3;
- public static final int ALARM_TIME_INDEX = 4;
- public static final int ALARM_ENABLED_INDEX = 5;
- public static final int ALARM_VIBRATE_INDEX = 6;
- public static final int ALARM_MESSAGE_INDEX = 7;
- public static final int ALARM_ALERT_INDEX = 8;
- }
+ private final static String DM12 = "E h:mm aa";
+ private final static String DM24 = "E k:mm";
- /**
- * getAlarm and getAlarms call this interface to report alarms in
- * the database
- */
- static interface AlarmSettings {
- void reportAlarm(
- int idx, boolean enabled, int hour, int minutes,
- DaysOfWeek daysOfWeek, boolean vibrate, String message,
- String alert);
- }
+ private final static String M12 = "h:mm aa";
+ private final static String M24 = "k:mm";
/**
* Creates a new Alarm.
*/
- public synchronized static Uri addAlarm(ContentResolver contentResolver) {
+ public static Uri addAlarm(ContentResolver contentResolver) {
ContentValues values = new ContentValues();
- values.put(Alarms.AlarmColumns.HOUR, 8);
- return contentResolver.insert(AlarmColumns.CONTENT_URI, values);
+ values.put(Alarm.Columns.HOUR, 8);
+ return contentResolver.insert(Alarm.Columns.CONTENT_URI, values);
}
/**
* Removes an existing Alarm. If this alarm is snoozing, disables
* snooze. Sets next alert.
*/
- public synchronized static void deleteAlarm(
+ public static void deleteAlarm(
Context context, int alarmId) {
ContentResolver contentResolver = context.getContentResolver();
/* If alarm is snoozing, lose it */
- int snoozeId = getSnoozeAlarmId(context);
- if (snoozeId == alarmId) disableSnoozeAlert(context);
+ disableSnoozeAlert(context, alarmId);
- Uri uri = ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, alarmId);
- deleteAlarm(contentResolver, uri);
+ Uri uri = ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId);
+ contentResolver.delete(uri, "", null);
setNextAlert(context);
}
- private synchronized static void deleteAlarm(
- ContentResolver contentResolver, Uri uri) {
- contentResolver.delete(uri, "", null);
- }
-
/**
* Queries all alarms
* @return cursor over all alarms
*/
- public synchronized static Cursor getAlarmsCursor(
- ContentResolver contentResolver) {
+ public static Cursor getAlarmsCursor(ContentResolver contentResolver) {
return contentResolver.query(
- AlarmColumns.CONTENT_URI, AlarmColumns.ALARM_QUERY_COLUMNS,
- null, null, AlarmColumns.DEFAULT_SORT_ORDER);
+ Alarm.Columns.CONTENT_URI, Alarm.Columns.ALARM_QUERY_COLUMNS,
+ null, null, Alarm.Columns.DEFAULT_SORT_ORDER);
}
- /**
- * Calls the AlarmSettings.reportAlarm interface on all alarms found in db.
- */
- public synchronized static void getAlarms(
- ContentResolver contentResolver, AlarmSettings alarmSettings) {
- Cursor cursor = getAlarmsCursor(contentResolver);
- getAlarms(alarmSettings, cursor);
- cursor.close();
- }
-
- private synchronized static void getAlarms(
- AlarmSettings alarmSettings, Cursor cur) {
- if (cur.moveToFirst()) {
- do {
- // Get the field values
- int id = cur.getInt(AlarmColumns.ALARM_ID_INDEX);
- int hour = cur.getInt(AlarmColumns.ALARM_HOUR_INDEX);
- int minutes = cur.getInt(AlarmColumns.ALARM_MINUTES_INDEX);
- int daysOfWeek = cur.getInt(AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX);
- boolean enabled = cur.getInt(AlarmColumns.ALARM_ENABLED_INDEX) == 1 ? true : false;
- boolean vibrate = cur.getInt(AlarmColumns.ALARM_VIBRATE_INDEX) == 1 ? true : false;
- String message = cur.getString(AlarmColumns.ALARM_MESSAGE_INDEX);
- String alert = cur.getString(AlarmColumns.ALARM_ALERT_INDEX);
- alarmSettings.reportAlarm(
- id, enabled, hour, minutes, new DaysOfWeek(daysOfWeek),
- vibrate, message, alert);
- } while (cur.moveToNext());
- }
+ // Private method to get a more limited set of alarms from the database.
+ private static Cursor getFilteredAlarmsCursor(
+ ContentResolver contentResolver) {
+ return contentResolver.query(Alarm.Columns.CONTENT_URI,
+ Alarm.Columns.ALARM_QUERY_COLUMNS, Alarm.Columns.WHERE_ENABLED,
+ null, null);
}
/**
- * Calls the AlarmSettings.reportAlarm interface on alarm with given
- * alarmId
+ * Return an Alarm object representing the alarm id in the database.
+ * Returns null if no alarm exists.
*/
- public synchronized static void getAlarm(
- ContentResolver contentResolver, AlarmSettings alarmSetting,
- int alarmId) {
+ public static Alarm getAlarm(ContentResolver contentResolver, int alarmId) {
Cursor cursor = contentResolver.query(
- ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, alarmId),
- AlarmColumns.ALARM_QUERY_COLUMNS,
- null, null, AlarmColumns.DEFAULT_SORT_ORDER);
-
- getAlarms(alarmSetting, cursor);
- cursor.close();
+ ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId),
+ Alarm.Columns.ALARM_QUERY_COLUMNS,
+ null, null, null);
+ Alarm alarm = null;
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ alarm = new Alarm(cursor);
+ }
+ cursor.close();
+ }
+ return alarm;
}
* @param message corresponds to the MESSAGE column
* @param alert corresponds to the ALERT column
*/
- public synchronized static void setAlarm(
+ public static void setAlarm(
Context context, int id, boolean enabled, int hour, int minutes,
- DaysOfWeek daysOfWeek, boolean vibrate, String message,
+ Alarm.DaysOfWeek daysOfWeek, boolean vibrate, String message,
String alert) {
ContentValues values = new ContentValues(8);
ContentResolver resolver = context.getContentResolver();
- long time = calculateAlarm(hour, minutes, daysOfWeek).getTimeInMillis();
+ // Set the alarm_time value if this alarm does not repeat. This will be
+ // used later to disable expired alarms.
+ long time = 0;
+ if (!daysOfWeek.isRepeatSet()) {
+ time = calculateAlarm(hour, minutes, daysOfWeek).getTimeInMillis();
+ }
if (Log.LOGV) Log.v(
"** setAlarm * idx " + id + " hour " + hour + " minutes " +
minutes + " enabled " + enabled + " time " + time);
- values.put(AlarmColumns.ENABLED, enabled ? 1 : 0);
- values.put(AlarmColumns.HOUR, hour);
- values.put(AlarmColumns.MINUTES, minutes);
- values.put(AlarmColumns.ALARM_TIME, time);
- values.put(AlarmColumns.DAYS_OF_WEEK, daysOfWeek.getCoded());
- values.put(AlarmColumns.VIBRATE, vibrate);
- values.put(AlarmColumns.MESSAGE, message);
- values.put(AlarmColumns.ALERT, alert);
- resolver.update(ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, id),
+ values.put(Alarm.Columns.ENABLED, enabled ? 1 : 0);
+ values.put(Alarm.Columns.HOUR, hour);
+ values.put(Alarm.Columns.MINUTES, minutes);
+ values.put(Alarm.Columns.ALARM_TIME, time);
+ values.put(Alarm.Columns.DAYS_OF_WEEK, daysOfWeek.getCoded());
+ values.put(Alarm.Columns.VIBRATE, vibrate);
+ values.put(Alarm.Columns.MESSAGE, message);
+ values.put(Alarm.Columns.ALERT, alert);
+ resolver.update(ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, id),
values, null, null);
- int aid = disableSnoozeAlert(context);
- if (aid != -1 && aid != id) enableAlarmInternal(context, aid, false);
setNextAlert(context);
}
* @param enabled corresponds to the ENABLED column
*/
- public synchronized static void enableAlarm(
+ public static void enableAlarm(
final Context context, final int id, boolean enabled) {
- int aid = disableSnoozeAlert(context);
- if (aid != -1 && aid != id) enableAlarmInternal(context, aid, false);
enableAlarmInternal(context, id, enabled);
setNextAlert(context);
}
- private synchronized static void enableAlarmInternal(
- final Context context, final int id, boolean enabled) {
- ContentResolver resolver = context.getContentResolver();
+ private static void enableAlarmInternal(final Context context,
+ final int id, boolean enabled) {
+ enableAlarmInternal(context, getAlarm(context.getContentResolver(), id),
+ enabled);
+ }
- class EnableAlarm implements AlarmSettings {
- public int mHour;
- public int mMinutes;
- public DaysOfWeek mDaysOfWeek;
-
- public void reportAlarm(
- int idx, boolean enabled, int hour, int minutes,
- DaysOfWeek daysOfWeek, boolean vibrate, String message,
- String alert) {
- mHour = hour;
- mMinutes = minutes;
- mDaysOfWeek = daysOfWeek;
- }
- }
+ private static void enableAlarmInternal(final Context context,
+ final Alarm alarm, boolean enabled) {
+ ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues(2);
- values.put(AlarmColumns.ENABLED, enabled ? 1 : 0);
+ values.put(Alarm.Columns.ENABLED, enabled ? 1 : 0);
- /* If we are enabling the alarm, load hour/minutes/daysOfWeek
- from db, so we can calculate alarm time */
+ // If we are enabling the alarm, calculate alarm time since the time
+ // value in Alarm may be old.
if (enabled) {
- EnableAlarm enableAlarm = new EnableAlarm();
- getAlarm(resolver, enableAlarm, id);
- if (enableAlarm.mDaysOfWeek == null) {
- /* Under monkey, sometimes reportAlarm is never
- called */
- Log.e("** enableAlarmInternal failed " + id + " h " +
- enableAlarm.mHour + " m " + enableAlarm.mMinutes);
- return;
+ long time = 0;
+ if (!alarm.daysOfWeek.isRepeatSet()) {
+ time = calculateAlarm(alarm.hour, alarm.minutes,
+ alarm.daysOfWeek).getTimeInMillis();
}
-
- long time = calculateAlarm(enableAlarm.mHour, enableAlarm.mMinutes,
- enableAlarm.mDaysOfWeek).getTimeInMillis();
- values.put(AlarmColumns.ALARM_TIME, time);
+ values.put(Alarm.Columns.ALARM_TIME, time);
}
- resolver.update(ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, id),
- values, null, null);
+ resolver.update(ContentUris.withAppendedId(
+ Alarm.Columns.CONTENT_URI, alarm.id), values, null, null);
}
-
- /**
- * Calculates next scheduled alert
- */
- static class AlarmCalculator implements AlarmSettings {
- private long mMinAlert = Long.MAX_VALUE;
- private int mMinIdx = -1;
- private String mLabel;
-
- /**
- * returns next scheduled alert, MAX_VALUE if none
- */
- public long getAlert() {
- return mMinAlert;
- }
- public int getIndex() {
- return mMinIdx;
- }
- public String getLabel() {
- return mLabel;
- }
-
- public void reportAlarm(
- int idx, boolean enabled, int hour, int minutes,
- DaysOfWeek daysOfWeek, boolean vibrate, String message,
- String alert) {
- if (enabled) {
- long atTime = calculateAlarm(hour, minutes,
- daysOfWeek).getTimeInMillis();
- /* Log.i("** SET ALERT* idx " + idx + " hour " + hour + " minutes " +
- minutes + " enabled " + enabled + " calc " + atTime); */
- if (atTime < mMinAlert) {
- mMinIdx = idx;
- mMinAlert = atTime;
- mLabel = message;
- }
+ public static Alarm calculateNextAlert(final Context context) {
+ Alarm alarm = null;
+ long minTime = Long.MAX_VALUE;
+ Cursor cursor = getFilteredAlarmsCursor(context.getContentResolver());
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ do {
+ Alarm a = new Alarm(cursor);
+ // A time of 0 indicates this is a repeating alarm, so
+ // calculate the time to get the next alert.
+ if (a.time == 0) {
+ a.time = calculateAlarm(a.hour, a.minutes, a.daysOfWeek)
+ .getTimeInMillis();
+ }
+ if (a.time < minTime) {
+ minTime = a.time;
+ alarm = a;
+ }
+ } while (cursor.moveToNext());
}
+ cursor.close();
}
- }
-
- static AlarmCalculator calculateNextAlert(final Context context) {
- ContentResolver resolver = context.getContentResolver();
- AlarmCalculator alarmCalc = new AlarmCalculator();
- getAlarms(resolver, alarmCalc);
- return alarmCalc;
+ return alarm;
}
/**
* boot.
*/
public static void disableExpiredAlarms(final Context context) {
- Cursor cur = getAlarmsCursor(context.getContentResolver());
+ Cursor cur = getFilteredAlarmsCursor(context.getContentResolver());
long now = System.currentTimeMillis();
if (cur.moveToFirst()) {
do {
- // Get the field values
- int id = cur.getInt(AlarmColumns.ALARM_ID_INDEX);
- boolean enabled = cur.getInt(
- AlarmColumns.ALARM_ENABLED_INDEX) == 1 ? true : false;
- DaysOfWeek daysOfWeek = new DaysOfWeek(
- cur.getInt(AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
- long time = cur.getLong(AlarmColumns.ALARM_TIME_INDEX);
-
- if (enabled && !daysOfWeek.isRepeatSet() && time < now) {
- if (Log.LOGV) Log.v(
- "** DISABLE " + id + " now " + now +" set " + time);
- enableAlarmInternal(context, id, false);
+ Alarm alarm = new Alarm(cur);
+ // A time of 0 means this alarm repeats. If the time is
+ // non-zero, check if the time is before now.
+ if (alarm.time != 0 && alarm.time < now) {
+ if (Log.LOGV) {
+ Log.v("** DISABLE " + alarm.id + " now " + now +" set "
+ + alarm.time);
+ }
+ enableAlarmInternal(context, alarm, false);
}
} while (cur.moveToNext());
}
cur.close();
}
- private static NotificationManager getNotificationManager(
- final Context context) {
- return (NotificationManager) context.getSystemService(
- context.NOTIFICATION_SERVICE);
- }
-
/**
* Called at system startup, on time/timezone change, and whenever
* the user changes alarm settings. Activates snooze if set,
* otherwise loads all alarms, activates next alert.
*/
public static void setNextAlert(final Context context) {
- int snoozeId = getSnoozeAlarmId(context);
- if (snoozeId == -1) {
- AlarmCalculator ac = calculateNextAlert(context);
- int id = ac.getIndex();
- long atTime = ac.getAlert();
-
- if (atTime < Long.MAX_VALUE) {
- enableAlert(context, id, ac.getLabel(), atTime);
+ if (!enableSnoozeAlert(context)) {
+ Alarm alarm = calculateNextAlert(context);
+ if (alarm != null) {
+ enableAlert(context, alarm, alarm.time);
} else {
- disableAlert(context, id);
+ disableAlert(context);
}
- } else {
- enableSnoozeAlert(context);
}
}
* Sets alert in AlarmManger and StatusBar. This is what will
* actually launch the alert when the alarm triggers.
*
- * Note: In general, apps should call setNextAlert() instead of
- * this method. setAlert() is only used outside this class when
- * the alert is not to be driven by the state of the db. "Snooze"
- * uses this API, as we do not want to alter the alarm in the db
- * with each snooze.
- *
- * @param id Alarm ID.
+ * @param alarm Alarm.
* @param atTimeInMillis milliseconds since epoch
*/
- static void enableAlert(Context context, int id, String label,
- long atTimeInMillis) {
+ private static void enableAlert(Context context, final Alarm alarm,
+ final long atTimeInMillis) {
AlarmManager am = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(ALARM_ALERT_ACTION);
- if (Log.LOGV) Log.v("** setAlert id " + id + " atTime " + atTimeInMillis);
- intent.putExtra(ID, id);
- if (label != null) {
- intent.putExtra(LABEL, label);
+ if (Log.LOGV) {
+ Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis);
}
- intent.putExtra(TIME, atTimeInMillis);
+
+ Intent intent = new Intent(ALARM_ALERT_ACTION);
+
+ // XXX: This is a slight hack to avoid an exception in the remote
+ // AlarmManagerService process. The AlarmManager adds extra data to
+ // this Intent which causes it to inflate. Since the remote process
+ // does not know about the Alarm class, it throws a
+ // ClassNotFoundException.
+ //
+ // To avoid this, we marshall the data ourselves and then parcel a plain
+ // byte[] array. The AlarmReceiver class knows to build the Alarm
+ // object from the byte[] array.
+ Parcel out = Parcel.obtain();
+ alarm.writeToParcel(out, 0);
+ out.setDataPosition(0);
+ intent.putExtra(ALARM_RAW_DATA, out.marshall());
+
PendingIntent sender = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- if (true) {
- am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);
- } else {
- // a five-second alarm, for testing
- am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000,
- sender);
- }
+ am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);
setStatusBarIcon(context, true);
*
* @param id Alarm ID.
*/
- static void disableAlert(Context context, int id) {
+ static void disableAlert(Context context) {
AlarmManager am = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(ALARM_ALERT_ACTION);
- intent.putExtra(ID, id);
PendingIntent sender = PendingIntent.getBroadcast(
- context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ context, 0, new Intent(ALARM_ALERT_ACTION),
+ PendingIntent.FLAG_CANCEL_CURRENT);
am.cancel(sender);
setStatusBarIcon(context, false);
saveNextAlarm(context, "");
}
- static void saveSnoozeAlert(final Context context, int id,
- long atTimeInMillis, String label) {
+ static void saveSnoozeAlert(final Context context, final int id,
+ final long time) {
SharedPreferences prefs = context.getSharedPreferences(
AlarmClock.PREFERENCES, 0);
SharedPreferences.Editor ed = prefs.edit();
- ed.putInt(PREF_SNOOZE_ID, id);
- ed.putLong(PREF_SNOOZE_TIME, atTimeInMillis);
- if (label != null) {
- ed.putString(PREF_SNOOZE_LABEL, label);
+ if (id == -1) {
+ ed.clear();
+ } else {
+ ed.putInt(PREF_SNOOZE_ID, id);
+ ed.putLong(PREF_SNOOZE_TIME, time);
}
ed.commit();
+ // Set the next alert after updating the snooze.
+ setNextAlert(context);
}
/**
- * @return ID of alarm disabled, if disabled, -1 otherwise
- */
- static int disableSnoozeAlert(final Context context) {
- int id = getSnoozeAlarmId(context);
- if (id == -1) return -1;
- saveSnoozeAlert(context, -1, 0, null);
- return id;
- }
-
- /**
- * @return alarm ID of snoozing alarm, -1 if snooze unset
+ * Disable the snooze alert if the given id matches the snooze id.
*/
- private static int getSnoozeAlarmId(final Context context) {
+ static void disableSnoozeAlert(final Context context, final int id) {
SharedPreferences prefs = context.getSharedPreferences(
AlarmClock.PREFERENCES, 0);
- return prefs.getInt(PREF_SNOOZE_ID, -1);
+ int snoozeId = prefs.getInt(PREF_SNOOZE_ID, -1);
+ if (snoozeId == -1) {
+ // No snooze set, do nothing.
+ return;
+ } else if (snoozeId == id) {
+ // This is the same id so clear the shared prefs.
+ SharedPreferences.Editor ed = prefs.edit();
+ ed.clear();
+ ed.commit();
+ }
}
/**
AlarmClock.PREFERENCES, 0);
int id = prefs.getInt(PREF_SNOOZE_ID, -1);
- if (id == -1) return false;
- long atTimeInMillis = prefs.getLong(PREF_SNOOZE_TIME, -1);
- if (id == -1) return false;
- // Try to get the label from the snooze preference.
- String label = prefs.getString(PREF_SNOOZE_LABEL, null);
- enableAlert(context, id, label, atTimeInMillis);
+ if (id == -1) {
+ return false;
+ }
+ long time = prefs.getLong(PREF_SNOOZE_TIME, -1);
+
+ // Get the alarm from the db.
+ final Alarm alarm = getAlarm(context.getContentResolver(), id);
+ // The time in the database is either 0 (repeating) or a specific time
+ // for a non-repeating alarm. Update this value so the AlarmReceiver
+ // has the right time to compare.
+ alarm.time = time;
+
+ enableAlert(context, alarm, time);
return true;
}
-
/**
* Tells the StatusBar whether the alarm is enabled or disabled
*/
* @param minute 0-59
* @param daysOfWeek 0-59
*/
- static Calendar calculateAlarm(int hour, int minute, DaysOfWeek daysOfWeek) {
+ static Calendar calculateAlarm(int hour, int minute, Alarm.DaysOfWeek daysOfWeek) {
// start with now
Calendar c = Calendar.getInstance();
}
static String formatTime(final Context context, int hour, int minute,
- DaysOfWeek daysOfWeek) {
+ Alarm.DaysOfWeek daysOfWeek) {
Calendar c = calculateAlarm(hour, minute, daysOfWeek);
return formatTime(context, c);
}
public class RepeatPreference extends ListPreference {
// Initial value that can be set with the values saved in the database.
- private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek();
+ private Alarm.DaysOfWeek mDaysOfWeek = new Alarm.DaysOfWeek(0);
// New value that will be set if a positive result comes back from the
// dialog.
- private Alarms.DaysOfWeek mNewDaysOfWeek = new Alarms.DaysOfWeek();
+ private Alarm.DaysOfWeek mNewDaysOfWeek = new Alarm.DaysOfWeek(0);
public RepeatPreference(Context context, AttributeSet attrs) {
super(context, attrs);
});
}
- public void setDaysOfWeek(Alarms.DaysOfWeek dow) {
+ public void setDaysOfWeek(Alarm.DaysOfWeek dow) {
mDaysOfWeek.set(dow);
mNewDaysOfWeek.set(dow);
setSummary(dow.toString(getContext(), true));
}
- public Alarms.DaysOfWeek getDaysOfWeek() {
+ public Alarm.DaysOfWeek getDaysOfWeek() {
return mDaysOfWeek;
}
}
* Manages each alarm
*/
public class SetAlarm extends PreferenceActivity
- implements Alarms.AlarmSettings, TimePickerDialog.OnTimeSetListener {
+ implements TimePickerDialog.OnTimeSetListener {
private EditTextPreference mLabel;
private Preference mTimePref;
private int mHour;
private int mMinutes;
- private boolean mReportAlarmCalled;
-
/**
- * Set an alarm. Requires an Alarms.ID to be passed in as an
- * extra
+ * Set an alarm. Requires an Alarms.ALARM_ID to be passed in as an
+ * extra. FIXME: Pass an Alarm object like every other Activity.
*/
@Override
protected void onCreate(Bundle icicle) {
mRepeatPref = (RepeatPreference) findPreference("setRepeat");
Intent i = getIntent();
- mId = i.getIntExtra(Alarms.ID, -1);
+ mId = i.getIntExtra(Alarms.ALARM_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();
- }
+ Alarm alarm = Alarms.getAlarm(getContentResolver(), mId);
+ mLabel.setText(alarm.label);
+ mLabel.setSummary(alarm.label);
+ mHour = alarm.hour;
+ mMinutes = alarm.minutes;
+ mRepeatPref.setDaysOfWeek(alarm.daysOfWeek);
+ mVibratePref.setChecked(alarm.vibrate);
+ // Give the alert uri to the preference.
+ mAlarmPref.setAlert(alarm.alert);
+ updateTime();
// We have to do this to get the save/cancel buttons to highlight on
// their own.
updateTime();
}
- /**
- * 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) {
-
- mLabel.setText(label);
- mLabel.setSummary(label);
- mHour = hour;
- mMinutes = minutes;
- mRepeatPref.setDaysOfWeek(daysOfWeek);
- mVibratePref.setChecked(vibrate);
-
- Uri alertUri = null;
- if (Alarms.ALARM_ALERT_SILENT.equals(alert)) {
- if (Log.LOGV) {
- Log.v("reportAlarm: silent alert");
- }
- } else {
- if (alert != null && alert.length() != 0) {
- alertUri = Uri.parse(alert);
- }
-
- // If the database alert is null or it failed to parse, use the
- // default alert.
- if (alertUri == null) {
- alertUri = RingtoneManager.getDefaultUri(
- RingtoneManager.TYPE_ALARM);
- }
-
- if (Log.LOGV) {
- Log.v("reportAlarm alert: " + alert + " uri: " + alertUri);
- }
- }
-
- // Give the alert uri to the preference.
- mAlarmPref.setAlert(alertUri);
-
- updateTime();
-
- mReportAlarmCalled = true;
- }
-
private void updateTime() {
if (Log.LOGV) {
Log.v("updateTime " + mId);
*/
private static void saveAlarm(
Context context, int id, boolean enabled, int hour, int minute,
- Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String label,
+ Alarm.DaysOfWeek daysOfWeek, boolean vibrate, String label,
String alert, boolean popToast) {
if (Log.LOGV) Log.v("** saveAlarm " + id + " " + label + " " + enabled
+ " " + hour + " " + minute + " vibe " + vibrate);
* goes off. This helps prevent "am/pm" mistakes.
*/
static void popAlarmSetToast(Context context, int hour, int minute,
- Alarms.DaysOfWeek daysOfWeek) {
+ Alarm.DaysOfWeek daysOfWeek) {
String toastText = formatToast(context, hour, minute, daysOfWeek);
Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
* now"
*/
static String formatToast(Context context, int hour, int minute,
- Alarms.DaysOfWeek daysOfWeek) {
+ Alarm.DaysOfWeek daysOfWeek) {
long alarm = Alarms.calculateAlarm(hour, minute,
daysOfWeek).getTimeInMillis();
long delta = alarm - System.currentTimeMillis();;