Store remote vtodo and preserve unsupported fields

pull/645/head
Alex Baker 6 years ago
parent 37bd715475
commit bcc5deba18

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 56, "version": 56,
"identityHash": "dcac47adc6c2df5755061388ec3a1248", "identityHash": "cc409e42965995fd353910a20daf49cf",
"entities": [ "entities": [
{ {
"tableName": "notification", "tableName": "notification",
@ -746,7 +746,7 @@
}, },
{ {
"tableName": "caldav_tasks", "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": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -795,6 +795,12 @@
"columnName": "deleted", "columnName": "deleted",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
},
{
"fieldPath": "vtodo",
"columnName": "vtodo",
"affinity": "TEXT",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -809,7 +815,7 @@
], ],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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\")"
] ]
} }
} }

@ -7,7 +7,7 @@ import android.content.SyncResult;
import android.os.Bundle; import android.os.Bundle;
import com.google.common.base.Strings; 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.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.SyncFlags;
@ -31,6 +31,7 @@ import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -155,17 +156,22 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter {
if (eTag == null || isNullOrEmpty(eTag.getETag())) { if (eTag == null || isNullOrEmpty(eTag.getETag())) {
throw new DavException("Received CalDAV GET response without ETag for " + vCard.getLocation()); throw new DavException("Received CalDAV GET response without ETag for " + vCard.getLocation());
} }
Timber.d("SINGLE %s", vCard.getLocation());
ResponseBody responseBody = vCard.get("text/calendar"); ResponseBody responseBody = vCard.get("text/calendar");
Reader reader = responseBody.charStream(); Reader reader = null;
try { try {
processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), reader); reader = responseBody.charStream();
processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), CharStreams.toString(reader));
} finally { } finally {
if (reader != null) { if (reader != null) {
reader.close(); reader.close();
} }
} }
} else { } else {
davCalendar.multiget(newArrayList(transform(changed, DavResource::getLocation))); ArrayList<HttpUrl> urls = newArrayList(transform(changed, DavResource::getLocation));
davCalendar.multiget(urls);
Timber.d("MULTI %s", urls);
for (DavResource vCard : davCalendar.getMembers()) { for (DavResource vCard : davCalendar.getMembers()) {
PropertyCollection vcardProperties = vCard.getProperties(); 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()); throw new DavException("Received CalDAV GET response without CalendarData for " + vCard.getLocation());
} }
String vtodo = calendarData.getICalendar(); processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), calendarData.getICalendar());
processVTodo(vCard.fileName(), caldavAccount, eTag.getETag(), new StringReader(vtodo));
} }
} }
} }
Sets.SetView<String> deleted = difference( List<String> deleted = newArrayList(difference(
newHashSet(caldavDao.getObjects(caldavAccount.getUuid())), newHashSet(caldavDao.getObjects(caldavAccount.getUuid())),
newHashSet(remoteObjects)); newHashSet(remoteObjects)));
List<String> toDelete = newArrayList(deleted); if (deleted.size() > 0) {
taskDeleter.markDeleted(caldavDao.getTasks(caldavAccount.getUuid(), toDelete)); Timber.d("DELETED %s", deleted);
caldavDao.deleteObjects(caldavAccount.getUuid(), toDelete); taskDeleter.markDeleted(caldavDao.getTasks(caldavAccount.getUuid(), deleted));
caldavDao.deleteObjects(caldavAccount.getUuid(), deleted);
}
caldavAccount.setCtag(remoteCtag); caldavAccount.setCtag(remoteCtag);
Timber.d("UPDATE %s", caldavAccount);
caldavDao.update(caldavAccount); caldavDao.update(caldavAccount);
} catch (IOException | HttpException | DavException | CalendarStorageException e) { } catch (IOException | HttpException | DavException | CalendarStorageException e) {
Timber.e(e, e.getMessage()); Timber.e(e, e.getMessage());
@ -243,58 +251,60 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter {
return; return;
} }
CaldavTask caldavMetadata = caldavDao.getTask(task.getId()); CaldavTask caldavTask = caldavDao.getTask(task.getId());
if (caldavMetadata == null) { if (caldavTask == null) {
return; return;
} }
if (task.isDeleted()) { if (task.isDeleted()) {
if (deleteRemoteResource(httpClient, httpUrl, caldavMetadata)) { if (deleteRemoteResource(httpClient, httpUrl, caldavTask)) {
caldavDao.delete(caldavMetadata); caldavDao.delete(caldavTask);
} }
return; 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(); String caldavUid = UUIDHelper.newUUID();
caldavMetadata.setRemoteId(caldavUid); caldavTask.setRemoteId(caldavUid);
remoteModel.setUid(caldavUid); remoteModel.setUid(caldavUid);
} else { } else {
remoteModel.setUid(caldavMetadata.getRemoteId()); remoteModel.setUid(caldavTask.getRemoteId());
} }
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
remoteModel.write(os); remoteModel.write(os);
RequestBody requestBody = RequestBody.create( byte[] data = os.toByteArray();
DavCalendar.MIME_ICALENDAR, RequestBody requestBody = RequestBody.create(DavCalendar.MIME_ICALENDAR, data);
os.toByteArray());
try { 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); 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) { } catch (HttpException e) {
Timber.e(e.getMessage(), e); Timber.e(e.getMessage(), e);
return; return;
} }
long modified = currentTimeMillis(); caldavTask.setLastSync(currentTimeMillis());
task.setModificationDate(modified); caldavDao.update(caldavTask);
task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); Timber.d("SENT %s", caldavTask);
taskDao.save(task);
caldavMetadata.setLastSync(modified);
caldavDao.update(caldavMetadata);
} }
private List<CaldavTask> getDeleted(long taskId, CaldavAccount caldavAccount) { private List<CaldavTask> getDeleted(long taskId, CaldavAccount caldavAccount) {
return caldavDao.getDeleted(taskId, caldavAccount.getUuid()); 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<at.bitfire.ical4android.Task> tasks; List<at.bitfire.ical4android.Task> tasks;
try { try {
tasks = at.bitfire.ical4android.Task.fromReader(reader); tasks = at.bitfire.ical4android.Task.fromReader(new StringReader(vtodo));
} catch (InvalidCalendarException e) { } catch (InvalidCalendarException e) {
Timber.e(e, e.getMessage()); Timber.e(e, e.getMessage());
return; return;
@ -308,20 +318,21 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter {
task = taskCreator.createWithValues(null, ""); task = taskCreator.createWithValues(null, "");
taskDao.createNew(task); taskDao.createNew(task);
caldavTask = new CaldavTask(task.getId(), caldavAccount.getUuid(), remote.getUid(), fileName); caldavTask = new CaldavTask(task.getId(), caldavAccount.getUuid(), remote.getUid(), fileName);
Timber.d("NEW %s", remote);
} else { } else {
task = taskDao.fetch(caldavTask.getTask()); task = taskDao.fetch(caldavTask.getTask());
Timber.d("UPDATE %s", remote);
} }
TaskConverter.apply(task, remote); TaskConverter.apply(task, remote);
task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true);
taskDao.save(task); taskDao.save(task);
caldavTask.setVtodo(vtodo);
caldavTask.setEtag(eTag); caldavTask.setEtag(eTag);
caldavTask.setLastSync(DateUtilities.now() + 1000L); caldavTask.setLastSync(DateUtilities.now() + 1000L);
if (caldavTask.getId() == Task.NO_ID) { if (caldavTask.getId() == Task.NO_ID) {
caldavDao.insert(caldavTask); caldavTask.setId(caldavDao.insert(caldavTask));
Timber.d("NEW %s", caldavTask);
} else { } else {
caldavDao.update(caldavTask); caldavDao.update(caldavTask);
Timber.d("UPDATE %s", caldavTask);
} }
} else { } else {
Timber.e("Received VCALENDAR with %s VTODOs; ignoring %s", tasks.size(), fileName); Timber.e("Received VCALENDAR with %s VTODOs; ignoring %s", tasks.size(), fileName);

@ -1,5 +1,6 @@
package org.tasks.caldav; package org.tasks.caldav;
import com.google.common.base.Strings;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import net.fortuna.ical4j.model.Date; 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.Due;
import net.fortuna.ical4j.model.property.RRule; 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.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Locale; import java.util.Locale;
import at.bitfire.ical4android.InvalidCalendarException;
import timber.log.Timber; 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;
import static com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY_TIME; import static com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY_TIME;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
@ -68,21 +73,31 @@ public class TaskConverter {
} }
} }
static int toRemote(int tasksPriority) { static int toRemote(int remotePriority, int localPriority) {
switch (tasksPriority) { switch (localPriority) {
case Task.IMPORTANCE_DO_OR_DIE: case Task.IMPORTANCE_DO_OR_DIE:
return 1; return 1;
case Task.IMPORTANCE_MUST_DO: case Task.IMPORTANCE_MUST_DO:
return 2; return 2;
case Task.IMPORTANCE_SHOULD_DO: case Task.IMPORTANCE_SHOULD_DO:
return 3; return remotePriority > 2 ? remotePriority : 3;
default: default:
return 0; return 0;
} }
} }
public static at.bitfire.ical4android.Task toCaldav(Task task) { public static at.bitfire.ical4android.Task toCaldav(CaldavTask caldavTask, Task task) {
at.bitfire.ical4android.Task remote = new at.bitfire.ical4android.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.setSummary(task.getTitle());
remote.setDescription(task.getNotes()); remote.setDescription(task.getNotes());
if (task.hasDueDate()) { if (task.hasDueDate()) {
@ -112,7 +127,8 @@ public class TaskConverter {
Timber.e(e, e.getMessage()); 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; return remote;
} }
} }

@ -25,7 +25,7 @@ public interface CaldavDao {
void update(CaldavAccount caldavAccount); void update(CaldavAccount caldavAccount);
@Insert @Insert
void insert(CaldavTask caldavTask); long insert(CaldavTask caldavTask);
@Update @Update
void update(CaldavTask caldavTask); void update(CaldavTask caldavTask);

@ -40,6 +40,9 @@ public class CaldavTask {
@ColumnInfo(name = "deleted") @ColumnInfo(name = "deleted")
private long deleted; private long deleted;
@ColumnInfo(name = "vtodo")
private String vtodo;
public CaldavTask() { public CaldavTask() {
} }
@ -121,6 +124,14 @@ public class CaldavTask {
this.deleted = deleted; this.deleted = deleted;
} }
public String getVtodo() {
return vtodo;
}
public void setVtodo(String vtodo) {
this.vtodo = vtodo;
}
@Override @Override
public String toString() { public String toString() {
return "CaldavTask{" + return "CaldavTask{" +
@ -132,6 +143,7 @@ public class CaldavTask {
", etag='" + etag + '\'' + ", etag='" + etag + '\'' +
", lastSync=" + lastSync + ", lastSync=" + lastSync +
", deleted=" + deleted + ", deleted=" + deleted +
", vtodo='" + vtodo + '\'' +
'}'; '}';
} }
} }

@ -155,6 +155,7 @@ public class Migrations {
@Override @Override
public void migrate(@NonNull SupportSQLiteDatabase database) { public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `object` TEXT"); database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `object` TEXT");
database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `vtodo` TEXT");
} }
}; };

Loading…
Cancel
Save