Got the alarm stuff working... I think.

pull/14/head
Tim Su 17 years ago
parent 7c317b4b52
commit 4d46900f45

@ -23,7 +23,7 @@
<activity android:name=".activities.TagList"/> <activity android:name=".activities.TagList"/>
<receiver android:name=".utilities.Notifications"> <receiver android:name=".utilities.StartupReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.HOME" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 176 B

@ -207,10 +207,10 @@
</LinearLayout> </LinearLayout>
<TextView android:id="@+id/blockingon_label" <!--<TextView android:id="@+id/blockingon_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/importance_label" android:text="@string/blockingon_label"
style="@style/TextAppearance.EditEvent_Label"/> style="@style/TextAppearance.EditEvent_Label"/>
<LinearLayout <LinearLayout
@ -226,7 +226,7 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>-->
</LinearLayout> </LinearLayout>
<!-- MISC FIELDS --> <!-- MISC FIELDS -->

@ -142,9 +142,14 @@
<string name="tagList_context_edit">Edit Tag</string> <string name="tagList_context_edit">Edit Tag</string>
<string name="tagList_context_delete">Delete Tag</string> <string name="tagList_context_delete">Delete Tag</string>
<string name="tagList_menu_sortAlpha">Sort A-Z</string>
<string name="tagList_menu_sortSize">Sort by Size</string>
<!-- Dialog Boxes --> <!-- Dialog Boxes -->
<skip /> <skip />
<string name="question_title">Question</string> <string name="question_title">Question</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="delete_title">Delete</string> <string name="delete_title">Delete</string>
<string name="delete_this_task_title">Delete this task?</string> <string name="delete_this_task_title">Delete this task?</string>
@ -152,5 +157,7 @@
<string name="stop_timer_title">Stop the timer?</string> <string name="stop_timer_title">Stop the timer?</string>
<string name="alarm_toast_title">Next notification time: </string>
</resources> </resources>

@ -58,6 +58,8 @@ public class TagList extends Activity {
private static final int ACTIVITY_LIST = 0; private static final int ACTIVITY_LIST = 0;
private static final int ACTIVITY_CREATE = 1; private static final int ACTIVITY_CREATE = 1;
private static final int MENU_SORT_ALPHA_ID = Menu.FIRST;
private static final int MENU_SORT_SIZE_ID = Menu.FIRST + 1;
private static final int CONTEXT_CREATE_ID = Menu.FIRST + 10; private static final int CONTEXT_CREATE_ID = Menu.FIRST + 10;
private static final int CONTEXT_DELETE_ID = Menu.FIRST + 11; private static final int CONTEXT_DELETE_ID = Menu.FIRST + 11;
@ -228,4 +230,23 @@ public class TagList extends Activity {
super.onDestroy(); super.onDestroy();
controller.close(); controller.close();
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem item;
item = menu.add(Menu.NONE, MENU_SORT_ALPHA_ID, Menu.NONE,
R.string.tagList_menu_sortAlpha);
item.setIcon(android.R.drawable.ic_menu_sort_alphabetically);
item.setAlphabeticShortcut('a');
item = menu.add(Menu.NONE, MENU_SORT_SIZE_ID, Menu.NONE,
R.string.tagList_menu_sortSize);
item.setIcon(android.R.drawable.ic_menu_sort_by_size);
item.setAlphabeticShortcut('s');
return true;
}
} }

