Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 9c31714b authored by Marten Gajda's avatar Marten Gajda Committed by GitHub
Browse files

Add initial recurrence support implements, #462 (#814)

This commit adds initial recurrence support in that recurring instances are handled correctly when instances are edited or deleted.
In order to support this, the UI now operates on the Instances table rather then the tasks table. The instances table now supports deletes, and updates like the tasks table with the difference that all these operations only affect the specific instance that's addressed. All the operations on the instances table are converted into operations on a task by creating exception instances and RDATEs or EXDATEs.

Inserting instances is currently not possible because only a fixed number of instances are expanded. This means an instance inserted beyond the expansion window would still not show up in the instances table.
See https://github.com/dmfs/opentasks/issues/741

There is still room for improvement in that we could split of completed tasks and modify recurrence rules when deleting instances from the start or end of the series. That's left to future updates.
Also there is no UI yet to create or edit recurrence properties itself.

At present there is only one upcoming instance expanded, so you can only see the "current" instance (and all completed ones). This may/will change in future but may require some UI changes as well.

In order to support this commit a number of tests have been implemented which test creating and altering recurring instances.

Currently an exception is created when a recurring instance is modified which wasn't an exception before. This approach is RFC 5545 compliant but not supported by Apple clients. This will be fixed in future updates.
parent 93562d69
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -776,8 +776,8 @@ public final class TaskContract
        String ORIGINAL_INSTANCE_ID = "original_instance_id";

        /**
         * The time in milliseconds since the Epoch of the original instance that is overridden by this instance or <code>null</code> if this task is not an
         * exception.
         * The time in milliseconds since the Epoch of the original instance that is overridden by this instance or <code>null</code> if this task is not a
         * recurring instance.
         * <p>
         * Value: Long
         * </p>
+34 −18
Original line number Diff line number Diff line
@@ -30,9 +30,10 @@ import org.dmfs.android.contentpal.RowSnapshot;
import org.dmfs.android.contentpal.operations.Assert;
import org.dmfs.android.contentpal.operations.BulkAssert;
import org.dmfs.android.contentpal.operations.BulkDelete;
import org.dmfs.android.contentpal.operations.BulkUpdate;
import org.dmfs.android.contentpal.operations.Counted;
import org.dmfs.android.contentpal.operations.Delete;
import org.dmfs.android.contentpal.operations.Put;
import org.dmfs.android.contentpal.predicates.ReferringTo;
import org.dmfs.android.contentpal.queues.BasicOperationsQueue;
import org.dmfs.android.contentpal.rowdata.CharSequenceRowData;
import org.dmfs.android.contentpal.rowdata.Composite;
@@ -57,6 +58,7 @@ import org.dmfs.tasks.contract.TaskContract.TaskLists;
import org.dmfs.tasks.contract.TaskContract.Tasks;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -123,6 +125,7 @@ public class TaskProviderInstancesTest
    /**
     * Create a single instance.
     */
    @Ignore("Inserting instances is currently unsupported.")
    @Test
    public void testInsertSingleInstance()
    {
@@ -158,15 +161,18 @@ public class TaskProviderInstancesTest
    public void testUpdateSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));
        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TasksTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                new Put<>(task, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // update the instance
                new Put<>(instance, new CharSequenceRowData<>(Tasks.TITLE, "Updated"))
                new BulkUpdate<>(
                        new InstanceTable(mAuthority),
                        new CharSequenceRowData<>(Tasks.TITLE, "Updated"),
                        new ReferringTo<>(Instances.TASK_ID, task))
        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the task list contains exactly one task with the title "Updated"
@@ -177,9 +183,12 @@ public class TaskProviderInstancesTest
                // the instances table contains one instance
                new Counted<>(1, new BulkAssert<>(new InstanceTable(mAuthority))),
                // the instances table contains the given instance
                new Assert<>(instance, new Composite<>(
                new Counted<>(1, new BulkAssert<>(
                        new InstanceTable(mAuthority),
                        new Composite<>(
                                new InstanceTestData(0),
                        new CharSequenceRowData<>(Tasks.TITLE, "Updated")))));
                                new CharSequenceRowData<>(Tasks.TITLE, "Updated")),
                        new ReferringTo<>(Instances.TASK_ID, task)))));
    }


