From d19d32fa86f054c11d646169d2141869eee9dd38 Mon Sep 17 00:00:00 2001 From: Phillip Hsu Date: Tue, 14 Jun 2016 21:41:14 -0700 Subject: [PATCH] Reschedule alarms after booting up --- app/src/main/AndroidManifest.xml | 15 +++ .../clock2/OnBootUpAlarmScheduler.java | 101 ++++++++++++++++++ .../philliphsu/clock2/OnBootUpReceiver.java | 18 ++++ .../clock2/ringtone/RingtoneActivity.java | 5 +- .../clock2/ringtone/RingtoneService.java | 30 ++++-- 5 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java create mode 100644 app/src/main/java/com/philliphsu/clock2/OnBootUpReceiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 70cf671..dd456d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java b/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java new file mode 100644 index 0000000..859b2d0 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java @@ -0,0 +1,101 @@ +package com.philliphsu.clock2; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; + +import com.philliphsu.clock2.model.AlarmsRepository; +import com.philliphsu.clock2.util.AlarmUtils; + +import java.util.List; + +/** + * An {@link IntentService} subclass for handling asynchronous task requests in + * a service on a separate handler thread. + */ +public class OnBootUpAlarmScheduler extends IntentService { + // TODO: Rename actions, choose action names that describe tasks that this + // IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS + private static final String ACTION_FOO = "com.philliphsu.clock2.action.FOO"; + private static final String ACTION_BAZ = "com.philliphsu.clock2.action.BAZ"; + + // TODO: Rename parameters + private static final String EXTRA_PARAM1 = "com.philliphsu.clock2.extra.PARAM1"; + private static final String EXTRA_PARAM2 = "com.philliphsu.clock2.extra.PARAM2"; + + public OnBootUpAlarmScheduler() { + super("OnBootUpAlarmScheduler"); + } + + /** + * Starts this service to perform action Foo with the given parameters. If + * the service is already performing a task this action will be queued. + * + * @see IntentService + */ + // TODO: Customize helper method + public static void startActionFoo(Context context, String param1, String param2) { + Intent intent = new Intent(context, OnBootUpAlarmScheduler.class); + intent.setAction(ACTION_FOO); + intent.putExtra(EXTRA_PARAM1, param1); + intent.putExtra(EXTRA_PARAM2, param2); + context.startService(intent); + } + + /** + * Starts this service to perform action Baz with the given parameters. If + * the service is already performing a task this action will be queued. + * + * @see IntentService + */ + // TODO: Customize helper method + public static void startActionBaz(Context context, String param1, String param2) { + Intent intent = new Intent(context, OnBootUpAlarmScheduler.class); + intent.setAction(ACTION_BAZ); + intent.putExtra(EXTRA_PARAM1, param1); + intent.putExtra(EXTRA_PARAM2, param2); + context.startService(intent); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent != null) { + List alarms = AlarmsRepository.getInstance(this).getItems(); + for (Alarm a : alarms) { + if (a.isEnabled()) { + AlarmUtils.scheduleAlarm(this, a, false); + } + } + +/* final String action = intent.getAction(); + if (ACTION_FOO.equals(action)) { + final String param1 = intent.getStringExtra(EXTRA_PARAM1); + final String param2 = intent.getStringExtra(EXTRA_PARAM2); + handleActionFoo(param1, param2); + } else if (ACTION_BAZ.equals(action)) { + final String param1 = intent.getStringExtra(EXTRA_PARAM1); + final String param2 = intent.getStringExtra(EXTRA_PARAM2); + handleActionBaz(param1, param2); + } +*/ + } + } + + /** + * Handle action Foo in the provided background thread with the provided + * parameters. + */ + private void handleActionFoo(String param1, String param2) { + // TODO: Handle action Foo + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Handle action Baz in the provided background thread with the provided + * parameters. + */ + private void handleActionBaz(String param1, String param2) { + // TODO: Handle action Baz + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/OnBootUpReceiver.java b/app/src/main/java/com/philliphsu/clock2/OnBootUpReceiver.java new file mode 100644 index 0000000..70d15f7 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/OnBootUpReceiver.java @@ -0,0 +1,18 @@ +package com.philliphsu.clock2; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * If the device is turned off, all alarms scheduled will be cancelled, and they will not be automatically + * rescheduled when it is turned on again. + */ +public class OnBootUpReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // Note that this will be called when the device boots up, not when the app first launches. + // We may have a lot of alarms to reschedule, so do this in the background using an IntentService. + context.startService(new Intent(context, OnBootUpAlarmScheduler.class)); + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java index e9ca66b..76e5b92 100644 --- a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java @@ -16,6 +16,7 @@ import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.R; import com.philliphsu.clock2.model.AlarmsRepository; import com.philliphsu.clock2.util.AlarmUtils; +import com.philliphsu.clock2.util.LocalBroadcastHelper; import static com.philliphsu.clock2.util.Preconditions.checkNotNull; @@ -97,9 +98,7 @@ public class RingtoneActivity extends AppCompatActivity { //super.onNewIntent(intent); // Not needed since no fragments hosted? // Notifies alarm missed and stops the service - // TODO: Find a better, cleaner way? Starting the service just to set a flag and stop itself - // is not very clear from this code. Perhaps the same Broadcast concept as done here. - startService(new Intent(this, RingtoneService.class).setAction(RingtoneService.ACTION_NOTIFY_MISSED)); + LocalBroadcastHelper.sendBroadcast(this, RingtoneService.ACTION_NOTIFY_MISSED); finish(); startActivity(intent); } diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java index 8abca31..c00e374 100644 --- a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java +++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java @@ -4,7 +4,10 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; @@ -15,6 +18,7 @@ import android.os.Vibrator; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; +import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.philliphsu.clock2.Alarm; @@ -65,6 +69,15 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc stopSelf(); } }; + private final BroadcastReceiver mNotifyMissedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mAutoSilenced = true; + // TODO: Do we need to call AlarmUtils.cancelAlarm()? + stopSelf(); + // Activity finishes itself + } + }; @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -73,13 +86,8 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc throw new IllegalStateException("No item id set"); Alarm alarm = checkNotNull(AlarmsRepository.getInstance(this).getItem(id)); - // TODO: Refactor to use switch block - if (intent.getAction() == null || intent.getAction().isEmpty()) { + if (intent.getAction() == null) { playRingtone(alarm); - } else if (ACTION_NOTIFY_MISSED.equals(intent.getAction())) { - mAutoSilenced = true; - stopSelf(startId); - // Activity finishes itself } else { if (ACTION_SNOOZE.equals(intent.getAction())) { AlarmUtils.snoozeAlarm(this, alarm); @@ -96,6 +104,13 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc return START_NOT_STICKY; // If killed while started, don't recreate. Should be sufficient. } + @Override + public void onCreate() { + super.onCreate(); + LocalBroadcastManager.getInstance(this).registerReceiver( + mNotifyMissedReceiver, new IntentFilter(ACTION_NOTIFY_MISSED)); + } + @Override public void onDestroy() { Log.d(TAG, "onDestroy()"); @@ -120,6 +135,7 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc nm.notify(getClass().getName(), mAlarm.intId(), note); } stopForeground(true); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mNotifyMissedReceiver); } @Override @@ -187,7 +203,7 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc // doing this in the respective subclass of this service. private void scheduleAutoSilence() { int minutes = AlarmUtils.minutesToSilenceAfter(this); - mSilenceHandler.postDelayed(mSilenceRunnable, /*minutes * 60000*/10000); // TODO: uncomment + mSilenceHandler.postDelayed(mSilenceRunnable, /*minutes * 60000*/70000); // TODO: uncomment } private void finishActivity() {