diff --git a/app/schemas/com.todoroo.astrid.dao.Database/56.json b/app/schemas/com.todoroo.astrid.dao.Database/56.json new file mode 100644 index 000000000..e763b5279 --- /dev/null +++ b/app/schemas/com.todoroo.astrid.dao.Database/56.json @@ -0,0 +1,815 @@ +{ + "formatVersion": 1, + "database": { + "version": 56, + "identityHash": "dcac47adc6c2df5755061388ec3a1248", + "entities": [ + { + "tableName": "notification", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "taskId", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_notification_task", + "unique": true, + "columnNames": [ + "task" + ], + "createSql": "CREATE UNIQUE INDEX `index_notification_task` ON `${TABLE_NAME}` (`task`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "tagdata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tagOrdering", + "columnName": "tagOrdering", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "userActivity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "picture", + "columnName": "picture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetId", + "columnName": "target_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "task_attachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `task_id` TEXT, `name` TEXT, `path` TEXT, `content_type` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskId", + "columnName": "task_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "task_list_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tagUuid", + "columnName": "tag_uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filter", + "columnName": "filter", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskIds", + "columnName": "task_ids", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `importance` INTEGER, `dueDate` INTEGER, `hideUntil` INTEGER, `created` INTEGER, `modified` INTEGER, `completed` INTEGER, `deleted` INTEGER, `notes` TEXT, `estimatedSeconds` INTEGER, `elapsedSeconds` INTEGER, `timerStart` INTEGER, `notificationFlags` INTEGER, `notifications` INTEGER, `lastNotified` INTEGER, `snoozeTime` INTEGER, `recurrence` TEXT, `repeatUntil` INTEGER, `calendarUri` TEXT, `remoteId` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "importance", + "columnName": "importance", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hideUntil", + "columnName": "hideUntil", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "estimatedSeconds", + "columnName": "estimatedSeconds", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "elapsedSeconds", + "columnName": "elapsedSeconds", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timerStart", + "columnName": "timerStart", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notificationFlags", + "columnName": "notificationFlags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notifications", + "columnName": "notifications", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastNotified", + "columnName": "lastNotified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "snoozeTime", + "columnName": "snoozeTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recurrence", + "columnName": "recurrence", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "repeatUntil", + "columnName": "repeatUntil", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "calendarUri", + "columnName": "calendarUri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "t_rid", + "unique": true, + "columnNames": [ + "remoteId" + ], + "createSql": "CREATE UNIQUE INDEX `t_rid` ON `${TABLE_NAME}` (`remoteId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "alarms", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "locations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `radius` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "radius", + "columnName": "radius", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tags", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tagUid", + "columnName": "tag_uid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskUid", + "columnName": "task_uid", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "google_tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `remote_id` TEXT, `list_id` TEXT, `parent` INTEGER NOT NULL, `indent` INTEGER NOT NULL, `order` INTEGER NOT NULL, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "listId", + "columnName": "list_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "indent", + "columnName": "indent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteOrder", + "columnName": "remote_order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSync", + "columnName": "last_sync", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `sql` TEXT, `values` TEXT, `criterion` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sql", + "columnName": "sql", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "criterion", + "columnName": "criterion", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "google_task_lists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `remote_id` TEXT, `title` TEXT, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `color` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteOrder", + "columnName": "remote_order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSync", + "columnName": "last_sync", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "caldav_account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT, `username` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ctag", + "columnName": "ctag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "caldav_tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `account` TEXT, `object` TEXT, `remote_id` TEXT, `etag` TEXT, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "object", + "columnName": "object", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSync", + "columnName": "last_sync", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"dcac47adc6c2df5755061388ec3a1248\")" + ] + } +} \ No newline at end of file diff --git a/app/src/googleplay/java/org/tasks/ui/RemoteListFragment.java b/app/src/googleplay/java/org/tasks/ui/RemoteListFragment.java index 95d0ec250..b9e115382 100644 --- a/app/src/googleplay/java/org/tasks/ui/RemoteListFragment.java +++ b/app/src/googleplay/java/org/tasks/ui/RemoteListFragment.java @@ -13,6 +13,7 @@ import com.todoroo.astrid.api.GtasksFilter; import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.gtasks.GtasksListService; +import com.todoroo.astrid.helper.UUIDHelper; import org.tasks.R; import org.tasks.analytics.Tracker; @@ -140,7 +141,7 @@ public class RemoteListFragment extends TaskEditControlFragment { if (selectedList instanceof GtasksFilter) { googleTaskDao.insert(new GoogleTask(task.getId(), ((GtasksFilter) selectedList).getRemoteId())); } else if (selectedList instanceof CaldavFilter) { - caldavDao.insert(new CaldavTask(task.getId(), ((CaldavFilter) selectedList).getUuid())); + caldavDao.insert(new CaldavTask(task.getId(), ((CaldavFilter) selectedList).getUuid(), UUIDHelper.newUUID())); } } diff --git a/app/src/main/java/com/todoroo/astrid/dao/Database.java b/app/src/main/java/com/todoroo/astrid/dao/Database.java index 508fa4f31..a3d516157 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/Database.java +++ b/app/src/main/java/com/todoroo/astrid/dao/Database.java @@ -66,7 +66,7 @@ import timber.log.Timber; CaldavAccount.class, CaldavTask.class }, - version = 55) + version = 56) public abstract class Database extends RoomDatabase { public abstract NotificationDao notificationDao(); diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskCreator.java b/app/src/main/java/com/todoroo/astrid/service/TaskCreator.java index 2b073e78d..cab46a9a0 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskCreator.java +++ b/app/src/main/java/com/todoroo/astrid/service/TaskCreator.java @@ -86,7 +86,7 @@ public class TaskCreator { if (task.hasTransitory(GoogleTask.KEY)) { googleTaskDao.insert(new GoogleTask(task.getId(), task.getTransitory(GoogleTask.KEY))); } else if (task.hasTransitory(CaldavTask.KEY)) { - caldavDao.insert(new CaldavTask(task.getId(), task.getTransitory(CaldavTask.KEY))); + caldavDao.insert(new CaldavTask(task.getId(), task.getTransitory(CaldavTask.KEY), UUIDHelper.newUUID())); } else { Filter remoteList = defaultFilterProvider.getDefaultRemoteList(); if (remoteList != null && remoteList instanceof GtasksFilter) { diff --git a/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java b/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java index a7ab5b4f7..8f260d79f 100644 --- a/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java +++ b/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java @@ -7,12 +7,14 @@ import android.content.SyncResult; import android.os.Bundle; import com.google.common.base.Strings; +import com.google.common.collect.Sets; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.helper.UUIDHelper; import com.todoroo.astrid.service.TaskCreator; +import com.todoroo.astrid.service.TaskDeleter; import org.apache.commons.codec.Charsets; import org.tasks.AccountManager; @@ -30,6 +32,7 @@ import java.io.InputStreamReader; import java.net.URI; import java.nio.charset.Charset; import java.util.List; +import java.util.Set; import javax.inject.Inject; @@ -51,7 +54,13 @@ import okhttp3.RequestBody; import okhttp3.ResponseBody; import timber.log.Timber; +import static com.google.common.base.Predicates.notNull; import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.difference; +import static com.google.common.collect.Sets.filter; +import static com.google.common.collect.Sets.newHashSet; import static org.tasks.time.DateTimeUtils.currentTimeMillis; public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { @@ -62,6 +71,7 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { @Inject TaskDao taskDao; @Inject LocalBroadcastManager localBroadcastManager; @Inject TaskCreator taskCreator; + @Inject TaskDeleter taskDeleter; CalDAVSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); @@ -115,27 +125,22 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { String remoteCtag = properties.get(GetCTag.class).getCTag(); String localCtag = caldavAccount.getCtag(); - if (localCtag != null && localCtag.equals(remoteCtag)) { Timber.d("%s up to date", caldavAccount.getName()); return; } - // fetch etags - - // check for deleted tasks - - // multiget updated tasks - davCalendar.calendarQuery("VTODO", null, null); - // fetch and apply remote changes + // TODO: multi-get new and modified objects + + // TODO: delete this loop for (DavResource vCard : davCalendar.getMembers()) { - ResponseBody responseBody = vCard.get("text/calendar"); GetETag eTag = (GetETag) vCard.getProperties().get(GetETag.NAME); if (eTag == null || isNullOrEmpty(eTag.getETag())) { throw new DavException("Received CalDAV GET response without ETag for " + vCard.getLocation()); } + ResponseBody responseBody = vCard.get("text/calendar"); MediaType contentType = responseBody.contentType(); Charset charset = contentType == null ? Charsets.UTF_8 : contentType.charset(Charsets.UTF_8); InputStream stream = responseBody.byteStream(); @@ -148,6 +153,13 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { } } + Sets.SetView deleted = difference( + newHashSet(caldavDao.getObjects(caldavAccount.getUuid())), + newHashSet(transform(davCalendar.getMembers(), DavResource::fileName))); + List toDelete = newArrayList(deleted); + taskDeleter.markDeleted(caldavDao.getTasks(caldavAccount.getUuid(), toDelete)); + caldavDao.deleteObjects(caldavAccount.getUuid(), toDelete); + caldavAccount.setCtag(remoteCtag); caldavDao.update(caldavAccount); } catch (IOException | HttpException | DavException | CalendarStorageException e) { @@ -172,8 +184,8 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { private boolean deleteRemoteResource(OkHttpClient httpClient, HttpUrl httpUrl, CaldavTask caldavTask) { try { - if (!Strings.isNullOrEmpty(caldavTask.getRemoteId())) { - DavResource remote = new DavResource(httpClient, httpUrl.newBuilder().addPathSegment(caldavTask.getRemoteId() + ".ics").build()); + if (!Strings.isNullOrEmpty(caldavTask.getObject())) { + DavResource remote = new DavResource(httpClient, httpUrl.newBuilder().addPathSegment(caldavTask.getObject()).build()); remote.delete(null); } } catch (HttpException e) { @@ -228,7 +240,7 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { DavCalendar.MIME_ICALENDAR, os.toByteArray()); try { - DavResource remote = new DavResource(httpClient, httpUrl.newBuilder().addPathSegment(caldavMetadata.getRemoteId() + ".ics").build()); + DavResource remote = new DavResource(httpClient, httpUrl.newBuilder().addPathSegment(caldavMetadata.getObject()).build()); remote.put(requestBody, null, false); } catch (HttpException e) { Timber.e(e.getMessage(), e); @@ -261,12 +273,11 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { if (tasks.size() == 1) { at.bitfire.ical4android.Task remote = tasks.get(0); Task task; - CaldavTask caldavTask = caldavDao.getTask(caldavAccount.getUuid(), remote.getUid()); + CaldavTask caldavTask = caldavDao.getTask(caldavAccount.getUuid(), fileName); if (caldavTask == null) { task = taskCreator.createWithValues(null, ""); taskDao.createNew(task); - caldavTask = new CaldavTask(task.getId(), caldavAccount.getUuid()); - caldavTask.setRemoteId(remote.getUid()); + caldavTask = new CaldavTask(task.getId(), caldavAccount.getUuid(), remote.getUid(), fileName); Timber.d("NEW %s", remote); } else { task = taskDao.fetch(caldavTask.getTask()); diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java index c7cb1e2d9..19b398dcd 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.java +++ b/app/src/main/java/org/tasks/data/CaldavDao.java @@ -7,6 +7,7 @@ import android.arch.persistence.room.Query; import android.arch.persistence.room.Update; import java.util.List; +import java.util.Set; @Dao public interface CaldavDao { @@ -38,8 +39,8 @@ public interface CaldavDao { @Query("SELECT * FROM caldav_tasks WHERE task = :taskId AND deleted = 0 LIMIT 1") CaldavTask getTask(long taskId); - @Query("SELECT * FROM caldav_tasks WHERE account = :account AND remote_id = :remoteId LIMIT 1") - CaldavTask getTask(String account, String remoteId); + @Query("SELECT * FROM caldav_tasks WHERE account = :account AND object = :object LIMIT 1") + CaldavTask getTask(String account, String object); @Query("DELETE FROM caldav_tasks WHERE task = :taskId") void deleteById(long taskId); @@ -58,4 +59,13 @@ public interface CaldavDao { @Query("DELETE FROM caldav_tasks WHERE account = :uuid") void deleteTasksForAccount(String uuid); + + @Query("SELECT object FROM caldav_tasks WHERE account = :account") + List getObjects(String account); + + @Query("SELECT task FROM caldav_tasks WHERE account = :account AND object IN (:objects)") + List getTasks(String account, List objects); + + @Query("DELETE FROM caldav_tasks WHERE account = :account AND object IN (:objects)") + void deleteObjects(String account, List objects); } diff --git a/app/src/main/java/org/tasks/data/CaldavTask.java b/app/src/main/java/org/tasks/data/CaldavTask.java index cb36f9ce0..ad38ca114 100644 --- a/app/src/main/java/org/tasks/data/CaldavTask.java +++ b/app/src/main/java/org/tasks/data/CaldavTask.java @@ -25,6 +25,9 @@ public class CaldavTask { @ColumnInfo(name = "account") private String account; + @ColumnInfo(name = "object") + private String object; + @ColumnInfo(name = "remote_id") private String remoteId; @@ -42,9 +45,16 @@ public class CaldavTask { } @Ignore - public CaldavTask(long task, String account) { + public CaldavTask(long task, String account, String remoteId) { + this(task, account, remoteId, remoteId + ".ics"); + } + + @Ignore + public CaldavTask(long task, String account, String remoteId, String object) { this.task = task; this.account = account; + this.remoteId = remoteId; + this.object = object; } public long getId() { @@ -63,6 +73,22 @@ public class CaldavTask { this.task = task; } + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + public String getRemoteId() { return remoteId; } @@ -95,20 +121,13 @@ public class CaldavTask { this.deleted = deleted; } - public String getAccount() { - return account; - } - - public void setAccount(String account) { - this.account = account; - } - @Override public String toString() { return "CaldavTask{" + "id=" + id + ", task=" + task + ", account='" + account + '\'' + + ", object='" + object + '\'' + ", remoteId='" + remoteId + '\'' + ", etag='" + etag + '\'' + ", lastSync=" + lastSync + diff --git a/app/src/main/java/org/tasks/db/Migrations.java b/app/src/main/java/org/tasks/db/Migrations.java index a10a5f2f9..c8fa27340 100644 --- a/app/src/main/java/org/tasks/db/Migrations.java +++ b/app/src/main/java/org/tasks/db/Migrations.java @@ -151,6 +151,13 @@ public class Migrations { } }; + private static final Migration MIGRATION_55_56 = new Migration(55, 56) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `object` TEXT"); + } + }; + private static Migration NOOP(int from, int to) { return new Migration(from, to) { @Override @@ -174,6 +181,7 @@ public class Migrations { MIGRATION_51_52, MIGRATION_52_53, MIGRATION_53_54, - MIGRATION_54_55 + MIGRATION_54_55, + MIGRATION_55_56 }; }