diff --git a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java index 952d6b850..f2d3a53c2 100644 --- a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java +++ b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java @@ -76,7 +76,7 @@ public class GenericDao { * properties to read * @param id * id of item - * @return + * @return null if no item found */ public TYPE fetch(long id, Property... properties) { TodorooCursor cursor = fetchItem(id, properties); diff --git a/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java b/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java index cb9ff8371..56e229eb7 100644 --- a/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java +++ b/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java @@ -28,7 +28,7 @@ public interface NotificationManager { * */ public static class AndroidNotificationManager implements NotificationManager { - final android.app.NotificationManager nm; + private final android.app.NotificationManager nm; public AndroidNotificationManager(Context context) { nm = (android.app.NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/astrid/default.properties b/astrid/default.properties index c5d5335ee..694331ea4 100644 --- a/astrid/default.properties +++ b/astrid/default.properties @@ -10,5 +10,5 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=android-8 +target=android-7 apk-configurations= diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java b/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java index 6676cfe13..59e5aed22 100644 --- a/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java +++ b/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java @@ -4,7 +4,6 @@ import java.util.Date; import android.app.Activity; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -23,6 +22,8 @@ import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.ExceptionService; +import com.todoroo.andlib.service.NotificationManager; +import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.model.Task; @@ -36,7 +37,7 @@ public class Notifications extends BroadcastReceiver { static final String ID_KEY = "id"; //$NON-NLS-1$ /** notification type extra */ - static final String TYPE_KEY = "flags"; //$NON-NLS-1$ + static final String TYPE_KEY = "type"; //$NON-NLS-1$ // --- instance variables @@ -46,6 +47,8 @@ public class Notifications extends BroadcastReceiver { @Autowired private ExceptionService exceptionService; + public static NotificationManager notificationManager = null; + // --- alarm handling static { @@ -57,9 +60,11 @@ public class Notifications extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { DependencyInjectionService.getInstance().inject(this); ContextManager.setContext(context); + if(notificationManager == null) + notificationManager = new AndroidNotificationManager(context); long id = intent.getLongExtra(ID_KEY, 0); - int type = intent.getIntExtra(TYPE_KEY, 0); + int type = intent.getIntExtra(TYPE_KEY, (byte) 0); Resources r = context.getResources(); String reminder; @@ -71,9 +76,7 @@ public class Notifications extends BroadcastReceiver { reminder = getRandomReminder(r.getStringArray(R.array.reminders)); if(!showNotification(id, type, reminder)) { - NotificationManager nm = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel((int)id); + notificationManager.cancel((int)id); } } @@ -104,6 +107,9 @@ public class Notifications extends BroadcastReceiver { try { task = taskDao.fetch(id, Task.TITLE, Task.HIDE_UNTIL, Task.COMPLETION_DATE, Task.DELETION_DATE, Task.REMINDER_FLAGS); + if(task == null) + throw new IllegalArgumentException("cound not find item with id"); //$NON-NLS-1$ + } catch (Exception e) { exceptionService.reportError("show-notif", e); //$NON-NLS-1$ return false; @@ -129,7 +135,7 @@ public class Notifications extends BroadcastReceiver { boolean quietHours = false; Integer quietHoursStart = Preferences.getQuietHourStart(context); Integer quietHoursEnd = Preferences.getQuietHourEnd(context); - if(quietHoursStart != null && quietHoursEnd != null && nonstopMode) { + if(quietHoursStart != null && quietHoursEnd != null && !nonstopMode) { int hour = new Date().getHours(); if(quietHoursStart < quietHoursEnd) { if(hour >= quietHoursStart && hour < quietHoursEnd) @@ -140,8 +146,6 @@ public class Notifications extends BroadcastReceiver { } } - NotificationManager nm = (NotificationManager) context - .getSystemService(Context.NOTIFICATION_SERVICE); Resources r = context.getResources(); Intent notifyIntent = new Intent(context, NotificationActivity.class); @@ -228,9 +232,16 @@ public class Notifications extends BroadcastReceiver { if(Constants.DEBUG) Log.w("Astrid", "Logging notification: " + reminder); //$NON-NLS-1$ //$NON-NLS-2$ - nm.notify((int)id, notification); + notificationManager.notify((int)id, notification); return true; } + // --- notification manager + + public static void setNotificationManager( + NotificationManager notificationManager) { + Notifications.notificationManager = notificationManager; + } + } \ No newline at end of file diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java index 816ec673d..57ab1842c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java +++ b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java @@ -43,20 +43,22 @@ public final class ReminderService { }; /** flag for due date reminder */ - static final byte TYPE_DUE = 0; + static final int TYPE_DUE = 0; /** flag for overdue reminder */ - static final byte TYPE_OVERDUE = 1; + static final int TYPE_OVERDUE = 1; /** flag for random reminder */ - static final byte TYPE_RANDOM = 2; + static final int TYPE_RANDOM = 2; /** flag for a snoozed reminder */ - static final byte TYPE_SNOOZE = 3; + static final int TYPE_SNOOZE = 3; + + static final Random random = new Random(); // --- instance variables @Autowired private TaskDao taskDao; - static final Random random = new Random(); + private AlarmScheduler scheduler = new ReminderAlarmScheduler(); public ReminderService() { DependencyInjectionService.getInstance().inject(this); @@ -98,7 +100,7 @@ public final class ReminderService { * whether to check if task has requisite properties */ private void scheduleAlarm(Task task, boolean shouldPerformPropertyCheck) { - if(!task.isSaved()) + if(task == null || !task.isSaved()) return; // read data if necessary @@ -106,6 +108,8 @@ public final class ReminderService { for(Property property : PROPERTIES) { if(!task.containsValue(property)) { task = taskDao.fetch(task.getId(), PROPERTIES); + if(task == null) + return; break; } } @@ -121,11 +125,11 @@ public final class ReminderService { long whenOverdue = calculateNextOverdueReminder(task); if(whenRandom < whenDueDate && whenRandom < whenOverdue) - createAlarm(task, whenRandom, TYPE_RANDOM); + scheduler.createAlarm(task, whenRandom, TYPE_RANDOM); else if(whenDueDate < whenOverdue) - createAlarm(task, whenDueDate, TYPE_DUE); + scheduler.createAlarm(task, whenDueDate, TYPE_DUE); else if(whenOverdue != NO_ALARM) - createAlarm(task, whenOverdue, TYPE_OVERDUE); + scheduler.createAlarm(task, whenOverdue, TYPE_OVERDUE); } /** @@ -201,37 +205,55 @@ public final class ReminderService { // --- alarm manager alarm creation /** - * Create an alarm for the given task at the given type - * - * @param task - * @param time - * @param type - * @param flags + * Interface for testing */ - @SuppressWarnings("nls") - static void createAlarm(Task task, long time, byte type) { - if(time == 0 || time == NO_ALARM) - return; + interface AlarmScheduler { + public void createAlarm(Task task, long time, int type); + } - if(time < DateUtilities.now()) { - time = DateUtilities.now() + (long)((0.5f + - 4 * random.nextFloat()) * DateUtilities.ONE_HOUR); - } + public void setScheduler(AlarmScheduler scheduler) { + this.scheduler = scheduler; + } - Context context = ContextManager.getContext(); - Intent intent = new Intent(context, Notifications.class); - intent.setType(Long.toString(task.getId())); - intent.setAction(Integer.toString(type)); - intent.putExtra(Notifications.ID_KEY, task.getId()); - intent.putExtra(Notifications.TYPE_KEY, type); + public AlarmScheduler getScheduler() { + return scheduler; + } - AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, - intent, 0); + private class ReminderAlarmScheduler implements AlarmScheduler { + /** + * Create an alarm for the given task at the given type + * + * @param task + * @param time + * @param type + * @param flags + */ + @SuppressWarnings("nls") + public void createAlarm(Task task, long time, int type) { + if(time == 0 || time == NO_ALARM) + return; + + if(time < DateUtilities.now()) { + time = DateUtilities.now() + (long)((0.5f + + 4 * random.nextFloat()) * DateUtilities.ONE_HOUR); + } - if(Constants.DEBUG) - Log.e("Astrid", "Alarm (" + task.getId() + ", " + type + ") set for " + new Date(time)); - am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent); + Context context = ContextManager.getContext(); + Intent intent = new Intent(context, Notifications.class); + intent.setType(Long.toString(task.getId())); + intent.setAction(Integer.toString(type)); + intent.putExtra(Notifications.ID_KEY, task.getId()); + intent.putExtra(Notifications.TYPE_KEY, type); + + AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, + intent, 0); + + if(Constants.DEBUG) + Log.e("Astrid", "Alarm (" + task.getId() + ", " + type + + ") set for " + new Date(time)); + am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent); + } } // --- data fetching classes diff --git a/astrid/res/values/arrays.xml b/astrid/res/values/arrays.xml index 47f113a18..24c97a09f 100644 --- a/astrid/res/values/arrays.xml +++ b/astrid/res/values/arrays.xml @@ -61,6 +61,14 @@ Next Month + + + Don\'t hide + Task is due + Day before due + Week before due + + Pink diff --git a/astrid/res/values/keys.xml b/astrid/res/values/keys.xml index a1dc87b41..9f3efd1ba 100644 --- a/astrid/res/values/keys.xml +++ b/astrid/res/values/keys.xml @@ -144,19 +144,30 @@ p_def_urg - - + + + 0 + 1 + 2 + 3 + 4 + 5 + + + + 0 1 2 3 - 4 - 5 p_def_imp + + p_def_hide + backup diff --git a/astrid/res/values/strings-reminders.xml b/astrid/res/values/strings-reminders.xml index c8e411e85..ad53e94c5 100644 --- a/astrid/res/values/strings-reminders.xml +++ b/astrid/res/values/strings-reminders.xml @@ -2,91 +2,88 @@ - - - Absolute Deadline! - Goal Deadline! - Working on: - - You have $NUM tagged $TAG! + + + + Reminder Settings - + - - - Hi there! Have a sec? - Can I see you for a sec? - Have a few minutes? - Did you forget? - Excuse me! - When you have a minute: - On your agenda: - Free for a moment? - Astrid here! - Hi! Can I bug you? - A minute of your time? - It\'s a great day to - + + + Hi there! Have a sec? + Can I see you for a sec? + Have a few minutes? + Did you forget? + Excuse me! + When you have a minute: + On your agenda: + Free for a moment? + Astrid here! + Hi! Can I bug you? + A minute of your time? + It\'s a great day to + - - - Time to work! - Due date is here! - Ready to start? - You said you would do: - You\'re supposed to start: - Time to start: - It\'s time! - Excuse me! Time for - You free? Time to - + + + Time to work! + Due date is here! + Ready to start? + You said you would do: + You\'re supposed to start: + Time to start: + It\'s time! + Excuse me! Time for + You free? Time to + - - - Don\'t be lazy now! - Snooze time is up! - No more snoozing! - Now are you ready? - No more postponing! - - - - - I\'ve got something for you! - Ready to put this in the past? - Why don\'t you get this done? - How about it? Ready tiger? - Ready to do this? - Can you handle this? - You can be happy! Just finish this! - I promise you\'ll feel better if you finish this! - Won\'t you do this today? - Please finish this, I\'m sick of it! - Can you finish this? Yes you can! - Are you ever going to do this? - Feel good about yourself! Let\'s go! - I\'m so proud of you! Lets get it done! - A little snack after you finish this? - Just this one task? Please? - Time to shorten your todo list! - + + + Don\'t be lazy now! + Snooze time is up! + No more snoozing! + Now are you ready? + No more postponing! + + + + + I\'ve got something for you! + Ready to put this in the past? + Why don\'t you get this done? + How about it? Ready tiger? + Ready to do this? + Can you handle this? + You can be happy! Just finish this! + I promise you\'ll feel better if you finish this! + Won\'t you do this today? + Please finish this, I\'m sick of it! + Can you finish this? Yes you can! + Are you ever going to do this? + Feel good about yourself! Let\'s go! + I\'m so proud of you! Lets get it done! + A little snack after you finish this? + Just this one task? Please? + Time to shorten your todo list! + - - - Please tell me it isn\'t true that you\'re a procrastinator! - Doesn\'t being lazy get old sometimes? - Somewhere, someone is depending on you to finish this! - When you said postpone, you really meant \'I\'m doing this\', right? - This is the last time you postpone this, right? - Just finish this today, I won\'t tell anyone! - Why postpone when you can um... not postpone! - You\'ll finish this eventually, I presume? - I think you\'re really great! How about not putting this off? - Will you be able to achieve your goals if you do that? - Postpone, postpone, postpone. When will you change! - I\'ve had enough with your excuses! Just do it already! - Didn\'t you make that excuse last time? - I can\'t help you organize your life if you do that... - + + + Please tell me it isn\'t true that you\'re a procrastinator! + Doesn\'t being lazy get old sometimes? + Somewhere, someone is depending on you to finish this! + When you said postpone, you really meant \'I\'m doing this\', right? + This is the last time you postpone this, right? + Just finish this today, I won\'t tell anyone! + Why postpone when you can um... not postpone! + You\'ll finish this eventually, I presume? + I think you\'re really great! How about not putting this off? + Will you be able to achieve your goals if you do that? + Postpone, postpone, postpone. When will you change! + I\'ve had enough with your excuses! Just do it already! + Didn\'t you make that excuse last time? + I can\'t help you organize your life if you do that... + diff --git a/astrid/res/xml/preferences_reminders.xml b/astrid/res/xml/preferences_reminders.xml index 7108a8029..d5ccb4519 100644 --- a/astrid/res/xml/preferences_reminders.xml +++ b/astrid/res/xml/preferences_reminders.xml @@ -1,7 +1,7 @@ + android:title="@string/EPr_alerts_header"> 0; } + + /** + * @return repeat data structure. Requires REPEAT + */ + public RepeatInfo getRepeatInfo() { + return RepeatInfo.fromSingleField(getValue(Task.REPEAT)); + } } diff --git a/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java b/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java index d02730ce0..8f743d5cd 100644 --- a/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java +++ b/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java @@ -4,6 +4,7 @@ import java.util.Date; import java.util.HashMap; import java.util.Map.Entry; +import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.database.Cursor; @@ -100,7 +101,9 @@ public class Astrid2To3UpgradeHelper { Context context = ContextManager.getContext(); // pop up a progress dialog - ProgressDialog dialog = dialogUtilities.progressDialog(context, context.getString(R.string.DLG_wait)); + ProgressDialog dialog = null; + if(context instanceof Activity) + dialog = dialogUtilities.progressDialog(context, context.getString(R.string.DLG_wait)); // initiate a backup try { @@ -159,7 +162,8 @@ public class Astrid2To3UpgradeHelper { database.close(); - dialog.dismiss(); + if(dialog != null) + dialog.dismiss(); } // --- database upgrade helpers diff --git a/astrid/src/com/todoroo/astrid/utility/Preferences.java b/astrid/src/com/todoroo/astrid/utility/Preferences.java index 4a1684111..235bc5125 100644 --- a/astrid/src/com/todoroo/astrid/utility/Preferences.java +++ b/astrid/src/com/todoroo/astrid/utility/Preferences.java @@ -180,4 +180,14 @@ public class Preferences { editor.putBoolean(key, value); editor.commit(); } + + /** + * Sets string preference from integer value + */ + public static void setStringFromInteger(int keyResource, int newValue) { + Context context = ContextManager.getContext(); + Editor editor = getPrefs(context).edit(); + editor.putString(context.getString(keyResource), Integer.toString(newValue)); + editor.commit(); + } } diff --git a/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java b/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java index e1edf70ec..498aa30d0 100644 --- a/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java +++ b/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java @@ -12,9 +12,9 @@ public class TestDependencyInjector implements AbstractDependencyInjector { * Dependencies this class knows how to handle */ private final HashMap injectables = new HashMap(); - + private String name; - + public TestDependencyInjector(String name) { this.name = name; } @@ -22,26 +22,43 @@ public class TestDependencyInjector implements AbstractDependencyInjector { public void addInjectable(String field, Object injection) { injectables.put(field, injection); } - + public Object getInjection(Object object, Field field) { if(injectables.containsKey(field.getName())) { return injectables.get(field.getName()); } - + return null; } - + // --- static stuff - + /** * Install TestDependencyInjector above other injectors */ public synchronized static TestDependencyInjector initialize(String name) { - ArrayList list = + deinitialize(name); + + ArrayList list = new ArrayList(Arrays.asList(DependencyInjectionService.getInstance().getInjectors())); + + TestDependencyInjector instance = new TestDependencyInjector(name); + list.add(0, instance); + DependencyInjectionService.getInstance().setInjectors(list.toArray(new AbstractDependencyInjector[list.size()])); + return instance; + } + + /** + * Remove an installed TestDependencyInjector + * @param string + */ + public static void deinitialize(String name) { + ArrayList list = + new ArrayList(Arrays.asList(DependencyInjectionService.getInstance().getInjectors())); + for(Iterator i = list.iterator(); i.hasNext(); ) { AbstractDependencyInjector injector = i.next(); - + // if another one of these injectors already exists in the // stack, remove it if(injector instanceof TestDependencyInjector) { @@ -49,11 +66,6 @@ public class TestDependencyInjector implements AbstractDependencyInjector { i.remove(); } } - - TestDependencyInjector instance = new TestDependencyInjector(name); - list.add(0, instance); - DependencyInjectionService.getInstance().setInjectors(list.toArray(new AbstractDependencyInjector[list.size()])); - return instance; } } diff --git a/tests/src/com/todoroo/andlib/test/TranslationTests.java b/tests/src/com/todoroo/andlib/test/TranslationTests.java index 24b4f8893..2d44f9ec5 100644 --- a/tests/src/com/todoroo/andlib/test/TranslationTests.java +++ b/tests/src/com/todoroo/andlib/test/TranslationTests.java @@ -203,14 +203,24 @@ abstract public class TranslationTests extends TodorooTestCase { final Resources r = getContext().getResources(); final int[] arrays = getResourceIds(getArrayResources()); final int[] sizes = new int[arrays.length]; + final StringBuilder failures = new StringBuilder(); + for(int i = 0; i < arrays.length; i++) { - sizes[i] = r.getStringArray(arrays[i]).length; + try { + sizes[i] = r.getStringArray(arrays[i]).length; + } catch (Resources.NotFoundException e) { + String name = r.getResourceName(arrays[i]); + failures.append(String.format("error opening %s: %s\n", + name, e.getMessage())); + sizes[i] = -1; + } } - final StringBuilder failures = new StringBuilder(); forEachLocale(new Runnable() { public void run() { for(int i = 0; i < arrays.length; i++) { + if(sizes[i] == -1) + continue; int size = r.getStringArray(arrays[i]).length; if(size != sizes[i]) { String name = r.getResourceName(arrays[i]); diff --git a/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java b/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java index 290a00f55..f2fb6035e 100644 --- a/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java +++ b/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java @@ -28,6 +28,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; +import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo; import com.todoroo.astrid.legacy.data.AbstractController; import com.todoroo.astrid.legacy.data.AbstractModel; import com.todoroo.astrid.legacy.data.enums.Importance; @@ -354,6 +355,26 @@ public abstract class AbstractTaskModel extends AbstractModel { public int getValue() { return value; } + + public static int toSingleField(RepeatInfo repeatInfo) { + int repeat; + if(repeatInfo == null) + repeat = 0; + else + repeat = (repeatInfo.value << REPEAT_VALUE_OFFSET) + + repeatInfo.interval.ordinal(); + return repeat; + } + + public static RepeatInfo fromSingleField(int repeat) { + if(repeat == 0) + return null; + int value = repeat >> REPEAT_VALUE_OFFSET; + RepeatInterval interval = RepeatInterval.values() + [repeat - (value << REPEAT_VALUE_OFFSET)]; + + return new RepeatInfo(interval, value); + } } diff --git a/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java b/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java index 6ad360800..8105e58bc 100644 --- a/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java +++ b/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java @@ -530,7 +530,6 @@ public class TaskController extends AbstractController { */ public TaskController(Context activity) { super(activity); - Log.e("HEY", "task table is " + tasksTable); } /** @@ -547,7 +546,6 @@ public class TaskController extends AbstractController { SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper( context, tasksTable, tasksTable); database = databaseHelper.getWritableDatabase(); - Log.e("HEY", "task table is " + tasksTable); } /** Closes database resource */ diff --git a/tests/src/com/todoroo/astrid/model/TaskTests.java b/tests/src/com/todoroo/astrid/model/TaskTests.java index 6f21d5d2c..1b99ebcd2 100644 --- a/tests/src/com/todoroo/astrid/model/TaskTests.java +++ b/tests/src/com/todoroo/astrid/model/TaskTests.java @@ -22,20 +22,14 @@ public class TaskTests extends DatabaseTestCase { assertTrue(Task.IMPORTANCE_MUST_DO < Task.IMPORTANCE_SHOULD_DO); assertTrue(Task.IMPORTANCE_SHOULD_DO < Task.IMPORTANCE_NONE); - ArrayList urgencies = new ArrayList(); - urgencies.add(Task.URGENCY_NONE); - urgencies.add(Task.URGENCY_SPECIFIC_DAY); - urgencies.add(Task.URGENCY_SPECIFIC_DAY_TIME); - urgencies.add(Task.URGENCY_THIS_MONTH); - urgencies.add(Task.URGENCY_THIS_WEEK); - urgencies.add(Task.URGENCY_TODAY); - urgencies.add(Task.URGENCY_WITHIN_A_YEAR); - urgencies.add(Task.URGENCY_WITHIN_SIX_MONTHS); - urgencies.add(Task.URGENCY_WITHIN_THREE_MONTHS); + ArrayList reminderFlags = new ArrayList(); + reminderFlags.add(Task.NOTIFY_AFTER_DEADLINE); + reminderFlags.add(Task.NOTIFY_AT_DEADLINE); + reminderFlags.add(Task.NOTIFY_NONSTOP); // assert no duplicates - assertEquals(new TreeSet(urgencies).size(), - urgencies.size()); + assertEquals(new TreeSet(reminderFlags).size(), + reminderFlags.size()); } /** Check defaults */ @@ -46,7 +40,6 @@ public class TaskTests extends DatabaseTestCase { assertTrue(defaults.containsKey(Task.DUE_DATE.name)); assertTrue(defaults.containsKey(Task.HIDE_UNTIL.name)); assertTrue(defaults.containsKey(Task.COMPLETION_DATE.name)); - assertTrue(defaults.containsKey(Task.URGENCY.name)); assertTrue(defaults.containsKey(Task.IMPORTANCE.name)); } diff --git a/tests/src/com/todoroo/astrid/reminders/NotificationTests.java b/tests/src/com/todoroo/astrid/reminders/NotificationTests.java new file mode 100644 index 000000000..85ca7e50e --- /dev/null +++ b/tests/src/com/todoroo/astrid/reminders/NotificationTests.java @@ -0,0 +1,179 @@ +package com.todoroo.astrid.reminders; + +import java.util.Date; + +import android.app.Notification; +import android.content.Intent; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.NotificationManager; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.test.DatabaseTestCase; +import com.todoroo.astrid.utility.Preferences; + +public class NotificationTests extends DatabaseTestCase { + + @Autowired + TaskDao taskDao; + + public class MutableBoolean { + boolean value = false; + } + + /** test that a normal task gets a notification */ + public void testAlarmToNotification() { + final Task task = new Task(); + task.setValue(Task.TITLE, "rubberduck"); + taskDao.persist(task); + + final MutableBoolean triggered = new MutableBoolean(); + + Notifications.setNotificationManager(new TestNotificationManager() { + public void notify(int id, Notification notification) { + assertNotNull(notification.contentIntent); + triggered.value = true; + } + + }); + + Intent intent = new Intent(); + intent.putExtra(Notifications.ID_KEY, task.getId()); + intent.putExtra(Notifications.TYPE_KEY, ReminderService.TYPE_DUE); + new Notifications().onReceive(getContext(), intent); + assertTrue(triggered.value); + } + + /** test that a deleted task doesn't get a notification */ + public void testDeletedTask() { + final Task task = new Task(); + task.setValue(Task.TITLE, "gooeyduck"); + task.setValue(Task.DELETION_DATE, DateUtilities.now()); + taskDao.persist(task); + + Notifications.setNotificationManager(new NotificationManager() { + + public void cancel(int id) { + // allowed + } + + public void cancelAll() { + fail("wtf cancel all?"); + } + + public void notify(int id, Notification notification) { + fail("sent a notification, you shouldn't have..."); + } + + }); + + Intent intent = new Intent(); + intent.putExtra(Notifications.ID_KEY, task.getId()); + intent.putExtra(Notifications.TYPE_KEY, ReminderService.TYPE_DUE); + new Notifications().onReceive(getContext(), intent); + } + + /** test that a completed task doesn't get a notification */ + public void testCompletedTask() { + final Task task = new Task(); + task.setValue(Task.TITLE, "rubberduck"); + task.setValue(Task.COMPLETION_DATE, DateUtilities.now()); + taskDao.persist(task); + + Notifications.setNotificationManager(new NotificationManager() { + + public void cancel(int id) { + // allowed + } + + public void cancelAll() { + fail("wtf cancel all?"); + } + + public void notify(int id, Notification notification) { + fail("sent a notification, you shouldn't have..."); + } + + }); + + Intent intent = new Intent(); + intent.putExtra(Notifications.ID_KEY, task.getId()); + intent.putExtra(Notifications.TYPE_KEY, ReminderService.TYPE_DUE); + new Notifications().onReceive(getContext(), intent); + } + + /** test of quiet hours */ + public void testQuietHours() { + final Task task = new Task(); + task.setValue(Task.TITLE, "rubberduck"); + taskDao.persist(task); + Intent intent = new Intent(); + intent.putExtra(Notifications.ID_KEY, task.getId()); + + int hour = new Date().getHours(); + Preferences.setStringFromInteger(R.string.p_notif_quietStart, hour - 1); + Preferences.setStringFromInteger(R.string.p_notif_quietEnd, hour + 1); + + // due date notification has vibrate + Notifications.setNotificationManager(new TestNotificationManager() { + public void notify(int id, Notification notification) { + assertNull(notification.sound); + assertTrue((notification.defaults & Notification.DEFAULT_SOUND) == 0); + assertNotNull(notification.vibrate); + assertTrue(notification.vibrate.length > 0); + } + }); + intent.putExtra(Notifications.TYPE_KEY, ReminderService.TYPE_DUE); + new Notifications().onReceive(getContext(), intent); + + // random notification does not + Notifications.setNotificationManager(new TestNotificationManager() { + public void notify(int id, Notification notification) { + assertNull(notification.sound); + assertTrue((notification.defaults & Notification.DEFAULT_SOUND) == 0); + assertTrue(notification.vibrate == null || + notification.vibrate.length == 0); + } + }); + intent.removeExtra(Notifications.TYPE_KEY); + intent.putExtra(Notifications.TYPE_KEY, ReminderService.TYPE_RANDOM); + new Notifications().onReceive(getContext(), intent); + + // wrapping works + Preferences.setStringFromInteger(R.string.p_notif_quietStart, hour + 2); + Preferences.setStringFromInteger(R.string.p_notif_quietEnd, hour + 1); + + Notifications.setNotificationManager(new TestNotificationManager() { + public void notify(int id, Notification notification) { + assertNull(notification.sound); + assertTrue((notification.defaults & Notification.DEFAULT_SOUND) == 0); + } + }); + intent.removeExtra(Notifications.TYPE_KEY); + intent.putExtra(Notifications.TYPE_KEY, ReminderService.TYPE_DUE); + new Notifications().onReceive(getContext(), intent); + + // nonstop notification still sounds + task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_NONSTOP); + task.save(); + Notifications.setNotificationManager(new TestNotificationManager() { + public void notify(int id, Notification notification) { + assertTrue(notification.sound != null || + (notification.defaults & Notification.DEFAULT_SOUND) > 0); + } + }); + new Notifications().onReceive(getContext(), intent); + } + + abstract public class TestNotificationManager implements NotificationManager { + public void cancel(int id) { + fail("wtf cance?"); + } + public void cancelAll() { + fail("wtf cancel all?"); + } + } + +} diff --git a/tests/src/com/todoroo/astrid/reminders/ReminderServiceTests.java b/tests/src/com/todoroo/astrid/reminders/ReminderServiceTests.java new file mode 100644 index 000000000..129ae624f --- /dev/null +++ b/tests/src/com/todoroo/astrid/reminders/ReminderServiceTests.java @@ -0,0 +1,172 @@ +package com.todoroo.astrid.reminders; + +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.reminders.ReminderService.AlarmScheduler; +import com.todoroo.astrid.test.DatabaseTestCase; +import com.todoroo.astrid.utility.Preferences; + +public class ReminderServiceTests extends DatabaseTestCase { + + ReminderService service; + + @Autowired + TaskDao taskDao; + + @Override + protected void setUp() throws Exception { + super.setUp(); + service = new ReminderService(); + Preferences.setPreferenceDefaults(); + } + + /** tests with no alarms */ + public void testNoReminders() { + service.setScheduler(new NoAlarmExpected()); + + Task task = new Task(); + task.setValue(Task.TITLE, "water"); + task.setValue(Task.REMINDER_FLAGS, 0); + taskDao.save(task, false); + service.scheduleAlarm(task); + } + + /** tests with due date */ + public void testDueDates() { + // test due date in the past + service.setScheduler(new NoAlarmExpected()); + final Task task = new Task(); + task.setValue(Task.TITLE, "water"); + task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_DAY); + task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE); + taskDao.save(task, false); + service.scheduleAlarm(task); + + // test due date in the future + task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY); + taskDao.save(task, false); + service.setScheduler(new AlarmExpected() { + @Override + public void createAlarm(Task task, long time, int type) { + super.createAlarm(task, time, type); + assertEquals((long)task.getValue(Task.DUE_DATE), time); + assertEquals(type, ReminderService.TYPE_DUE); + } + }); + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + } + + /** tests with random */ + public void testRandom() { + // test random + final Task task = new Task(); + task.setValue(Task.TITLE, "water"); + task.setValue(Task.REMINDER_PERIOD, DateUtilities.ONE_WEEK); + taskDao.save(task, false); + service.setScheduler(new AlarmExpected() { + @Override + public void createAlarm(Task task, long time, int type) { + super.createAlarm(task, time, type); + assertTrue(time > DateUtilities.now()); + assertTrue(time < DateUtilities.now() + 1.2 * DateUtilities.ONE_WEEK); + assertEquals(type, ReminderService.TYPE_RANDOM); + } + }); + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + + // test random with last notify way in the past + task.setValue(Task.REMINDER_LAST, DateUtilities.now() - 2 * DateUtilities.ONE_WEEK); + ((AlarmExpected)service.getScheduler()).alarmCreated = false; + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + } + + /** tests with overdue */ + public void testOverdue() { + // test due date in the future + service.setScheduler(new NoAlarmExpected()); + final Task task = new Task(); + task.setValue(Task.TITLE, "water"); + task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY); + task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE); + taskDao.save(task, false); + service.scheduleAlarm(task); + + // test due date in the past + task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_DAY); + taskDao.save(task, false); + service.setScheduler(new AlarmExpected() { + @Override + public void createAlarm(Task task, long time, int type) { + super.createAlarm(task, time, type); + assertTrue(time > DateUtilities.now()); + assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY); + assertEquals(type, ReminderService.TYPE_OVERDUE); + } + }); + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + } + + /** tests with multiple */ + public void testMultipleReminders() { + // test due date in the future, enable random + final Task task = new Task(); + task.setValue(Task.TITLE, "water"); + task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_WEEK); + task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE); + task.setValue(Task.REMINDER_PERIOD, DateUtilities.ONE_DAY); + taskDao.save(task, false); + service.setScheduler(new AlarmExpected() { + @Override + public void createAlarm(Task task, long time, int type) { + super.createAlarm(task, time, type); + assertTrue(time > DateUtilities.now()); + assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY); + assertEquals(type, ReminderService.TYPE_RANDOM); + } + }); + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + + // now set the due date in the past + task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_WEEK); + ((AlarmExpected)service.getScheduler()).alarmCreated = false; + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + + // now set the due date before the random + task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_HOUR); + taskDao.save(task, false); + service.setScheduler(new AlarmExpected() { + @Override + public void createAlarm(Task task, long time, int type) { + super.createAlarm(task, time, type); + assertEquals((long)task.getValue(Task.DUE_DATE), time); + assertEquals(type, ReminderService.TYPE_DUE); + } + }); + service.scheduleAlarm(task); + assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); + } + + // --- helper classes + + public class NoAlarmExpected implements AlarmScheduler { + public void createAlarm(Task task, long time, int type) { + fail("created alarm, no alarm expected"); + } + } + + public class AlarmExpected implements AlarmScheduler { + public boolean alarmCreated = false; + public void createAlarm(Task task, long time, int type) { + alarmCreated = true; + } + } + +} diff --git a/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java b/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java index dedce0b8e..bd2918758 100644 --- a/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java +++ b/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java @@ -17,12 +17,6 @@ import com.todoroo.astrid.service.AstridDependencyInjector; */ public class DatabaseTestCase extends TodorooTestCase { - private static final String SYNC_TEST = "synctest"; - private static final String ALERTS_TEST = "alertstest"; - private static final String TAG_TASK_TEST = "tagtasktest"; - private static final String TAGS_TEST = "tagstest"; - private static final String TASKS_TEST = "taskstest"; - public static Database database = new TestDatabase(); public AlarmDatabase alarmsDatabase; @@ -32,11 +26,6 @@ public class DatabaseTestCase extends TodorooTestCase { // initialize test dependency injector TestDependencyInjector injector = TestDependencyInjector.initialize("db"); - injector.addInjectable("tasksTable", TASKS_TEST); - injector.addInjectable("tagsTable", TAGS_TEST); - injector.addInjectable("tagTaskTable", TAG_TASK_TEST); - injector.addInjectable("alertsTable", ALERTS_TEST); - injector.addInjectable("syncTable", SYNC_TEST); injector.addInjectable("database", database); } @@ -48,19 +37,15 @@ public class DatabaseTestCase extends TodorooTestCase { // empty out test databases database.clear(); - deleteDatabase(TASKS_TEST); - deleteDatabase(TAGS_TEST); - deleteDatabase(TAG_TASK_TEST); - deleteDatabase(ALERTS_TEST); - deleteDatabase(SYNC_TEST); - alarmsDatabase = new AlarmDatabase(); - alarmsDatabase.clear(); - database.openForWriting(); } - private void deleteDatabase(String database) { + /** + * Helper to delete a database by name + * @param database + */ + protected void deleteDatabase(String database) { File db = getContext().getDatabasePath(database); if(db.exists()) db.delete(); diff --git a/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java b/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java index 56a3651cf..b461cc4fb 100644 --- a/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java +++ b/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java @@ -4,8 +4,10 @@ import java.util.Date; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.TestDependencyInjector; import com.todoroo.andlib.sql.Query; import com.todoroo.astrid.alarms.Alarm; +import com.todoroo.astrid.alarms.AlarmDatabase; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.legacy.data.alerts.AlertController; import com.todoroo.astrid.legacy.data.enums.Importance; @@ -24,20 +26,63 @@ import com.todoroo.astrid.test.DatabaseTestCase; public class Astrid2To3UpgradeTests extends DatabaseTestCase { + // --- legacy table names + + private static final String SYNC_TEST = "synctest"; + private static final String ALERTS_TEST = "alertstest"; + private static final String TAG_TASK_TEST = "tagtasktest"; + private static final String TAGS_TEST = "tagstest"; + private static final String TASKS_TEST = "taskstest"; + + // --- setup and teardnwo + @Autowired TaskDao taskDao; + @Override + protected void setUp() throws Exception { + super.setUp(); + + // initialize test dependency injector + TestDependencyInjector injector = TestDependencyInjector.initialize("upgrade"); + injector.addInjectable("tasksTable", TASKS_TEST); + injector.addInjectable("tagsTable", TAGS_TEST); + injector.addInjectable("tagTaskTable", TAG_TASK_TEST); + injector.addInjectable("alertsTable", ALERTS_TEST); + injector.addInjectable("syncTable", SYNC_TEST); + injector.addInjectable("database", database); + + deleteDatabase(TASKS_TEST); + deleteDatabase(TAGS_TEST); + deleteDatabase(TAG_TASK_TEST); + deleteDatabase(ALERTS_TEST); + deleteDatabase(SYNC_TEST); + + alarmsDatabase = new AlarmDatabase(); + alarmsDatabase.clear(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + TestDependencyInjector.deinitialize("upgrade"); + } + + // --- helper methods + public void upgrade2To3() { new Astrid2To3UpgradeHelper().upgrade2To3(); } - public static void assertDatesEqual(Date old, int newDate) { + public static void assertDatesEqual(Date old, long newDate) { if(old == null) assertEquals(0, newDate); else - assertEquals(old.getTime() / 1000L, newDate); + assertEquals(old.getTime(), newDate); } + // --- tests + /** * Test upgrade doesn't crash and burn when there is nothing */ @@ -78,6 +123,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase { guti.setHiddenUntil(new Date()); guti.setRepeat(new RepeatInfo(RepeatInterval.DAYS, 10)); guti.setElapsedSeconds(500); + guti.setNotificationIntervalSeconds(200); taskController.saveTask(guti, false); Date createdDate = new Date(); @@ -96,22 +142,22 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase { Task task = new Task(tasks); assertEquals(griffey.getName(), task.getValue(Task.TITLE)); assertDatesEqual(griffey.getDefiniteDueDate(), task.getValue(Task.DUE_DATE)); - assertEquals((Integer)Task.IMPORTANCE_NONE, task.getValue(Task.IMPORTANCE)); + assertEquals(Task.IMPORTANCE_NONE, (int)task.getValue(Task.IMPORTANCE)); assertEquals(griffey.getEstimatedSeconds(), task.getValue(Task.ESTIMATED_SECONDS)); assertEquals(griffey.getNotes(), task.getValue(Task.NOTES)); - assertEquals((Integer)0, task.getValue(Task.REMINDER_LAST)); - assertEquals((Integer)0, task.getValue(Task.HIDE_UNTIL)); + assertEquals(0, (long)task.getValue(Task.REMINDER_LAST)); + assertEquals(0, (long)task.getValue(Task.HIDE_UNTIL)); tasks.moveToNext(); task = new Task(tasks); assertEquals(guti.getName(), task.getValue(Task.TITLE)); - assertDatesEqual(guti.getDefiniteDueDate(), task.getValue(Task.DUE_DATE)); - assertDatesEqual(guti.getPreferredDueDate(), task.getValue(Task.PREFERRED_DUE_DATE)); + assertDatesEqual(guti.getPreferredDueDate(), task.getValue(Task.DUE_DATE)); assertDatesEqual(guti.getHiddenUntil(), task.getValue(Task.HIDE_UNTIL)); assertEquals((Integer)Task.IMPORTANCE_DO_OR_DIE, task.getValue(Task.IMPORTANCE)); assertEquals(guti.getRepeat().getValue(), task.getRepeatInfo().getValue()); assertEquals(guti.getRepeat().getInterval().ordinal(), task.getRepeatInfo().getInterval().ordinal()); assertEquals(guti.getElapsedSeconds(), task.getValue(Task.ELAPSED_SECONDS)); + assertEquals(guti.getNotificationIntervalSeconds() * 1000L, (long)task.getValue(Task.REMINDER_PERIOD)); assertDatesEqual(createdDate, task.getValue(Task.CREATION_DATE)); assertDatesEqual(createdDate, task.getValue(Task.MODIFICATION_DATE)); } @@ -147,7 +193,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase { // verify that data exists in our new table database.openForReading(); - TagService tagService = new TagService(getContext()); + TagService tagService = new TagService(); Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA); assertEquals(2, tags.length); assertEquals("salty", tags[0].tag); @@ -201,7 +247,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase { // verify that data exists in our new table database.openForReading(); - TagService tagService = new TagService(getContext()); + TagService tagService = new TagService(); Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA); assertEquals(1, tags.length); assertEquals("attached", tags[0].tag);