@ -77,7 +77,7 @@ public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
private static final int DELETE_ID = Menu.FIRST + 2; private static final int DELETE_ID = Menu.FIRST + 2;
// activity results // activity results
public static final int RESULT_DELETE = RESULT_FIRST_USER; public static final int RESULT_DELETE = RESULT_FIRST_USER + 10;
// other constants // other constants
private static final int MAX_TAGS = 5; private static final int MAX_TAGS = 5;
@ -146,7 +146,7 @@ public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
definiteDueDate.setDate(model.getDefiniteDueDate()); definiteDueDate.setDate(model.getDefiniteDueDate());
preferredDueDate.setDate(model.getPreferredDueDate()); preferredDueDate.setDate(model.getPreferredDueDate());
hiddenUntil.setDate(model.getHiddenUntil()); hiddenUntil.setDate(model.getHiddenUntil());
blockingOn.setBlockingOn(model.getBlockingOn()); // blockingOn.setBlockingOn(model.getBlockingOn());
notification.setTimeDuration(model.getNotificationIntervalSeconds()); notification.setTimeDuration(model.getNotificationIntervalSeconds());
notes.setText(model.getNotes()); notes.setText(model.getNotes());
@ -186,7 +186,7 @@ public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
model.setDefiniteDueDate(definiteDueDate.getDate()); model.setDefiniteDueDate(definiteDueDate.getDate());
model.setPreferredDueDate(preferredDueDate.getDate()); model.setPreferredDueDate(preferredDueDate.getDate());
model.setHiddenUntil(hiddenUntil.getDate()); model.setHiddenUntil(hiddenUntil.getDate());
model.setBlockingOn(blockingOn.getBlockingOn()); // model.setBlockingOn(blockingOn.getBlockingOn());
model.setNotes(notes.getText().toString()); model.setNotes(notes.getText().toString());
model.setNotificationIntervalSeconds(notification.getTimeDurationInSeconds()); model.setNotificationIntervalSeconds(notification.getTimeDurationInSeconds());
@ -198,10 +198,8 @@ public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
Log.e(getClass().getSimpleName(), "Error saving task!", e); // TODO Log.e(getClass().getSimpleName(), "Error saving task!", e); // TODO
} }
// recompute task visibility
// set up notification // set up notification
Notifications.scheduleNextNotification(this, model); Notifications.scheduleNextAlarm(this, model);
} }
/** Save task tags. Must be called after task already has an ID */ /** Save task tags. Must be called after task already has an ID */
@ -264,7 +262,7 @@ public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
TimeDurationType.HOURS_MINUTES); TimeDurationType.HOURS_MINUTES);
notification = new TimeDurationControlSet(this, R.id.notification, notification = new TimeDurationControlSet(this, R.id.notification,
R.string.notification_prefix, R.string.notification_dialog, R.string.notification_prefix, R.string.notification_dialog,
TimeDurationType.DAYS_HOURS); TimeDurationType.HOURS_MINUTES);
definiteDueDate = new DateControlSet(this, R.id.definiteDueDate_notnull, definiteDueDate = new DateControlSet(this, R.id.definiteDueDate_notnull,
R.id.definiteDueDate_date, R.id.definiteDueDate_time); R.id.definiteDueDate_date, R.id.definiteDueDate_time);
preferredDueDate = new DateControlSet(this, R.id.preferredDueDate_notnull, preferredDueDate = new DateControlSet(this, R.id.preferredDueDate_notnull,
@ -272,8 +270,8 @@ public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
hiddenUntil = new DateControlSet(this, R.id.hiddenUntil_notnull, hiddenUntil = new DateControlSet(this, R.id.hiddenUntil_notnull,
R.id.hiddenUntil_date, R.id.hiddenUntil_time); R.id.hiddenUntil_date, R.id.hiddenUntil_time);
notes = (EditText)findViewById(R.id.notes); notes = (EditText)findViewById(R.id.notes);
blockingOn = new BlockingOnControlSet(R.id.blockingOn_notnull, // blockingOn = new BlockingOnControlSet(R.id.blockingOn_notnull,
R.id.blockingon); // R.id.blockingon);
// individual ui component initialization // individual ui component initialization
ImportanceAdapter importanceAdapter = new ImportanceAdapter(this, ImportanceAdapter importanceAdapter = new ImportanceAdapter(this,

@ -175,7 +175,7 @@ public class TaskList extends Activity {
title.append(r.getQuantityString(R.plurals.Ntasks, title.append(r.getQuantityString(R.plurals.Ntasks,
taskArray.size(), taskArray.size())); taskArray.size(), taskArray.size()));
if(hiddenTasks > 0) if(hiddenTasks > 0)
title.append(" (").append(hiddenTasks).append(" "). title.append(" (+").append(hiddenTasks).append(" ").
append(r.getString(R.string.taskList_hiddenSuffix)).append(")"); append(r.getString(R.string.taskList_hiddenSuffix)).append(")");
setTitle(title); setTitle(title);
@ -228,6 +228,12 @@ public class TaskList extends Activity {
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, protected void onActivityResult(int requestCode, int resultCode,
Intent intent) { Intent intent) {
if(resultCode == TaskView.RESULT_DISMISS) {
finish();
return;
}
fillData(); fillData();
} }

@ -61,6 +61,9 @@ public class TaskView extends TaskModificationActivity<TaskModelForView> {
private static final int EDIT_ID = Menu.FIRST; private static final int EDIT_ID = Menu.FIRST;
private static final int DELETE_ID = Menu.FIRST + 1; private static final int DELETE_ID = Menu.FIRST + 1;
// activity results
public static final int RESULT_DISMISS = RESULT_FIRST_USER + 20;
// UI components // UI components
private TextView name; private TextView name;
private TextView elapsed; private TextView elapsed;
@ -105,7 +108,7 @@ public class TaskView extends TaskModificationActivity<TaskModelForView> {
// clear residual, schedule the next one // clear residual, schedule the next one
Notifications.clearAllNotifications(this, model.getTaskIdentifier()); Notifications.clearAllNotifications(this, model.getTaskIdentifier());
Notifications.scheduleNextNotification(this, model); Notifications.scheduleNextAlarm(this, model);
String[] responses = r.getStringArray(R.array.reminder_responses); String[] responses = r.getStringArray(R.array.reminder_responses);
String response = responses[new Random().nextInt(responses.length)]; String response = responses[new Random().nextInt(responses.length)];
@ -113,8 +116,8 @@ public class TaskView extends TaskModificationActivity<TaskModelForView> {
.setTitle(R.string.taskView_notifyTitle) .setTitle(R.string.taskView_notifyTitle)
.setMessage(response) .setMessage(response)
.setIcon(android.R.drawable.ic_dialog_alert) .setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.yes, null) .setPositiveButton(R.string.yes, null)
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_CANCELED); setResult(RESULT_CANCELED);

@ -31,6 +31,7 @@ import android.database.sqlite.SQLiteOpenHelper;
import com.timsu.astrid.data.AbstractController; import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper; import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
import com.timsu.astrid.utilities.Notifications;
/** Controller for task-related operations */ /** Controller for task-related operations */
public class TaskController extends AbstractController { public class TaskController extends AbstractController {
@ -118,6 +119,7 @@ public class TaskController extends AbstractController {
if(taskId == null) if(taskId == null)
throw new UnsupportedOperationException("Cannot delete uncreated task!"); throw new UnsupportedOperationException("Cannot delete uncreated task!");
long id = taskId.getId(); long id = taskId.getId();
Notifications.deleteAlarm(context, id);
return database.delete(TASK_TABLE_NAME, KEY_ROWID + "=" + id, null) > 0; return database.delete(TASK_TABLE_NAME, KEY_ROWID + "=" + id, null) > 0;
} }

@ -19,6 +19,8 @@
*/ */
package com.timsu.astrid.data.task; package com.timsu.astrid.data.task;
import java.util.Date;
import android.database.Cursor; import android.database.Cursor;
import com.timsu.astrid.data.AbstractController; import com.timsu.astrid.data.AbstractController;
@ -32,6 +34,7 @@ public class TaskModelForNotify extends AbstractTaskModel implements Notifiable
static String[] FIELD_LIST = new String[] { static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID, AbstractController.KEY_ROWID,
NOTIFICATIONS, NOTIFICATIONS,
HIDDEN_UNTIL,
}; };
// --- constructors // --- constructors
@ -39,6 +42,7 @@ public class TaskModelForNotify extends AbstractTaskModel implements Notifiable
public TaskModelForNotify(Cursor cursor) { public TaskModelForNotify(Cursor cursor) {
super(cursor); super(cursor);
getNotificationIntervalSeconds(); getNotificationIntervalSeconds();
getHiddenUntil();
} }
// --- getters and setters // --- getters and setters
@ -48,4 +52,8 @@ public class TaskModelForNotify extends AbstractTaskModel implements Notifiable
return super.getNotificationIntervalSeconds(); return super.getNotificationIntervalSeconds();
} }
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
} }

@ -42,6 +42,7 @@ public class TaskModelForView extends AbstractTaskModel implements Notifiable {
TIMER_START, TIMER_START,
DEFINITE_DUE_DATE, DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE, PREFERRED_DUE_DATE,
HIDDEN_UNTIL,
CREATION_DATE, CREATION_DATE,
NOTIFICATIONS, NOTIFICATIONS,
NOTES, NOTES,
@ -120,6 +121,11 @@ public class TaskModelForView extends AbstractTaskModel implements Notifiable {
return super.getCreationDate(); return super.getCreationDate();
} }
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override @Override
public void setTimerStart(Date timerStart) { public void setTimerStart(Date timerStart) {
super.setTimerStart(timerStart); super.setTimerStart(timerStart);

@ -1,9 +1,11 @@
package com.timsu.astrid.utilities; package com.timsu.astrid.utilities;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import android.app.Activity; import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -21,53 +23,82 @@ import com.timsu.astrid.data.task.TaskModelForNotify;
public class Notifications extends BroadcastReceiver { public class Notifications extends BroadcastReceiver {
private static final int MIN_INTERVAL_SECONDS = 60; private static final String ID_KEY = "id";
private static final int MIN_INTERVAL_SECONDS = 120;
private static Random random = new Random(); private static Random random = new Random();
/** Something we can create a notification for */
public interface Notifiable {
public TaskIdentifier getTaskIdentifier();
public Integer getNotificationIntervalSeconds();
public Date getHiddenUntil();
}
@Override @Override
/** Startup intent */ /** Startup intent */
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
NotificationManager nm = (NotificationManager) context. long id = intent.getLongExtra(ID_KEY, 0);
getSystemService(Context.NOTIFICATION_SERVICE); Log.e("ALARM", "Alarm triggered id " + id);
showNotification(context, id);
Notification notification = new Notification( }
android.R.drawable.stat_notify_chat, "started up",
System.currentTimeMillis());
nm.notify(0, notification); // --- alarm manager stuff
public static void scheduleAllAlarms(Context context) {
TaskController controller = new TaskController(context); TaskController controller = new TaskController(context);
controller.open(); controller.open();
List<TaskModelForNotify> tasks = controller.getTasksWithNotifications(); List<TaskModelForNotify> tasks = controller.getTasksWithNotifications();
for(TaskModelForNotify task : tasks) for(TaskModelForNotify task : tasks)
scheduleNextNotification(context, task); scheduleNextAlarm(context, task);
}
public interface Notifiable {
public TaskIdentifier getTaskIdentifier();
public Integer getNotificationIntervalSeconds();
} }
/** Schedules the next notification for this task */ /** Schedules the next notification for this task */
public static void scheduleNextNotification(Context context, public static void scheduleNextAlarm(Context context,
Notifiable task) { Notifiable task) {
if(task.getNotificationIntervalSeconds() == null || if(task.getNotificationIntervalSeconds() == null ||
task.getNotificationIntervalSeconds() == 0 || task.getNotificationIntervalSeconds() == 0 ||
task.getTaskIdentifier() == null) task.getTaskIdentifier() == null)
return; return;
// TODO if task is hidden, disregard if(task.getHiddenUntil() != null && task.getHiddenUntil().after(new Date()))
return;
// compute, and add a fudge factor to mix things up a bit
int interval = task.getNotificationIntervalSeconds();
int currentSeconds = (int)(System.currentTimeMillis() / 1000);
int untilNextInterval = interval - currentSeconds % interval;
untilNextInterval *= 0.2f + random.nextFloat() * 0.6f;
if(untilNextInterval < MIN_INTERVAL_SECONDS)
untilNextInterval = MIN_INTERVAL_SECONDS;
long when = System.currentTimeMillis() + untilNextInterval * 1000;
scheduleAlarm(context, task.getTaskIdentifier().getId(), when);
}
/** Delete the given alarm */
public static void deleteAlarm(Context context, long id) {
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
// add a fudge factor to mix things up a bit Intent intent = new Intent(context, Notifications.class);
int nextSeconds = (int)((random.nextFloat() * 0.2f + 0.8f) * intent.putExtra(ID_KEY, id);
task.getNotificationIntervalSeconds()/60); // TODO remove /60 PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
if(nextSeconds < MIN_INTERVAL_SECONDS)
nextSeconds = MIN_INTERVAL_SECONDS; am.cancel(sender);
long when = System.currentTimeMillis() + nextSeconds * 1000; }
scheduleNotification(context, task.getTaskIdentifier(), when);
/** Schedules a single alarm */
public static void scheduleAlarm(Context context, long id, long when) {
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Notifications.class);
intent.putExtra(ID_KEY, id);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
Log.e("ALARM", "Alarm set for " + new Date(when));
am.set(AlarmManager.RTC, when, sender);
} }
// --- notification manager stuff
/** Clear notifications associated with this application */ /** Clear notifications associated with this application */
public static void clearAllNotifications(Context context, TaskIdentifier taskId) { public static void clearAllNotifications(Context context, TaskIdentifier taskId) {
@ -77,16 +108,14 @@ public class Notifications extends BroadcastReceiver {
} }
/** Schedule a new notification about the given task */ /** Schedule a new notification about the given task */
public static void scheduleNotification(Context context, public static void showNotification(Context context, long id) {
TaskIdentifier taskId, long when) {
NotificationManager nm = (NotificationManager) context NotificationManager nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE); .getSystemService(Context.NOTIFICATION_SERVICE);
Resources r = context.getResources(); Resources r = context.getResources();
Intent notifyIntent = new Intent(context, TaskView.class); Intent notifyIntent = new Intent(context, TaskView.class);
notifyIntent.putExtra(TaskView.LOAD_INSTANCE_TOKEN, notifyIntent.putExtra(TaskView.LOAD_INSTANCE_TOKEN, id);
taskId.getId());
notifyIntent.putExtra(TaskView.FROM_NOTIFICATION_TOKEN, true); notifyIntent.putExtra(TaskView.FROM_NOTIFICATION_TOKEN, true);
PendingIntent pendingIntent = PendingIntent.getActivity(context, PendingIntent pendingIntent = PendingIntent.getActivity(context,
0, notifyIntent, PendingIntent.FLAG_ONE_SHOT); 0, notifyIntent, PendingIntent.FLAG_ONE_SHOT);
@ -98,7 +127,8 @@ public class Notifications extends BroadcastReceiver {
String reminder = reminders[next]; String reminder = reminders[next];
Notification notification = new Notification( Notification notification = new Notification(
android.R.drawable.stat_notify_chat, reminder, when); android.R.drawable.stat_notify_chat, reminder,
System.currentTimeMillis());
notification.setLatestEventInfo(context, notification.setLatestEventInfo(context,
appName, appName,
@ -109,9 +139,8 @@ public class Notifications extends BroadcastReceiver {
notification.vibrate = new long[] { 300, 50, 50, 300, 100, 300, 100, notification.vibrate = new long[] { 300, 50, 50, 300, 100, 300, 100,
100, 200 }; 100, 200 };
Log.w("Notifications", "Logging notification: " + reminder + " for " + Log.w("Notifications", "Logging notification: " + reminder);
(when - System.currentTimeMillis())/1000 + " seconds from now"); nm.notify((int)id, notification);
nm.notify((int)taskId.getId(), notification);
} }
} }

@ -0,0 +1,14 @@
package com.timsu.astrid.utilities;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class StartupReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// do nothing...?
}
}

@ -94,8 +94,8 @@ public class NNumberPickerDialog extends AlertDialog implements OnClickListener
if(separators[i].length() < 3) if(separators[i].length() < 3)
text.setTextSize(48); text.setTextSize(48);
else else
text.setTextSize(24); text.setTextSize(20);
text.setGravity(Gravity.CENTER_VERTICAL); text.setGravity(Gravity.CENTER);
text.setLayoutParams(sepLayout); text.setLayoutParams(sepLayout);
container.addView(text); container.addView(text);
} }

Loading…
Cancel
Save