Alarms.java revision 5c464d35f230912053d7a5873dd6b59ca6e11852
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.deskclock; 18 19import android.app.AlarmManager; 20import android.app.NotificationManager; 21import android.app.PendingIntent; 22import android.content.ContentResolver; 23import android.content.ContentUris; 24import android.content.ContentValues; 25import android.content.Context; 26import android.content.CursorLoader; 27import android.content.Intent; 28import android.content.SharedPreferences; 29import android.database.Cursor; 30import android.net.Uri; 31import android.os.Parcel; 32import android.provider.Settings; 33import android.text.format.DateFormat; 34 35import java.util.Calendar; 36import java.util.HashSet; 37import java.util.Set; 38 39/** 40 * The Alarms provider supplies info about Alarm Clock settings 41 */ 42public class Alarms { 43 44 static final String PREFERENCES = "AlarmClock"; 45 46 // This action triggers the AlarmReceiver as well as the AlarmKlaxon. It 47 // is a public action used in the manifest for receiving Alarm broadcasts 48 // from the alarm manager. 49 public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT"; 50 51 // A public action sent by AlarmKlaxon when the alarm has stopped sounding 52 // for any reason (e.g. because it has been dismissed from AlarmAlertFullScreen, 53 // or killed due to an incoming phone call, etc). 54 public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE"; 55 56 // AlarmAlertFullScreen listens for this broadcast intent, so that other applications 57 // can snooze the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION). 58 public static final String ALARM_SNOOZE_ACTION = "com.android.deskclock.ALARM_SNOOZE"; 59 60 // AlarmAlertFullScreen listens for this broadcast intent, so that other applications 61 // can dismiss the alarm (after ALARM_ALERT_ACTION and before ALARM_DONE_ACTION). 62 public static final String ALARM_DISMISS_ACTION = "com.android.deskclock.ALARM_DISMISS"; 63 64 // A public action sent by AlarmAlertFullScreen when a snoozed alarm was dismissed due 65 // to it handling ALARM_DISMISS_ACTION cancelled 66 public static final String ALARM_SNOOZE_CANCELLED = "com.android.deskclock.ALARM_SNOOZE_CANCELLED"; 67 68 // A broadcast sent every time the next alarm time is set in the system 69 public static final String NEXT_ALARM_TIME_SET = "com.android.deskclock.NEXT_ALARM_TIME_SET"; 70 71 // This is a private action used by the AlarmKlaxon to update the UI to 72 // show the alarm has been killed. 73 public static final String ALARM_KILLED = "alarm_killed"; 74 75 // Extra in the ALARM_KILLED intent to indicate to the user how long the 76 // alarm played before being killed. 77 public static final String ALARM_KILLED_TIMEOUT = "alarm_killed_timeout"; 78 79 // This string is used to indicate a silent alarm in the db. 80 public static final String ALARM_ALERT_SILENT = "silent"; 81 82 // This intent is sent from the notification when the user cancels the 83 // snooze alert. 84 public static final String CANCEL_SNOOZE = "cancel_snooze"; 85 86 // This string is used when passing an Alarm object through an intent. 87 public static final String ALARM_INTENT_EXTRA = "intent.extra.alarm"; 88 89 // This extra is the raw Alarm object data. It is used in the 90 // AlarmManagerService to avoid a ClassNotFoundException when filling in 91 // the Intent extras. 92 public static final String ALARM_RAW_DATA = "intent.extra.alarm_raw"; 93 94 private static final String PREF_SNOOZE_IDS = "snooze_ids"; 95 private static final String PREF_SNOOZE_TIME = "snooze_time"; 96 97 private final static String DM12 = "E h:mm aa"; 98 private final static String DM24 = "E kk:mm"; 99 100 private final static String M12 = "h:mm aa"; 101 // Shared with DigitalClock 102 final static String M24 = "kk:mm"; 103 104 final static int INVALID_ALARM_ID = -1; 105 106 /** 107 * Creates a new Alarm and fills in the given alarm's id. 108 */ 109 public static long addAlarm(Context context, Alarm alarm) { 110 ContentValues values = createContentValues(alarm); 111 Uri uri = context.getContentResolver().insert( 112 Alarm.Columns.CONTENT_URI, values); 113 alarm.id = (int) ContentUris.parseId(uri); 114 115 long timeInMillis = calculateAlarm(alarm); 116 if (alarm.enabled) { 117 clearSnoozeIfNeeded(context, timeInMillis); 118 } 119 setNextAlert(context); 120 return timeInMillis; 121 } 122 123 /** 124 * Removes an existing Alarm. If this alarm is snoozing, disables 125 * snooze. Sets next alert. 126 */ 127 public static void deleteAlarm(Context context, int alarmId) { 128 if (alarmId == INVALID_ALARM_ID) return; 129 130 ContentResolver contentResolver = context.getContentResolver(); 131 /* If alarm is snoozing, lose it */ 132 disableSnoozeAlert(context, alarmId); 133 134 Uri uri = ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId); 135 contentResolver.delete(uri, "", null); 136 137 setNextAlert(context); 138 } 139 140 141 public static CursorLoader getAlarmsCursorLoader(Context context) { 142 return new CursorLoader(context, Alarm.Columns.CONTENT_URI, 143 Alarm.Columns.ALARM_QUERY_COLUMNS, null, null, Alarm.Columns.DEFAULT_SORT_ORDER); 144 } 145 146 /** 147 * Queries all alarms 148 * @return cursor over all alarms 149 */ 150 public static Cursor getAlarmsCursor(ContentResolver contentResolver) { 151 return contentResolver.query( 152 Alarm.Columns.CONTENT_URI, Alarm.Columns.ALARM_QUERY_COLUMNS, 153 null, null, Alarm.Columns.DEFAULT_SORT_ORDER); 154 } 155 156 // Private method to get a more limited set of alarms from the database. 157 private static Cursor getFilteredAlarmsCursor( 158 ContentResolver contentResolver) { 159 return contentResolver.query(Alarm.Columns.CONTENT_URI, 160 Alarm.Columns.ALARM_QUERY_COLUMNS, Alarm.Columns.WHERE_ENABLED, 161 null, null); 162 } 163 164 private static ContentValues createContentValues(Alarm alarm) { 165 ContentValues values = new ContentValues(8); 166 // Set the alarm_time value if this alarm does not repeat. This will be 167 // used later to disable expire alarms. 168 long time = 0; 169 if (!alarm.daysOfWeek.isRepeatSet()) { 170 time = calculateAlarm(alarm); 171 } 172 173 // -1 means generate new id. 174 if (alarm.id != -1) { 175 values.put(Alarm.Columns._ID, alarm.id); 176 } 177 178 values.put(Alarm.Columns.ENABLED, alarm.enabled ? 1 : 0); 179 values.put(Alarm.Columns.HOUR, alarm.hour); 180 values.put(Alarm.Columns.MINUTES, alarm.minutes); 181 values.put(Alarm.Columns.ALARM_TIME, time); 182 values.put(Alarm.Columns.DAYS_OF_WEEK, alarm.daysOfWeek.getCoded()); 183 values.put(Alarm.Columns.VIBRATE, alarm.vibrate); 184 values.put(Alarm.Columns.MESSAGE, alarm.label); 185 186 // A null alert Uri indicates a silent alarm. 187 values.put(Alarm.Columns.ALERT, alarm.alert == null ? ALARM_ALERT_SILENT 188 : alarm.alert.toString()); 189 190 return values; 191 } 192 193 private static void clearSnoozeIfNeeded(Context context, long alarmTime) { 194 // If this alarm fires before the next snooze, clear the snooze to 195 // enable this alarm. 196 SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0); 197 198 // Get the list of snoozed alarms 199 final Set<String> snoozedIds = prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 200 for (String snoozedAlarm : snoozedIds) { 201 final long snoozeTime = prefs.getLong(getAlarmPrefSnoozeTimeKey(snoozedAlarm), 0); 202 if (alarmTime < snoozeTime) { 203 final int alarmId = Integer.parseInt(snoozedAlarm); 204 clearSnoozePreference(context, prefs, alarmId); 205 } 206 } 207 } 208 209 /** 210 * Return an Alarm object representing the alarm id in the database. 211 * Returns null if no alarm exists. 212 */ 213 public static Alarm getAlarm(ContentResolver contentResolver, int alarmId) { 214 Cursor cursor = contentResolver.query( 215 ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarmId), 216 Alarm.Columns.ALARM_QUERY_COLUMNS, 217 null, null, null); 218 Alarm alarm = null; 219 if (cursor != null) { 220 if (cursor.moveToFirst()) { 221 alarm = new Alarm(cursor); 222 } 223 cursor.close(); 224 } 225 return alarm; 226 } 227 228 229 /** 230 * A convenience method to set an alarm in the Alarms 231 * content provider. 232 * @return Time when the alarm will fire. Or < 1 if update failed. 233 */ 234 public static long setAlarm(Context context, Alarm alarm) { 235 ContentValues values = createContentValues(alarm); 236 ContentResolver resolver = context.getContentResolver(); 237 long rowsUpdated = resolver.update( 238 ContentUris.withAppendedId(Alarm.Columns.CONTENT_URI, alarm.id), 239 values, null, null); 240 if (rowsUpdated < 1) { 241 Log.e("Error updating alarm " + alarm); 242 return rowsUpdated; 243 } 244 245 long timeInMillis = calculateAlarm(alarm); 246 247 if (alarm.enabled) { 248 // Disable the snooze if we just changed the snoozed alarm. This 249 // only does work if the snoozed alarm is the same as the given 250 // alarm. 251 // TODO: disableSnoozeAlert should have a better name. 252 disableSnoozeAlert(context, alarm.id); 253 254 // Disable the snooze if this alarm fires before the snoozed alarm. 255 // This works on every alarm since the user most likely intends to 256 // have the modified alarm fire next. 257 clearSnoozeIfNeeded(context, timeInMillis); 258 } 259 260 setNextAlert(context); 261 262 return timeInMillis; 263 } 264 265 /** 266 * A convenience method to enable or disable an alarm. 267 * 268 * @param id corresponds to the _id column 269 * @param enabled corresponds to the ENABLED column 270 */ 271 272 public static void enableAlarm( 273 final Context context, final int id, boolean enabled) { 274 enableAlarmInternal(context, id, enabled); 275 setNextAlert(context); 276 } 277 278 private static void enableAlarmInternal(final Context context, 279 final int id, boolean enabled) { 280 enableAlarmInternal(context, getAlarm(context.getContentResolver(), id), 281 enabled); 282 } 283 284 private static void enableAlarmInternal(final Context context, 285 final Alarm alarm, boolean enabled) { 286 if (alarm == null) { 287 return; 288 } 289 ContentResolver resolver = context.getContentResolver(); 290 291 ContentValues values = new ContentValues(2); 292 values.put(Alarm.Columns.ENABLED, enabled ? 1 : 0); 293 294 // If we are enabling the alarm, calculate alarm time since the time 295 // value in Alarm may be old. 296 if (enabled) { 297 long time = 0; 298 if (!alarm.daysOfWeek.isRepeatSet()) { 299 time = calculateAlarm(alarm); 300 } 301 values.put(Alarm.Columns.ALARM_TIME, time); 302 } else { 303 // Clear the snooze if the id matches. 304 disableSnoozeAlert(context, alarm.id); 305 } 306 307 resolver.update(ContentUris.withAppendedId( 308 Alarm.Columns.CONTENT_URI, alarm.id), values, null, null); 309 } 310 311 private static Alarm calculateNextAlert(final Context context) { 312 long minTime = Long.MAX_VALUE; 313 long now = System.currentTimeMillis(); 314 final SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0); 315 316 Set<Alarm> alarms = new HashSet<Alarm>(); 317 318 // We need to to build the list of alarms from both the snoozed list and the scheduled 319 // list. For a non-repeating alarm, when it goes of, it becomes disabled. A snoozed 320 // non-repeating alarm is not in the active list in the database. 321 322 // first go through the snoozed alarms 323 final Set<String> snoozedIds = prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 324 for (String snoozedAlarm : snoozedIds) { 325 final int alarmId = Integer.parseInt(snoozedAlarm); 326 final Alarm a = getAlarm(context.getContentResolver(), alarmId); 327 alarms.add(a); 328 } 329 330 // Now add the scheduled alarms 331 final Cursor cursor = getFilteredAlarmsCursor(context.getContentResolver()); 332 if (cursor != null) { 333 try { 334 if (cursor.moveToFirst()) { 335 do { 336 final Alarm a = new Alarm(cursor); 337 alarms.add(a); 338 } while (cursor.moveToNext()); 339 } 340 } finally { 341 cursor.close(); 342 } 343 } 344 345 Alarm alarm = null; 346 347 for (Alarm a : alarms) { 348 // A time of 0 indicates this is a repeating alarm, so 349 // calculate the time to get the next alert. 350 if (a.time == 0) { 351 a.time = calculateAlarm(a); 352 } 353 354 // Update the alarm if it has been snoozed 355 updateAlarmTimeForSnooze(prefs, a); 356 357 if (a.time < now) { 358 Log.v("Disabling expired alarm set for " + Log.formatTime(a.time)); 359 // Expired alarm, disable it and move along. 360 enableAlarmInternal(context, a, false); 361 continue; 362 } 363 if (a.time < minTime) { 364 minTime = a.time; 365 alarm = a; 366 } 367 } 368 369 return alarm; 370 } 371 372 /** 373 * Disables non-repeating alarms that have passed. Called at 374 * boot. 375 */ 376 public static void disableExpiredAlarms(final Context context) { 377 Cursor cur = getFilteredAlarmsCursor(context.getContentResolver()); 378 long now = System.currentTimeMillis(); 379 380 try { 381 if (cur.moveToFirst()) { 382 do { 383 Alarm alarm = new Alarm(cur); 384 // A time of 0 means this alarm repeats. If the time is 385 // non-zero, check if the time is before now. 386 if (alarm.time != 0 && alarm.time < now) { 387 Log.v("Disabling expired alarm set for " + 388 Log.formatTime(alarm.time)); 389 enableAlarmInternal(context, alarm, false); 390 } 391 } while (cur.moveToNext()); 392 } 393 } finally { 394 cur.close(); 395 } 396 } 397 398 /** 399 * Called at system startup, on time/timezone change, and whenever 400 * the user changes alarm settings. Activates snooze if set, 401 * otherwise loads all alarms, activates next alert. 402 */ 403 public static void setNextAlert(final Context context) { 404 final Alarm alarm = calculateNextAlert(context); 405 if (alarm != null) { 406 enableAlert(context, alarm, alarm.time); 407 } else { 408 disableAlert(context); 409 } 410 Intent i = new Intent(NEXT_ALARM_TIME_SET); 411 context.sendBroadcast(i); 412 } 413 414 /** 415 * Sets alert in AlarmManger and StatusBar. This is what will 416 * actually launch the alert when the alarm triggers. 417 * 418 * @param alarm Alarm. 419 * @param atTimeInMillis milliseconds since epoch 420 */ 421 private static void enableAlert(Context context, final Alarm alarm, 422 final long atTimeInMillis) { 423 AlarmManager am = (AlarmManager) 424 context.getSystemService(Context.ALARM_SERVICE); 425 426 if (Log.LOGV) { 427 Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis); 428 } 429 430 Intent intent = new Intent(ALARM_ALERT_ACTION); 431 432 // XXX: This is a slight hack to avoid an exception in the remote 433 // AlarmManagerService process. The AlarmManager adds extra data to 434 // this Intent which causes it to inflate. Since the remote process 435 // does not know about the Alarm class, it throws a 436 // ClassNotFoundException. 437 // 438 // To avoid this, we marshall the data ourselves and then parcel a plain 439 // byte[] array. The AlarmReceiver class knows to build the Alarm 440 // object from the byte[] array. 441 Parcel out = Parcel.obtain(); 442 alarm.writeToParcel(out, 0); 443 out.setDataPosition(0); 444 intent.putExtra(ALARM_RAW_DATA, out.marshall()); 445 446 PendingIntent sender = PendingIntent.getBroadcast( 447 context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 448 449 am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender); 450 451 setStatusBarIcon(context, true); 452 453 Calendar c = Calendar.getInstance(); 454 c.setTimeInMillis(atTimeInMillis); 455 String timeString = formatDayAndTime(context, c); 456 saveNextAlarm(context, timeString); 457 } 458 459 /** 460 * Disables alert in AlarmManager and StatusBar. 461 * 462 * @param context The context 463 */ 464 static void disableAlert(Context context) { 465 AlarmManager am = (AlarmManager) 466 context.getSystemService(Context.ALARM_SERVICE); 467 PendingIntent sender = PendingIntent.getBroadcast( 468 context, 0, new Intent(ALARM_ALERT_ACTION), 469 PendingIntent.FLAG_CANCEL_CURRENT); 470 am.cancel(sender); 471 setStatusBarIcon(context, false); 472 saveNextAlarm(context, ""); 473 } 474 475 static void saveSnoozeAlert(final Context context, final int id, 476 final long time) { 477 SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0); 478 if (id == INVALID_ALARM_ID) { 479 clearAllSnoozePreferences(context, prefs); 480 } else { 481 final Set<String> snoozedIds = 482 prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 483 snoozedIds.add(Integer.toString(id)); 484 final SharedPreferences.Editor ed = prefs.edit(); 485 ed.putStringSet(PREF_SNOOZE_IDS, snoozedIds); 486 ed.putLong(getAlarmPrefSnoozeTimeKey(id), time); 487 ed.apply(); 488 } 489 // Set the next alert after updating the snooze. 490 setNextAlert(context); 491 } 492 493 private static String getAlarmPrefSnoozeTimeKey(int id) { 494 return getAlarmPrefSnoozeTimeKey(Integer.toString(id)); 495 } 496 497 private static String getAlarmPrefSnoozeTimeKey(String id) { 498 return PREF_SNOOZE_TIME + id; 499 } 500 501 /** 502 * Disable the snooze alert if the given id matches the snooze id. 503 */ 504 static void disableSnoozeAlert(final Context context, final int id) { 505 SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0); 506 if (hasAlarmBeenSnoozed(prefs, id)) { 507 // This is the same id so clear the shared prefs. 508 clearSnoozePreference(context, prefs, id); 509 } 510 } 511 512 // Helper to remove the snooze preference. Do not use clear because that 513 // will erase the clock preferences. Also clear the snooze notification in 514 // the window shade. 515 private static void clearSnoozePreference(final Context context, 516 final SharedPreferences prefs, final int id) { 517 final String alarmStr = Integer.toString(id); 518 final Set<String> snoozedIds = 519 prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 520 if (snoozedIds.contains(alarmStr)) { 521 NotificationManager nm = (NotificationManager) 522 context.getSystemService(Context.NOTIFICATION_SERVICE); 523 nm.cancel(id); 524 } 525 526 final SharedPreferences.Editor ed = prefs.edit(); 527 snoozedIds.remove(alarmStr); 528 ed.putStringSet(PREF_SNOOZE_IDS, snoozedIds); 529 ed.remove(getAlarmPrefSnoozeTimeKey(alarmStr)); 530 ed.apply(); 531 } 532 533 private static void clearAllSnoozePreferences(final Context context, 534 final SharedPreferences prefs) { 535 NotificationManager nm = (NotificationManager) 536 context.getSystemService(Context.NOTIFICATION_SERVICE); 537 final Set<String> snoozedIds = 538 prefs.getStringSet(PREF_SNOOZE_IDS, new HashSet<String>()); 539 final SharedPreferences.Editor ed = prefs.edit(); 540 for (String snoozeId : snoozedIds) { 541 nm.cancel(Integer.parseInt(snoozeId)); 542 ed.remove(getAlarmPrefSnoozeTimeKey(snoozeId)); 543 } 544 545 ed.remove(PREF_SNOOZE_IDS); 546 ed.apply(); 547 } 548 549 private static boolean hasAlarmBeenSnoozed(final SharedPreferences prefs, final int alarmId) { 550 final Set<String> snoozedIds = prefs.getStringSet(PREF_SNOOZE_IDS, null); 551 552 // Return true if there a valid snoozed alarmId was saved 553 return snoozedIds != null && snoozedIds.contains(Integer.toString(alarmId)); 554 } 555 556 /** 557 * Updates the specified Alarm with the additional snooze time. 558 * Returns a boolean indicating whether the alarm was updated. 559 */ 560 private static boolean updateAlarmTimeForSnooze( 561 final SharedPreferences prefs, final Alarm alarm) { 562 if (!hasAlarmBeenSnoozed(prefs, alarm.id)) { 563 // No need to modify the alarm 564 return false; 565 } 566 567 final long time = prefs.getLong(getAlarmPrefSnoozeTimeKey(alarm.id), -1); 568 // The time in the database is either 0 (repeating) or a specific time 569 // for a non-repeating alarm. Update this value so the AlarmReceiver 570 // has the right time to compare. 571 alarm.time = time; 572 573 return true; 574 } 575 576 /** 577 * Tells the StatusBar whether the alarm is enabled or disabled 578 */ 579 private static void setStatusBarIcon(Context context, boolean enabled) { 580 Intent alarmChanged = new Intent("android.intent.action.ALARM_CHANGED"); 581 alarmChanged.putExtra("alarmSet", enabled); 582 context.sendBroadcast(alarmChanged); 583 } 584 585 private static long calculateAlarm(Alarm alarm) { 586 return calculateAlarm(alarm.hour, alarm.minutes, alarm.daysOfWeek) 587 .getTimeInMillis(); 588 } 589 590 /** 591 * Given an alarm in hours and minutes, return a time suitable for 592 * setting in AlarmManager. 593 */ 594 static Calendar calculateAlarm(int hour, int minute, 595 Alarm.DaysOfWeek daysOfWeek) { 596 597 // start with now 598 Calendar c = Calendar.getInstance(); 599 c.setTimeInMillis(System.currentTimeMillis()); 600 601 int nowHour = c.get(Calendar.HOUR_OF_DAY); 602 int nowMinute = c.get(Calendar.MINUTE); 603 604 // if alarm is behind current time, advance one day 605 if (hour < nowHour || 606 hour == nowHour && minute <= nowMinute) { 607 c.add(Calendar.DAY_OF_YEAR, 1); 608 } 609 c.set(Calendar.HOUR_OF_DAY, hour); 610 c.set(Calendar.MINUTE, minute); 611 c.set(Calendar.SECOND, 0); 612 c.set(Calendar.MILLISECOND, 0); 613 614 int addDays = daysOfWeek.getNextAlarm(c); 615 if (addDays > 0) c.add(Calendar.DAY_OF_WEEK, addDays); 616 return c; 617 } 618 619 static String formatTime(final Context context, int hour, int minute, 620 Alarm.DaysOfWeek daysOfWeek) { 621 Calendar c = calculateAlarm(hour, minute, daysOfWeek); 622 return formatTime(context, c); 623 } 624 625 /* used by AlarmAlert */ 626 static String formatTime(final Context context, Calendar c) { 627 String format = get24HourMode(context) ? M24 : M12; 628 return (c == null) ? "" : (String)DateFormat.format(format, c); 629 } 630 631 /** 632 * Shows day and time -- used for lock screen 633 */ 634 private static String formatDayAndTime(final Context context, Calendar c) { 635 String format = get24HourMode(context) ? DM24 : DM12; 636 return (c == null) ? "" : (String)DateFormat.format(format, c); 637 } 638 639 /** 640 * Save time of the next alarm, as a formatted string, into the system 641 * settings so those who care can make use of it. 642 */ 643 static void saveNextAlarm(final Context context, String timeString) { 644 Settings.System.putString(context.getContentResolver(), 645 Settings.System.NEXT_ALARM_FORMATTED, 646 timeString); 647 } 648 649 /** 650 * @return true if clock is set to 24-hour mode 651 */ 652 public static boolean get24HourMode(final Context context) { 653 return android.text.format.DateFormat.is24HourFormat(context); 654 } 655} 656