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