diff --git a/src/com/timsu/astrid/activities/TaskEdit.java b/src/com/timsu/astrid/activities/TaskEdit.java index 31c6a144e..e4901096f 100644 --- a/src/com/timsu/astrid/activities/TaskEdit.java +++ b/src/com/timsu/astrid/activities/TaskEdit.java @@ -221,8 +221,7 @@ public class TaskEdit extends TaskModificationTabbedActivity { // alerts if(model.getTaskIdentifier() != null) { - List alerts = alertController.getTaskAlerts(this, - model.getTaskIdentifier()); + List alerts = alertController.getTaskAlerts(model.getTaskIdentifier()); for(Date alert : alerts) { addAlert(alert); } diff --git a/src/com/timsu/astrid/activities/TaskViewNotifier.java b/src/com/timsu/astrid/activities/TaskViewNotifier.java index f3cbfe9b6..55a12cd0a 100644 --- a/src/com/timsu/astrid/activities/TaskViewNotifier.java +++ b/src/com/timsu/astrid/activities/TaskViewNotifier.java @@ -19,14 +19,24 @@ public class TaskViewNotifier extends TaskView { // bundle tokens public static final String FROM_NOTIFICATION_TOKEN = "notify"; public static final String NOTIF_FLAGS_TOKEN = "notif_flags"; + public static final String NOTIF_REPEAT_TOKEN = "notif_repeat"; + + // properties of the alarm that was triggered + private long repeatInterval = 0; + private int flags = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle extras = getIntent().getExtras(); - if(extras != null && extras.containsKey(FROM_NOTIFICATION_TOKEN)) + if(extras != null && extras.containsKey(FROM_NOTIFICATION_TOKEN)) { + if(extras.containsKey(NOTIF_REPEAT_TOKEN)) + repeatInterval = extras.getLong(NOTIF_REPEAT_TOKEN); + if(extras.containsKey(NOTIF_FLAGS_TOKEN)) + flags = extras.getInt(NOTIF_FLAGS_TOKEN); showNotificationAlert(); + } } /** Called when user clicks on a notification to get here */ @@ -75,7 +85,8 @@ public class TaskViewNotifier extends TaskView { @Override public void onNumberPicked(NumberPicker view, int number) { Notifications.createSnoozeAlarm(TaskViewNotifier.this, - model.getTaskIdentifier(), number * 60); + model.getTaskIdentifier(), number * 60, flags, + repeatInterval); setResult(Constants.RESULT_GO_HOME); TaskList.shouldCloseInstance = true; diff --git a/src/com/timsu/astrid/data/alerts/AlertController.java b/src/com/timsu/astrid/data/alerts/AlertController.java index a33a4c002..e42af1a43 100644 --- a/src/com/timsu/astrid/data/alerts/AlertController.java +++ b/src/com/timsu/astrid/data/alerts/AlertController.java @@ -23,7 +23,6 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; -import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -47,22 +46,25 @@ public class AlertController extends AbstractController { return cursor; } /** Get a list of tag identifiers for the given task */ - public List getTaskAlerts(Activity activity, TaskIdentifier + public List getTaskAlerts(TaskIdentifier taskId) throws SQLException { List list = new LinkedList(); Cursor cursor = alertDatabase.query(ALERT_TABLE_NAME, Alert.FIELD_LIST, Alert.TASK + " = ?", new String[] { taskId.idAsString() }, null, null, null); - activity.startManagingCursor(cursor); - if(cursor.getCount() == 0) - return list; - do { - cursor.moveToNext(); - list.add(new Alert(cursor).getDate()); - } while(!cursor.isLast()); + try { + if(cursor.getCount() == 0) + return list; + do { + cursor.moveToNext(); + list.add(new Alert(cursor).getDate()); + } while(!cursor.isLast()); - return list; + return list; + } finally { + cursor.close(); + } } /** Remove all alerts from the task */ diff --git a/src/com/timsu/astrid/data/task/TaskController.java b/src/com/timsu/astrid/data/task/TaskController.java index 2e914fa35..65e6e2ab4 100644 --- a/src/com/timsu/astrid/data/task/TaskController.java +++ b/src/com/timsu/astrid/data/task/TaskController.java @@ -192,7 +192,7 @@ public class TaskController extends AbstractController { TaskModelForRepeat repeatModel = new TaskModelForRepeat(cursor, values); RepeatInfo repeatInfo = repeatModel.getRepeat(); if(repeatInfo != null) - repeatModel.repeatTaskBy(repeatInfo); + repeatModel.repeatTaskBy(context, this, repeatInfo); cursor.close(); } diff --git a/src/com/timsu/astrid/data/task/TaskModelForRepeat.java b/src/com/timsu/astrid/data/task/TaskModelForRepeat.java index 7d128a4ed..9ed01f615 100644 --- a/src/com/timsu/astrid/data/task/TaskModelForRepeat.java +++ b/src/com/timsu/astrid/data/task/TaskModelForRepeat.java @@ -20,16 +20,21 @@ package com.timsu.astrid.data.task; import java.util.Date; +import java.util.List; import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; import com.timsu.astrid.data.AbstractController; +import com.timsu.astrid.data.alerts.AlertController; +import com.timsu.astrid.utilities.Notifications; +import com.timsu.astrid.utilities.Notifications.Notifiable; /** Fields that you would want to edit pertaining to repeats */ -public class TaskModelForRepeat extends AbstractTaskModel { +public class TaskModelForRepeat extends AbstractTaskModel implements Notifiable { static String[] FIELD_LIST = new String[] { AbstractController.KEY_ROWID, @@ -38,9 +43,14 @@ public class TaskModelForRepeat extends AbstractTaskModel { PREFERRED_DUE_DATE, HIDDEN_UNTIL, PROGRESS_PERCENTAGE, + ESTIMATED_SECONDS, + LAST_NOTIFIED, + NOTIFICATIONS, + NOTIFICATION_FLAGS, }; - public void repeatTaskBy(RepeatInfo repeatInfo) { + public void repeatTaskBy(Context context, TaskController taskController, + RepeatInfo repeatInfo) { if(getDefiniteDueDate() != null) setDefiniteDueDate(repeatInfo.shiftDate(getDefiniteDueDate())); if(getHiddenUntil() != null) @@ -49,7 +59,18 @@ public class TaskModelForRepeat extends AbstractTaskModel { setPreferredDueDate(repeatInfo.shiftDate(getPreferredDueDate())); setProgressPercentage(0); - // TODO shift fixed alerts? + // shift fixed alerts? + AlertController alertController = new AlertController(context); + alertController.open(); + List alerts = alertController.getTaskAlerts(getTaskIdentifier()); + alertController.removeAlerts(getTaskIdentifier()); + for(int i = 0; i < alerts.size(); i++) { + Date newAlert = repeatInfo.shiftDate(alerts.get(i)); + alertController.addAlert(getTaskIdentifier(), newAlert); + alerts.set(i, newAlert); + } + Notifications.updateAlarm(context, taskController, alertController, this); + alertController.close(); } // --- constructors @@ -66,6 +87,45 @@ public class TaskModelForRepeat extends AbstractTaskModel { return super.getRepeat(); } + @Override + public Integer getNotificationIntervalSeconds() { + return super.getNotificationIntervalSeconds(); + } + + @Override + public boolean isTaskCompleted() { + return super.isTaskCompleted(); + } + + @Override + public Date getDefiniteDueDate() { + return super.getDefiniteDueDate(); + } + + @Override + public Integer getEstimatedSeconds() { + return super.getEstimatedSeconds(); + } + + @Override + public Date getHiddenUntil() { + return super.getHiddenUntil(); + } + + @Override + public Date getPreferredDueDate() { + return super.getPreferredDueDate(); + } + @Override + public int getNotificationFlags() { + return super.getNotificationFlags(); + } + + @Override + public Date getLastNotificationDate() { + return super.getLastNotificationDate(); + } + @Override public void setDefiniteDueDate(Date definiteDueDate) { super.setDefiniteDueDate(definiteDueDate); @@ -80,4 +140,5 @@ public class TaskModelForRepeat extends AbstractTaskModel { public void setHiddenUntil(Date hiddenUntil) { super.setHiddenUntil(hiddenUntil); } + } diff --git a/src/com/timsu/astrid/utilities/Notifications.java b/src/com/timsu/astrid/utilities/Notifications.java index 760a94af4..6da176421 100644 --- a/src/com/timsu/astrid/utilities/Notifications.java +++ b/src/com/timsu/astrid/utilities/Notifications.java @@ -13,13 +13,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.database.Cursor; import android.net.Uri; import android.util.Log; import com.timsu.astrid.R; import com.timsu.astrid.activities.TaskViewNotifier; -import com.timsu.astrid.data.alerts.Alert; import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.task.TaskController; import com.timsu.astrid.data.task.TaskIdentifier; @@ -30,6 +28,7 @@ public class Notifications extends BroadcastReceiver { private static final String ID_KEY = "id"; private static final String FLAGS_KEY = "flags"; + private static final String REPEAT_KEY = "repeat"; // stuff for scheduling /** minimum # of seconds before a deadline to notify */ @@ -78,7 +77,9 @@ public class Notifications extends BroadcastReceiver { else reminder = getRandomReminder(r); - if(!showNotification(context, id, flags, reminder)) { + long repeatInterval = intent.getLongExtra(REPEAT_KEY, 0); + + if(!showNotification(context, id, flags, repeatInterval, reminder)) { deleteAlarm(context, id); NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); @@ -164,19 +165,22 @@ public class Notifications extends BroadcastReceiver { } // fixed alerts - Cursor cursor = alertController.getTaskAlertsCursor(task.getTaskIdentifier()); - Date currentDate = new Date(); + List alerts = alertController.getTaskAlerts(task.getTaskIdentifier()); + scheduleFixedAlerts(context, task.getTaskIdentifier(), alerts); + } + + /** Schedule a list of alerts for a task */ + public static void scheduleFixedAlerts(Context context, TaskIdentifier taskId, + List alerts) { int alertId = 0; - while(cursor.getCount() > 0 && !cursor.isLast()) { - cursor.moveToNext(); - Date alert = new Alert(cursor).getDate(); + Date currentDate = new Date(); + for(Date alert : alerts) { if(alert.before(currentDate)) continue; - scheduleAlarm(context, task.getTaskIdentifier().getId(), + scheduleAlarm(context, taskId.getId(), alert.getTime(), FLAG_FIXED | (alertId++ << FIXED_ID_SHIFT)); } - cursor.close(); } /** Schedule an alert around a deadline @@ -206,29 +210,39 @@ public class Notifications extends BroadcastReceiver { /** Create a 'snooze' reminder for this task */ public static void createSnoozeAlarm(Context context, TaskIdentifier id, - int secondsToSnooze) { - scheduleAlarm(context, id.getId(), System.currentTimeMillis() + + int secondsToSnooze, int flags, long repeatInterval) { + // if this is a one-off alarm, just schedule a snooze-type alarm + if(repeatInterval == 0) + scheduleAlarm(context, id.getId(), System.currentTimeMillis() + secondsToSnooze * 1000, FLAG_SNOOZE); + + // else, reschedule our normal alarm + else + scheduleRepeatingAlarm(context, id.getId(), System.currentTimeMillis() + + secondsToSnooze * 1000, flags, repeatInterval); } - /** Helper method to create a PendingIntent from an ID & flags */ - private static PendingIntent createPendingIntent(Context context, - long id, int flags) { + /** Helper method to create a Intent for alarm from an ID & flags */ + private static Intent createAlarmIntent(Context context, long id, int flags) { Intent intent = new Intent(context, Notifications.class); intent.setType(Long.toString(id)); intent.setAction(Integer.toString(flags)); intent.putExtra(ID_KEY, id); intent.putExtra(FLAGS_KEY, flags); - PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0); - return sender; + return intent; } /** Delete the given alarm */ public static void deleteAlarm(Context context, long id) { AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); - am.cancel(createPendingIntent(context, id, 0)); + // clear all possible alarms + for(int flag = 0; flag < (6 << FIXED_ID_SHIFT); flag++) { + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, + createAlarmIntent(context, id, flag), 0); + am.cancel(pendingIntent); + } // clear current notifications too clearAllNotifications(context, new TaskIdentifier(id)); @@ -238,9 +252,11 @@ public class Notifications extends BroadcastReceiver { public static void scheduleAlarm(Context context, long id, long when, int flags) { AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, + createAlarmIntent(context, id, flags), 0); - Log.e("Astrid", "Alarm set for " + new Date(when)); - am.set(AlarmManager.RTC_WAKEUP, when, createPendingIntent(context, id, flags)); + Log.e("Astrid", "Alarm (" + id + ", " + flags + ") set for " + new Date(when)); + am.set(AlarmManager.RTC_WAKEUP, when, pendingIntent); } /** Schedules a recurring alarm for a single task */ @@ -250,10 +266,14 @@ public class Notifications extends BroadcastReceiver { return; AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); - - Log.e("Astrid", "Alarm set for " + new Date(when) + " every " + interval/1000 + " s"); - am.setRepeating(AlarmManager.RTC_WAKEUP, when, interval, - createPendingIntent(context, id, flags)); + Intent alarmIntent = createAlarmIntent(context, id, flags); + alarmIntent.putExtra(REPEAT_KEY, interval); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, + alarmIntent, 0); + + Log.e("Astrid", "Alarm (" + id + ", " + flags + ") set for " + + new Date(when) + " every " + interval/1000 + " s"); + am.setRepeating(AlarmManager.RTC_WAKEUP, when, interval, pendingIntent); } // --- notification manager stuff @@ -275,7 +295,7 @@ public class Notifications extends BroadcastReceiver { /** Schedule a new notification about the given task. Returns false if there was * some sort of error or the alarm should be disabled. */ public static boolean showNotification(Context context, long id, - int flags, String reminder) { + int flags, long repeatInterval, String reminder) { String taskName; TaskController controller = new TaskController(context); @@ -336,6 +356,7 @@ public class Notifications extends BroadcastReceiver { notifyIntent.putExtra(TaskViewNotifier.LOAD_INSTANCE_TOKEN, id); notifyIntent.putExtra(TaskViewNotifier.FROM_NOTIFICATION_TOKEN, true); notifyIntent.putExtra(TaskViewNotifier.NOTIF_FLAGS_TOKEN, flags); + notifyIntent.putExtra(TaskViewNotifier.NOTIF_REPEAT_TOKEN, repeatInterval); PendingIntent pendingIntent = PendingIntent.getActivity(context, (int)id, notifyIntent, PendingIntent.FLAG_ONE_SHOT);