From bcc5deba18d3664970f803c29e99acac78483eb0 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Fri, 16 Mar 2018 09:35:23 -0500 Subject: [PATCH] Store remote vtodo and preserve unsupported fields --- .../com.todoroo.astrid.dao.Database/56.json | 12 ++- .../org/tasks/caldav/CalDAVSyncAdapter.java | 79 +++++++++++-------- .../java/org/tasks/caldav/TaskConverter.java | 30 +++++-- .../main/java/org/tasks/data/CaldavDao.java | 2 +- .../main/java/org/tasks/data/CaldavTask.java | 12 +++ .../main/java/org/tasks/db/Migrations.java | 1 + 6 files changed, 91 insertions(+), 45 deletions(-) diff --git a/app/schemas/com.todoroo.astrid.dao.Database/56.json b/app/schemas/com.todoroo.astrid.dao.Database/56.json index e763b5279..fea0a14fc 100644 --- a/app/schemas/com.todoroo.astrid.dao.Database/56.json +++ b/app/schemas/com.todoroo.astrid.dao.Database/56.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 56, - "identityHash": "dcac47adc6c2df5755061388ec3a1248", + "identityHash": "cc409e42965995fd353910a20daf49cf", "entities": [ { "tableName": "notification", @@ -746,7 +746,7 @@ }, { "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)", + "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, `vtodo` TEXT)", "fields": [ { "fieldPath": "id", @@ -795,6 +795,12 @@ "columnName": "deleted", "affinity": "INTEGER", "notNull": true + }, + { + "fieldPath": "vtodo", + "columnName": "vtodo", + "affinity": "TEXT", + "notNull": false } ], "primaryKey": { @@ -809,7 +815,7 @@ ], "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\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"cc409e42965995fd353910a20daf49cf\")" ] } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java b/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java index 5e41630da..176572a34 100644 --- a/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java +++ b/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java @@ -7,7 +7,7 @@ import android.content.SyncResult; import android.os.Bundle; import com.google.common.base.Strings; -import com.google.common.collect.Sets; +import com.google.common.io.CharStreams; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.SyncFlags; @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.net.URI; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -155,17 +156,22 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { if (eTag == null || isNullOrEmpty(eTag.getETag())) { throw new DavException("Received CalDAV GET response without ETag for " + vCard.getLocation()); } + Timber.d("SINGLE %s", vCard.getLocation()); ResponseBody responseBody = vCard.get("text/calendar"); - Reader reader = responseBody.charStream(); + Reader reader = null; try { - processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), reader); + reader = responseBody.charStream(); + processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), CharStreams.toString(reader)); } finally { if (reader != null) { reader.close(); } } } else { - davCalendar.multiget(newArrayList(transform(changed, DavResource::getLocation))); + ArrayList urls = newArrayList(transform(changed, DavResource::getLocation)); + davCalendar.multiget(urls); + + Timber.d("MULTI %s", urls); for (DavResource vCard : davCalendar.getMembers()) { PropertyCollection vcardProperties = vCard.getProperties(); @@ -179,20 +185,22 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { throw new DavException("Received CalDAV GET response without CalendarData for " + vCard.getLocation()); } - String vtodo = calendarData.getICalendar(); - processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), new StringReader(vtodo)); + processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), calendarData.getICalendar()); } } } - Sets.SetView deleted = difference( + List deleted = newArrayList(difference( newHashSet(caldavDao.getObjects(caldavAccount.getUuid())), - newHashSet(remoteObjects)); - List toDelete = newArrayList(deleted); - taskDeleter.markDeleted(caldavDao.getTasks(caldavAccount.getUuid(), toDelete)); - caldavDao.deleteObjects(caldavAccount.getUuid(), toDelete); + newHashSet(remoteObjects))); + if (deleted.size() > 0) { + Timber.d("DELETED %s", deleted); + taskDeleter.markDeleted(caldavDao.getTasks(caldavAccount.getUuid(), deleted)); + caldavDao.deleteObjects(caldavAccount.getUuid(), deleted); + } caldavAccount.setCtag(remoteCtag); + Timber.d("UPDATE %s", caldavAccount); caldavDao.update(caldavAccount); } catch (IOException | HttpException | DavException | CalendarStorageException e) { Timber.e(e, e.getMessage()); @@ -243,58 +251,60 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { return; } - CaldavTask caldavMetadata = caldavDao.getTask(task.getId()); + CaldavTask caldavTask = caldavDao.getTask(task.getId()); - if (caldavMetadata == null) { + if (caldavTask == null) { return; } if (task.isDeleted()) { - if (deleteRemoteResource(httpClient, httpUrl, caldavMetadata)) { - caldavDao.delete(caldavMetadata); + if (deleteRemoteResource(httpClient, httpUrl, caldavTask)) { + caldavDao.delete(caldavTask); } return; } - at.bitfire.ical4android.Task remoteModel = TaskConverter.toCaldav(task); + at.bitfire.ical4android.Task remoteModel = TaskConverter.toCaldav(caldavTask, task); - if (Strings.isNullOrEmpty(caldavMetadata.getRemoteId())) { + if (Strings.isNullOrEmpty(caldavTask.getRemoteId())) { String caldavUid = UUIDHelper.newUUID(); - caldavMetadata.setRemoteId(caldavUid); + caldavTask.setRemoteId(caldavUid); remoteModel.setUid(caldavUid); } else { - remoteModel.setUid(caldavMetadata.getRemoteId()); + remoteModel.setUid(caldavTask.getRemoteId()); } ByteArrayOutputStream os = new ByteArrayOutputStream(); remoteModel.write(os); - RequestBody requestBody = RequestBody.create( - DavCalendar.MIME_ICALENDAR, - os.toByteArray()); + byte[] data = os.toByteArray(); + RequestBody requestBody = RequestBody.create(DavCalendar.MIME_ICALENDAR, data); + try { - DavResource remote = new DavResource(httpClient, httpUrl.newBuilder().addPathSegment(caldavMetadata.getObject()).build()); + DavResource remote = new DavResource(httpClient, httpUrl.newBuilder().addPathSegment(caldavTask.getObject()).build()); remote.put(requestBody, null, false); + GetETag getETag = remote.getProperties().get(GetETag.class); + if (getETag != null && !isNullOrEmpty(getETag.getETag())) { + caldavTask.setEtag(getETag.getETag()); + caldavTask.setVtodo(new String(data)); + } } catch (HttpException e) { Timber.e(e.getMessage(), e); return; } - long modified = currentTimeMillis(); - task.setModificationDate(modified); - task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); - taskDao.save(task); - caldavMetadata.setLastSync(modified); - caldavDao.update(caldavMetadata); + caldavTask.setLastSync(currentTimeMillis()); + caldavDao.update(caldavTask); + Timber.d("SENT %s", caldavTask); } private List getDeleted(long taskId, CaldavAccount caldavAccount) { return caldavDao.getDeleted(taskId, caldavAccount.getUuid()); } - private void processVTodo(String fileName, CaldavAccount caldavAccount, String eTag, Reader reader) throws IOException, CalendarStorageException { + private void processVTodo(String fileName, CaldavAccount caldavAccount, String eTag, String vtodo) throws IOException, CalendarStorageException { List tasks; try { - tasks = at.bitfire.ical4android.Task.fromReader(reader); + tasks = at.bitfire.ical4android.Task.fromReader(new StringReader(vtodo)); } catch (InvalidCalendarException e) { Timber.e(e, e.getMessage()); return; @@ -308,20 +318,21 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { task = taskCreator.createWithValues(null, ""); taskDao.createNew(task); caldavTask = new CaldavTask(task.getId(), caldavAccount.getUuid(), remote.getUid(), fileName); - Timber.d("NEW %s", remote); } else { task = taskDao.fetch(caldavTask.getTask()); - Timber.d("UPDATE %s", remote); } TaskConverter.apply(task, remote); task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); taskDao.save(task); + caldavTask.setVtodo(vtodo); caldavTask.setEtag(eTag); caldavTask.setLastSync(DateUtilities.now() + 1000L); if (caldavTask.getId() == Task.NO_ID) { - caldavDao.insert(caldavTask); + caldavTask.setId(caldavDao.insert(caldavTask)); + Timber.d("NEW %s", caldavTask); } else { caldavDao.update(caldavTask); + Timber.d("UPDATE %s", caldavTask); } } else { Timber.e("Received VCALENDAR with %s VTODOs; ignoring %s", tasks.size(), fileName); diff --git a/app/src/main/java/org/tasks/caldav/TaskConverter.java b/app/src/main/java/org/tasks/caldav/TaskConverter.java index 60f6a2f07..f19216cff 100644 --- a/app/src/main/java/org/tasks/caldav/TaskConverter.java +++ b/app/src/main/java/org/tasks/caldav/TaskConverter.java @@ -1,5 +1,6 @@ package org.tasks.caldav; +import com.google.common.base.Strings; import com.todoroo.astrid.data.Task; import net.fortuna.ical4j.model.Date; @@ -9,14 +10,18 @@ import net.fortuna.ical4j.model.property.Completed; import net.fortuna.ical4j.model.property.Due; import net.fortuna.ical4j.model.property.RRule; +import org.tasks.data.CaldavTask; + +import java.io.IOException; +import java.io.StringReader; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Locale; +import at.bitfire.ical4android.InvalidCalendarException; import timber.log.Timber; -import static com.todoroo.astrid.data.Task.DUE_DATE; import static com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY; import static com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY_TIME; import static org.tasks.date.DateTimeUtils.newDateTime; @@ -68,21 +73,31 @@ public class TaskConverter { } } - static int toRemote(int tasksPriority) { - switch (tasksPriority) { + static int toRemote(int remotePriority, int localPriority) { + switch (localPriority) { case Task.IMPORTANCE_DO_OR_DIE: return 1; case Task.IMPORTANCE_MUST_DO: return 2; case Task.IMPORTANCE_SHOULD_DO: - return 3; + return remotePriority > 2 ? remotePriority : 3; default: return 0; } } - public static at.bitfire.ical4android.Task toCaldav(Task task) { - at.bitfire.ical4android.Task remote = new at.bitfire.ical4android.Task(); + public static at.bitfire.ical4android.Task toCaldav(CaldavTask caldavTask, Task task) { + at.bitfire.ical4android.Task remote = null; + try { + if (!Strings.isNullOrEmpty(caldavTask.getVtodo())) { + remote = at.bitfire.ical4android.Task.fromReader(new StringReader(caldavTask.getVtodo())).get(0); + } + } catch (IOException | InvalidCalendarException e) { + Timber.e(e, e.getMessage()); + } + if (remote == null) { + remote = new at.bitfire.ical4android.Task(); + } remote.setSummary(task.getTitle()); remote.setDescription(task.getNotes()); if (task.hasDueDate()) { @@ -112,7 +127,8 @@ public class TaskConverter { Timber.e(e, e.getMessage()); } } - remote.setPriority(toRemote(task.getImportance())); + remote.setLastModified(newDateTime(task.getModificationDate()).toUTC().getMillis()); + remote.setPriority(toRemote(remote.getPriority(), task.getImportance())); return remote; } } diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java index 19b398dcd..91f325616 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.java +++ b/app/src/main/java/org/tasks/data/CaldavDao.java @@ -25,7 +25,7 @@ public interface CaldavDao { void update(CaldavAccount caldavAccount); @Insert - void insert(CaldavTask caldavTask); + long insert(CaldavTask caldavTask); @Update void update(CaldavTask caldavTask); diff --git a/app/src/main/java/org/tasks/data/CaldavTask.java b/app/src/main/java/org/tasks/data/CaldavTask.java index ad38ca114..064cebb7b 100644 --- a/app/src/main/java/org/tasks/data/CaldavTask.java +++ b/app/src/main/java/org/tasks/data/CaldavTask.java @@ -40,6 +40,9 @@ public class CaldavTask { @ColumnInfo(name = "deleted") private long deleted; + @ColumnInfo(name = "vtodo") + private String vtodo; + public CaldavTask() { } @@ -121,6 +124,14 @@ public class CaldavTask { this.deleted = deleted; } + public String getVtodo() { + return vtodo; + } + + public void setVtodo(String vtodo) { + this.vtodo = vtodo; + } + @Override public String toString() { return "CaldavTask{" + @@ -132,6 +143,7 @@ public class CaldavTask { ", etag='" + etag + '\'' + ", lastSync=" + lastSync + ", deleted=" + deleted + + ", vtodo='" + vtodo + '\'' + '}'; } } diff --git a/app/src/main/java/org/tasks/db/Migrations.java b/app/src/main/java/org/tasks/db/Migrations.java index c8fa27340..5b4de7a5d 100644 --- a/app/src/main/java/org/tasks/db/Migrations.java +++ b/app/src/main/java/org/tasks/db/Migrations.java @@ -155,6 +155,7 @@ public class Migrations { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `object` TEXT"); + database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `vtodo` TEXT"); } };