diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/deskclock/AlarmInitReceiver.java | 57 | ||||
-rw-r--r-- | src/com/android/deskclock/DeskClockBackupAgent.java | 85 |
2 files changed, 115 insertions, 27 deletions
diff --git a/src/com/android/deskclock/AlarmInitReceiver.java b/src/com/android/deskclock/AlarmInitReceiver.java index ec5813a40..e248e2fd1 100644 --- a/src/com/android/deskclock/AlarmInitReceiver.java +++ b/src/com/android/deskclock/AlarmInitReceiver.java @@ -33,8 +33,16 @@ public class AlarmInitReceiver extends BroadcastReceiver { private static final String PREF_VOLUME_DEF_DONE = "vol_def_done"; /** - * Sets alarm on ACTION_BOOT_COMPLETED. Resets alarm on - * TIME_SET, TIMEZONE_CHANGED + * This receiver handles a variety of actions: + * + * <ul> + * <li>Clean up backup data that was recently restored to this device on + * ACTION_COMPLETE_RESTORE.</li> + * <li>Clean up backup data that was recently restored to this device and reset timers and + * clear stopwatch on ACTION_BOOT_COMPLETED</li> + * <li>Fix alarm states on ACTION_BOOT_COMPLETED, TIME_SET, TIMEZONE_CHANGED, + * and LOCALE_CHANGED</li> + * </ul> */ @Override public void onReceive(final Context context, Intent intent) { @@ -45,32 +53,37 @@ public class AlarmInitReceiver extends BroadcastReceiver { final WakeLock wl = AlarmAlertWakeLock.createPartialWakeLock(context); wl.acquire(); - // We need to increment the global id out of the async task to prevent - // race conditions + // We need to increment the global id out of the async task to prevent race conditions AlarmStateManager.updateGlobalIntentId(context); + AsyncHandler.post(new Runnable() { @Override public void run() { - if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { - // Clear stopwatch and timers data - SharedPreferences prefs = - PreferenceManager.getDefaultSharedPreferences(context); - LogUtils.v("AlarmInitReceiver - Reset timers and clear stopwatch data"); - TimerObj.resetTimersInSharedPrefs(prefs); - Utils.clearSwSharedPref(prefs); + try { + if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + // Clear stopwatch and timers data + final SharedPreferences prefs = + PreferenceManager.getDefaultSharedPreferences(context); + LogUtils.v("AlarmInitReceiver - Reset timers and clear stopwatch data"); + TimerObj.resetTimersInSharedPrefs(prefs); + Utils.clearSwSharedPref(prefs); - if (!prefs.getBoolean(PREF_VOLUME_DEF_DONE, false)) { - // Fix the default - LogUtils.v("AlarmInitReceiver - resetting volume button default"); - switchVolumeButtonDefault(prefs); + if (!prefs.getBoolean(PREF_VOLUME_DEF_DONE, false)) { + // Fix the default + LogUtils.v("AlarmInitReceiver - resetting volume button default"); + switchVolumeButtonDefault(prefs); + } } - } - - // Update all the alarm instances on time change event - AlarmStateManager.fixAlarmInstances(context); - result.finish(); - LogUtils.v("AlarmInitReceiver finished"); - wl.release(); + // Process restored data if any exists + if (!DeskClockBackupAgent.processRestoredData(context)) { + // Update all the alarm instances on time change event + AlarmStateManager.fixAlarmInstances(context); + } + } finally { + result.finish(); + wl.release(); + LogUtils.v("AlarmInitReceiver finished"); + } } }); } diff --git a/src/com/android/deskclock/DeskClockBackupAgent.java b/src/com/android/deskclock/DeskClockBackupAgent.java index 3eb63fe36..841382476 100644 --- a/src/com/android/deskclock/DeskClockBackupAgent.java +++ b/src/com/android/deskclock/DeskClockBackupAgent.java @@ -16,10 +16,19 @@ package com.android.deskclock; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import com.android.deskclock.alarms.AlarmStateManager; @@ -33,6 +42,11 @@ import java.util.List; public class DeskClockBackupAgent extends BackupAgent { + private static final String KEY_RESTORE_FINISHED = "restore_finished"; + + public static final String ACTION_COMPLETE_RESTORE = + "com.android.deskclock.action.COMPLETE_RESTORE"; + private static final String TAG = "DeskClockBackupAgent"; @Override @@ -56,28 +70,89 @@ public class DeskClockBackupAgent extends BackupAgent { super.onRestoreFile(data, size, destination, type, mode, mtime); } + /** + * When this method is called during backup/restore, the application is executing in a + * "minimalist" state. Because of this, the application's ContentResolver cannot be used. + * Consequently, the work of scheduling alarms on the restore device cannot be done here. + * Instead, a future callback to DeskClock is used as a signal to reschedule the alarms. The + * future callback may take the form of ACTION_BOOT_COMPLETED if the device is not yet fully + * booted (i.e. the restore occurred as part of the setup wizard). If the device is booted, an + * ACTION_COMPLETE_RESTORE broadcast is scheduled 10 seconds in the future to give + * backup/restore enough time to kill the Clock process. Both of these future callbacks result + * in the execution of {@link #processRestoredData(Context)}. + */ @Override public void onRestoreFinished() { - // Now that alarms have been restored, schedule them in AlarmManager. - final List<Alarm> alarms = Alarm.getAlarms(getContentResolver(), null); + // Write a preference to indicate a data restore has been completed. + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + prefs.edit().putBoolean(KEY_RESTORE_FINISHED, true).apply(); + + // If device boot is not yet completed, use ACTION_BOOT_COMPLETED to trigger completion of + // the data restore process at a safer time. + if (registerReceiver(null, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)) != null) { + LogUtils.i(TAG, "Waiting for %s to complete the data restore", + Intent.ACTION_BOOT_COMPLETED); + return; + } + + // Otherwise, the device is already booted, so schedule a custom broadcast to start the + // application in 10 seconds. + + // Create an Intent to send into DeskClock indicating restore is complete. + final PendingIntent restoreIntent = PendingIntent.getBroadcast(this, 0, + new Intent(ACTION_COMPLETE_RESTORE).setClass(this, AlarmInitReceiver.class), + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT); + + // Deliver the Intent 10 seconds from now. + final long triggerAtMillis = SystemClock.elapsedRealtime() + 10000; + + // Schedule the Intent delivery in AlarmManager. + final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, restoreIntent); + + LogUtils.i(TAG, "Waiting for %s to complete the data restore", ACTION_COMPLETE_RESTORE); + } + + /** + * @param context a context to access resources and services + * @return {@code true} if restore data was processed; {@code false} otherwise. + */ + public static boolean processRestoredData(Context context) { + // If the preference indicates data was not recently restored, there is nothing to do. + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + if (!prefs.getBoolean(KEY_RESTORE_FINISHED, false)) { + return false; + } + + LogUtils.i(TAG, "processRestoredData() started"); + + // Now that alarms have been restored, schedule new instances in AlarmManager. + final ContentResolver contentResolver = context.getContentResolver(); + final List<Alarm> alarms = Alarm.getAlarms(contentResolver, null); final Calendar now = Calendar.getInstance(); for (Alarm alarm : alarms) { // Remove any instances that may currently exist for the alarm; // these aren't relevant on the restore device and we'll recreate them below. - AlarmStateManager.deleteAllInstances(this, alarm.id); + AlarmStateManager.deleteAllInstances(context, alarm.id); if (alarm.enabled) { // Create the next alarm instance to schedule. AlarmInstance alarmInstance = alarm.createInstanceAfter(now); // Add the next alarm instance to the database. - alarmInstance = AlarmInstance.addInstance(getContentResolver(), alarmInstance); + alarmInstance = AlarmInstance.addInstance(contentResolver, alarmInstance); // Schedule the next alarm instance in AlarmManager. - AlarmStateManager.registerInstance(this, alarmInstance, true); + AlarmStateManager.registerInstance(context, alarmInstance, true); LogUtils.i(TAG, "DeskClockBackupAgent scheduled alarm instance: %s", alarmInstance); } } + + // Remove the preference to avoid executing this logic multiple times. + prefs.edit().remove(KEY_RESTORE_FINISHED).apply(); + + LogUtils.i(TAG, "processRestoredData() completed"); + return true; } }
\ No newline at end of file |