@@ -190,15 +199,18 @@ public class TaskProviderInstancesTest
    public void testCompleteSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));
        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TasksTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                new Put<>(task, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // update the instance status
                new Put<>(instance, (transactionContext, builder) -> builder.withValue(Tasks.STATUS, Tasks.STATUS_COMPLETED))
                new BulkUpdate<>(
                        new InstanceTable(mAuthority),
                        (transactionContext, builder) -> builder.withValue(Tasks.STATUS, Tasks.STATUS_COMPLETED),
                        new ReferringTo<>(Instances.TASK_ID, task))
        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the task list contains exactly one task with the title "Updated"
@@ -209,9 +221,12 @@ public class TaskProviderInstancesTest
                // the instances table contains one instance
                new Counted<>(1, new BulkAssert<>(new InstanceTable(mAuthority))),
                // the instances table contains the given instance
                new Assert<>(instance, new Composite<>(
                new Counted<>(1, new BulkAssert<>(
                        new InstanceTable(mAuthority),
                        new Composite<>(
                                new InstanceTestData(-1),
                        new CharSequenceRowData<>(Tasks.TITLE, "task1")))));
                                new CharSequenceRowData<>(Tasks.TITLE, "task1")),
                        new ReferringTo<>(Instances.TASK_ID, task)))));
    }


@@ -222,15 +237,15 @@ public class TaskProviderInstancesTest
    public void testDeleteSingleInstance()
    {
        RowSnapshot<TaskLists> taskList = new VirtualRowSnapshot<>(new LocalTaskListsTable(mAuthority));
        RowSnapshot<Instances> instance = new VirtualRowSnapshot<>(new InstanceTable(mAuthority));
        RowSnapshot<Tasks> task = new VirtualRowSnapshot<>(new TasksTable(mAuthority));

        assertThat(new Seq<>(
                // create a local list
                new Put<>(taskList, new NameData("list1")),
                // insert a new task straight into the instances table
                new Put<>(instance, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // insert a new task
                new Put<>(task, new Referring<>(Tasks.LIST_ID, taskList, new CharSequenceRowData<>(Tasks.TITLE, "task1"))),
                // delete the instance
                new Delete<>(instance)
                new BulkDelete<>(new InstanceTable(mAuthority), new ReferringTo<>(Instances.TASK_ID, task))
        ), resultsIn(mClient,
                new Assert<>(taskList, new NameData("list1")),
                // the list does not contain a single task
@@ -260,7 +275,7 @@ public class TaskProviderInstancesTest
                new Put<>(task,
                        new Composite<>(
                                new Referring<>(Tasks.LIST_ID, taskList),
                                new TimeData(dateTime),
                                new TimeData<>(dateTime),
                                new TitleData("task1"))),
                new Put<>(instance,
                        new Composite<>(
@@ -279,6 +294,7 @@ public class TaskProviderInstancesTest
    /**
     * Create a single instance and insert an override for a new instance, turning the event into a recurring event.
     */
    @Ignore("Inserting instances is currently not supported.")
    @Test
    public void testInsertSingleInstanceAddAnother()
    {
+981 −0

File added.

Preview size limit exceeded, changes collapsed.

+34 −31
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import org.dmfs.android.contentpal.operations.BulkDelete;
import org.dmfs.android.contentpal.operations.BulkUpdate;
import org.dmfs.android.contentpal.operations.Delete;
import org.dmfs.android.contentpal.operations.Put;
import org.dmfs.android.contentpal.operations.Referring;
import org.dmfs.android.contentpal.predicates.ReferringTo;
import org.dmfs.android.contentpal.queues.BasicOperationsQueue;
import org.dmfs.android.contentpal.rowdata.CharSequenceRowData;
@@ -50,6 +49,7 @@ import org.dmfs.opentaskspal.tables.TaskListScoped;
import org.dmfs.opentaskspal.tables.TaskListsTable;
import org.dmfs.opentaskspal.tables.TasksTable;
import org.dmfs.opentaskspal.tasklists.NameData;
import org.dmfs.opentaskspal.tasks.OriginalInstanceData;
import org.dmfs.opentaskspal.tasks.OriginalInstanceSyncIdData;
import org.dmfs.opentaskspal.tasks.StatusData;
import org.dmfs.opentaskspal.tasks.SyncIdData;
@@ -308,11 +308,11 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<TaskLists>()),
                new Put<>(task, new TimeData(start, due))
                new Put<>(task, new TimeData<>(start, due))

        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(start, due),
                        new TimeData<>(start, due),
                        new VersionData(0))),
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -341,12 +341,12 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<>()),
                new Put<>(task, new TimeData(start, due)),
                new Put<>(task, new TimeData<>(start, due)),
                // update the status of the new task
                new Put<>(task, new StatusData(Tasks.STATUS_COMPLETED))
                new Put<>(task, new StatusData<>(Tasks.STATUS_COMPLETED))
        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(start, due),
                        new TimeData<>(start, due),
                        new VersionData(1))), // task has been updated once
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -375,14 +375,14 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<>()),
                new Put<>(task, new TimeData(start, due)),
                new Put<>(task, new TimeData<>(start, due)),
                // update the status of the new task
                new Put<>(task, new StatusData(Tasks.STATUS_COMPLETED)),
                new Put<>(task, new StatusData<>(Tasks.STATUS_COMPLETED)),
                // update the title of the new task
                new Put<>(task, new TitleData("Task Title"))
        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(start, due),
                        new TimeData<>(start, due),
                        new TitleData("Task Title"),
                        new VersionData(2))), // task has been updated twice
                new AssertRelated<>(
@@ -416,11 +416,11 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<TaskLists>()),
                new Put<>(task, new TimeData(start, due)),
                new Put<>(task, new TimeData(startNew, dueNew))
                new Put<>(task, new TimeData<>(start, due)),
                new Put<>(task, new TimeData<>(startNew, dueNew))
        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(startNew, dueNew),
                        new TimeData<>(startNew, dueNew),
                        new VersionData(1))),
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -453,11 +453,11 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<TaskLists>()),
                new Put<>(task, new TimeData(start, due)),
                new Put<>(task, new TimeData(startNew, dueNew))
                new Put<>(task, new TimeData<>(start, due)),
                new Put<>(task, new TimeData<>(startNew, dueNew))
        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(startNew, dueNew),
                        new TimeData<>(startNew, dueNew),
                        new VersionData(1))),
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -487,10 +487,10 @@ public class TaskProviderTest
        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<TaskLists>()),
                new Put<>(task, new TitleData("Test")),
                new Put<>(task, new TimeData(start, due))
                new Put<>(task, new TimeData<>(start, due))
        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(start, due),
                        new TimeData<>(start, due),
                        new VersionData(1))),
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -520,11 +520,11 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<TaskLists>()),
                new Put<>(task, new TimeData(start, duration))
                new Put<>(task, new TimeData<>(start, duration))

        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(start, duration),
                        new TimeData<>(start, duration),
                        new VersionData(0))),
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -555,13 +555,13 @@ public class TaskProviderTest

        assertThat(new Seq<>(
                new Put<>(taskList, new EmptyRowData<TaskLists>()),
                new Put<>(task, new TimeData(start, duration)),
                new Put<>(task, new TimeData<>(start, duration)),
                // update the task with a the same start in a different time zone
                new Put<>(task, new TimeData(startNew, duration))
                new Put<>(task, new TimeData<>(startNew, duration))

        ), resultsIn(mClient,
                new Assert<>(task, new Composite<>(
                        new TimeData(startNew, duration),
                        new TimeData<>(startNew, duration),
                        new VersionData(1))),
                // note that, apart from the time zone, all values stay the same
                new AssertRelated<>(
@@ -593,18 +593,18 @@ public class TaskProviderTest

        queue.enqueue(new Seq<>(
                new Put<>(taskList, new NameData("list1")),
                new Put<>(task, new TimeData(start, due))
                new Put<>(task, new TimeData<>(start, due))
        ));
        queue.flush();

        DateTime due2 = due.addDuration(new Duration(1, 0, 2));

        assertThat(new SingletonIterable<>(
                new Put<>(task, new TimeData(start, due2))
                new Put<>(task, new TimeData<>(start, due2))

        ), resultsIn(queue,
                new Assert<>(task, new Composite<>(
                        new TimeData(start, due2),
                        new TimeData<>(start, due2),
                        new VersionData(1))),
                new AssertRelated<>(
                        new InstanceTable(mAuthority), Instances.TASK_ID, task,
@@ -732,7 +732,7 @@ public class TaskProviderTest
        assertThat(new SingletonIterable<>(
                new Put<>(exceptionTask, new Composite<>(
                        new TitleData("task1exception"),
                        new OriginalInstanceSyncIdData("syncId1"))
                        new OriginalInstanceSyncIdData("syncId1", new DateTime(0)))
                )

        ), resultsIn(queue,
@@ -759,20 +759,23 @@ public class TaskProviderTest
                new Put<>(taskList, new NameData("list1")),
                new Put<>(task, new Composite<>(
                        new TitleData("task1"),
                        new SyncIdData("syncId1"))
                )
                        new SyncIdData("syncId1")))
        ));
        queue.flush();

        DateTime now = DateTime.now();

        assertThat(new SingletonIterable<>(
                new Referring<>(task, Tasks.ORIGINAL_INSTANCE_ID,
                        new Put<>(exceptionTask, new TitleData("task1exception")))
                new Put<>(exceptionTask,
                        new Composite<>(
                                new TitleData("task1exception"),
                                new OriginalInstanceData(task, now)))

        ), resultsIn(queue,
                new AssertRelated<>(new TasksTable(mAuthority), Tasks.ORIGINAL_INSTANCE_ID, task,
                        new Composite<>(
                                new TitleData("task1exception"),
                                new OriginalInstanceSyncIdData("syncId1")
                                new OriginalInstanceSyncIdData("syncId1", now)
                        ))
        ));
    }
+24 −37
Original line number Diff line number Diff line
@@ -26,11 +26,12 @@ import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.util.Log;

import org.dmfs.provider.tasks.model.CursorContentValuesInstanceAdapter;
import org.dmfs.provider.tasks.model.CursorContentValuesTaskAdapter;
import org.dmfs.provider.tasks.model.InstanceAdapter;
import org.dmfs.provider.tasks.model.TaskAdapter;
import org.dmfs.provider.tasks.processors.tasks.Instantiating;
import org.dmfs.rfc5545.DateTime;
@@ -90,25 +91,25 @@ public enum ContentOperation
            String nowString = Long.toString(now.getInstance());

            // load all tasks that have started or became due since the last time we've shown a notification.
            Cursor taskCursor = db.query(TaskDatabaseHelper.Tables.INSTANCE_VIEW, null, "((" + TaskContract.Instances.INSTANCE_DUE_SORTING + ">? and "
            Cursor instancesCursor = db.query(TaskDatabaseHelper.Tables.INSTANCE_VIEW, null, "((" + TaskContract.Instances.INSTANCE_DUE_SORTING + ">? and "
                    + TaskContract.Instances.INSTANCE_DUE_SORTING + "<=?) or (" + TaskContract.Instances.INSTANCE_START_SORTING + ">? and "
                    + TaskContract.Instances.INSTANCE_START_SORTING + "<=?)) and " + Instances.IS_CLOSED + " = 0 and " + Tasks._DELETED + "=0", new String[] {
                    lastAlarmString, nowString, lastAlarmString, nowString }, null, null, null);

            try
            {
                while (taskCursor.moveToNext())
                while (instancesCursor.moveToNext())
                {
                    TaskAdapter task = new CursorContentValuesTaskAdapter(TaskAdapter.INSTANCE_TASK_ID.getFrom(taskCursor), taskCursor, null);
                    InstanceAdapter task = new CursorContentValuesInstanceAdapter(InstanceAdapter._ID.getFrom(instancesCursor), instancesCursor, null);

                    DateTime instanceDue = task.valueOf(TaskAdapter.INSTANCE_DUE);
                    DateTime instanceDue = task.valueOf(InstanceAdapter.INSTANCE_DUE);
                    if (instanceDue != null && !instanceDue.isFloating())
                    {
                        // make sure we compare instances in local time
                        instanceDue = instanceDue.shiftTimeZone(localTimeZone);
                    }

                    DateTime instanceStart = task.valueOf(TaskAdapter.INSTANCE_START);
                    DateTime instanceStart = task.valueOf(InstanceAdapter.INSTANCE_START);
                    if (instanceStart != null && !instanceStart.isFloating())
                    {
                        // make sure we compare instances in local time
@@ -118,20 +119,18 @@ public enum ContentOperation
                    if (instanceDue != null && lastAlarm.getInstance() < instanceDue.getInstance() && instanceDue.getInstance() <= now.getInstance())
                    {
                        // this task became due since the last alarm, send a due broadcast
                        sendBroadcast(context, TaskContract.ACTION_BROADCAST_TASK_DUE, task.uri(uri.getAuthority()), instanceDue,
                                task.valueOf(TaskAdapter.TITLE));
                        sendBroadcast(context, TaskContract.ACTION_BROADCAST_TASK_DUE, task.uri(uri.getAuthority()));
                    }
                    else if (instanceStart != null && lastAlarm.getInstance() < instanceStart.getInstance() && instanceStart.getInstance() <= now.getInstance())
                    {
                        // this task has started since the last alarm, send a start broadcast
                        sendBroadcast(context, TaskContract.ACTION_BROADCAST_TASK_STARTING, task.uri(uri.getAuthority()), instanceStart,
                                task.valueOf(TaskAdapter.TITLE));
                        sendBroadcast(context, TaskContract.ACTION_BROADCAST_TASK_STARTING, task.uri(uri.getAuthority()));
                    }
                }
            }
            finally
            {
                taskCursor.close();
                instancesCursor.close();
            }

            // all notifications up to now have been triggered
@@ -160,7 +159,7 @@ public enum ContentOperation


        /**
         * Sends a notification broadcast for a task that has started or became due.
         * Sends a notification broadcast for a task instance that has started or became due.
         *
         * @param context
         *         A {@link Context}.
@@ -168,27 +167,13 @@ public enum ContentOperation
         *         The broadcast action.
         * @param uri
         *         The task uri.
         * @param datetime
         *         The datetime to add.
         * @param title
         *         The task title.
         */
        private void sendBroadcast(Context context, String action, Uri uri, DateTime datetime, String title)
        private void sendBroadcast(Context context, String action, Uri uri)
        {
            Intent intent = new Intent(action);
            intent.setData(uri);
            intent.putExtra(TaskContract.EXTRA_TASK_TIMESTAMP, datetime.getTimestamp());
            intent.putExtra(TaskContract.EXTRA_TASK_ALLDAY, datetime.isAllDay());
            if (!datetime.isFloating())
            {
                intent.putExtra(TaskContract.EXTRA_TASK_TIMEZONE, datetime.getTimeZone().getID());
            }
            intent.putExtra(TaskContract.EXTRA_TASK_TITLE, title);
            if (Build.VERSION.SDK_INT >= 26)
            {
                // for now only notify our own package
            // only notify our own package
            intent.setPackage(context.getPackageName());
            }
            context.sendBroadcast(intent);
        }
    }),
@@ -218,15 +203,16 @@ public enum ContentOperation
            DateTime nextAlarm = null;

            // find the next task that starts
            Cursor nextTaskStartCursor = db.query(TaskDatabaseHelper.Tables.INSTANCE_VIEW, null, TaskContract.Instances.INSTANCE_START_SORTING + ">? and "
            Cursor nextInstanceStartCursor = db.query(TaskDatabaseHelper.Tables.INSTANCE_VIEW, null, TaskContract.Instances.INSTANCE_START_SORTING + ">? and "
                            + Instances.IS_CLOSED + " = 0 and " + Tasks._DELETED + "=0", new String[] { lastAlarmString }, null, null,
                    TaskContract.Instances.INSTANCE_START_SORTING, "1");

            try
            {
                if (nextTaskStartCursor.moveToNext())
                if (nextInstanceStartCursor.moveToNext())
                {
                    TaskAdapter task = new CursorContentValuesTaskAdapter(TaskAdapter.INSTANCE_TASK_ID.getFrom(nextTaskStartCursor), nextTaskStartCursor, null);
                    TaskAdapter task = new CursorContentValuesTaskAdapter(TaskAdapter.INSTANCE_TASK_ID.getFrom(nextInstanceStartCursor),
                            nextInstanceStartCursor, null);
                    nextAlarm = task.valueOf(TaskAdapter.INSTANCE_START);
                    if (!nextAlarm.isFloating())
                    {
@@ -236,19 +222,20 @@ public enum ContentOperation
            }
            finally
            {
                nextTaskStartCursor.close();
                nextInstanceStartCursor.close();
            }

            // find the next task that's due
            Cursor nextTaskDueCursor = db.query(TaskDatabaseHelper.Tables.INSTANCE_VIEW, null, TaskContract.Instances.INSTANCE_DUE_SORTING + ">? and "
            Cursor nextInstanceDueCursor = db.query(TaskDatabaseHelper.Tables.INSTANCE_VIEW, null, TaskContract.Instances.INSTANCE_DUE_SORTING + ">? and "
                            + Instances.IS_CLOSED + " = 0 and " + Tasks._DELETED + "=0", new String[] { lastAlarmString }, null, null,
                    TaskContract.Instances.INSTANCE_DUE_SORTING, "1");

            try
            {
                if (nextTaskDueCursor.moveToNext())
                if (nextInstanceDueCursor.moveToNext())
                {
                    TaskAdapter task = new CursorContentValuesTaskAdapter(TaskAdapter.INSTANCE_TASK_ID.getFrom(nextTaskDueCursor), nextTaskDueCursor, null);
                    TaskAdapter task = new CursorContentValuesTaskAdapter(TaskAdapter.INSTANCE_TASK_ID.getFrom(nextInstanceDueCursor), nextInstanceDueCursor,
                            null);
                    DateTime nextDue = task.valueOf(TaskAdapter.INSTANCE_DUE);
                    if (!nextDue.isFloating())
                    {
@@ -263,7 +250,7 @@ public enum ContentOperation
            }
            finally
            {
                nextTaskDueCursor.close();
                nextInstanceDueCursor.close();
            }

            if (nextAlarm != null)
Loading