Fix Google Task and Google Drive issues

pull/848/head 6.7.2
Alex Baker 7 years ago
parent f1bcc697eb
commit 56f3dceec7

@ -1,5 +1,11 @@
Change Log Change Log
--- ---
### 6.7.2 (2019-07-08)
* Handle 404 errors when creating new Google Tasks
* Ignore 404 errors when deleting Google Drive files
* Don't report connection errors
### 6.7.1 (2019-07-05) ### 6.7.1 (2019-07-05)
* Add location chip to task list * Add location chip to task list

@ -25,8 +25,8 @@ android {
defaultConfig { defaultConfig {
testApplicationId = "org.tasks.test" testApplicationId = "org.tasks.test"
applicationId = "org.tasks" applicationId = "org.tasks"
versionCode = 591 versionCode = 592
versionName = "6.7.1" versionName = "6.7.2"
targetSdkVersion(Versions.compileSdk) targetSdkVersion(Versions.compileSdk)
minSdkVersion(Versions.minSdk) minSdkVersion(Versions.minSdk)
multiDexEnabled = true multiDexEnabled = true

@ -34,10 +34,14 @@ import com.todoroo.astrid.service.TaskDeleter;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.net.ssl.SSLException;
import net.fortuna.ical4j.model.property.ProdId; import net.fortuna.ical4j.model.property.ProdId;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -102,15 +106,20 @@ public class CaldavSynchronizer {
setError(account, context.getString(R.string.password_required)); setError(account, context.getString(R.string.password_required));
return; return;
} }
CaldavClient caldavClient = client.forAccount(account);
List<Response> resources;
try { try {
resources = caldavClient.getCalendars(); synchronize(account);
} catch (SocketTimeoutException | SSLException | ConnectException | UnknownHostException e) {
Timber.e(e);
account.setError(e.getMessage());
} catch (IOException | DavException e) { } catch (IOException | DavException e) {
setError(account, e.getMessage()); setError(account, e.getMessage());
tracker.reportException(e); tracker.reportException(e);
return;
} }
}
private void synchronize(CaldavAccount account) throws IOException, DavException {
CaldavClient caldavClient = client.forAccount(account);
List<Response> resources = caldavClient.getCalendars();
Set<String> urls = newHashSet(transform(resources, c -> c.getHref().toString())); Set<String> urls = newHashSet(transform(resources, c -> c.getHref().toString()));
Timber.d("Found calendars: %s", urls); Timber.d("Found calendars: %s", urls);
for (CaldavCalendar calendar : for (CaldavCalendar calendar :
@ -143,103 +152,98 @@ public class CaldavSynchronizer {
} }
} }
private void sync(CaldavCalendar caldavCalendar, Response resource, OkHttpClient httpClient) { private void sync(CaldavCalendar caldavCalendar, Response resource, OkHttpClient httpClient)
throws IOException, DavException {
Timber.d("sync(%s)", caldavCalendar); Timber.d("sync(%s)", caldavCalendar);
HttpUrl httpUrl = resource.getHref(); HttpUrl httpUrl = resource.getHref();
try { pushLocalChanges(caldavCalendar, httpClient, httpUrl);
pushLocalChanges(caldavCalendar, httpClient, httpUrl);
String remoteName = resource.get(DisplayName.class).getDisplayName();
String remoteName = resource.get(DisplayName.class).getDisplayName(); if (!caldavCalendar.getName().equals(remoteName)) {
if (!caldavCalendar.getName().equals(remoteName)) { Timber.d("%s -> %s", caldavCalendar.getName(), remoteName);
Timber.d("%s -> %s", caldavCalendar.getName(), remoteName); caldavCalendar.setName(remoteName);
caldavCalendar.setName(remoteName); caldavDao.update(caldavCalendar);
caldavDao.update(caldavCalendar); localBroadcastManager.broadcastRefreshList();
localBroadcastManager.broadcastRefreshList(); }
}
String remoteCtag = resource.get(GetCTag.class).getCTag(); String remoteCtag = resource.get(GetCTag.class).getCTag();
String localCtag = caldavCalendar.getCtag(); String localCtag = caldavCalendar.getCtag();
if (localCtag != null && localCtag.equals(remoteCtag)) { if (localCtag != null && localCtag.equals(remoteCtag)) {
Timber.d("%s up to date", caldavCalendar.getName()); Timber.d("%s up to date", caldavCalendar.getName());
return; return;
} }
DavCalendar davCalendar = new DavCalendar(httpClient, httpUrl);
ResponseList members = new ResponseList(HrefRelation.MEMBER);
davCalendar.calendarQuery("VTODO", null, null, members);
Set<String> remoteObjects = newHashSet(transform(members, Response::hrefName));
Iterable<Response> changed =
filter(
ImmutableSet.copyOf(members),
vCard -> {
GetETag eTag = vCard.get(GetETag.class);
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
return false;
}
CaldavTask caldavTask = caldavDao.getTask(caldavCalendar.getUuid(), vCard.hrefName());
return caldavTask == null || !eTag.getETag().equals(caldavTask.getEtag());
});
for (List<Response> items : partition(changed, 30)) {
if (items.size() == 1) {
Response vCard = items.get(0);
GetETag eTag = vCard.get(GetETag.class);
HttpUrl url = vCard.getHref();
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
throw new DavException("Received CalDAV GET response without ETag for " + url);
}
Timber.d("SINGLE %s", url);
DavCalendar davCalendar = new DavCalendar(httpClient, httpUrl); org.tasks.caldav.Response response = new org.tasks.caldav.Response(true);
new DavResource(httpClient, url).get("text/calendar", response);
ResponseList members = new ResponseList(HrefRelation.MEMBER); processVTodo(vCard.hrefName(), caldavCalendar, eTag.getETag(), response.getBody());
davCalendar.calendarQuery("VTODO", null, null, members); } else {
ArrayList<HttpUrl> urls = newArrayList(Iterables.transform(items, Response::getHref));
Set<String> remoteObjects = newHashSet(transform(members, Response::hrefName)); ResponseList responses = new ResponseList(HrefRelation.MEMBER);
davCalendar.multiget(urls, responses);
Iterable<Response> changed =
filter( Timber.d("MULTI %s", urls);
ImmutableSet.copyOf(members),
vCard -> { for (Response vCard : responses) {
GetETag eTag = vCard.get(GetETag.class);
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
return false;
}
CaldavTask caldavTask =
caldavDao.getTask(caldavCalendar.getUuid(), vCard.hrefName());
return caldavTask == null || !eTag.getETag().equals(caldavTask.getEtag());
});
for (List<Response> items : partition(changed, 30)) {
if (items.size() == 1) {
Response vCard = items.get(0);
GetETag eTag = vCard.get(GetETag.class); GetETag eTag = vCard.get(GetETag.class);
HttpUrl url = vCard.getHref(); HttpUrl url = vCard.getHref();
if (eTag == null || isNullOrEmpty(eTag.getETag())) { if (eTag == null || isNullOrEmpty(eTag.getETag())) {
throw new DavException("Received CalDAV GET response without ETag for " + url); throw new DavException("Received CalDAV GET response without ETag for " + url);
} }
Timber.d("SINGLE %s", url); CalendarData calendarData = vCard.get(CalendarData.class);
if (calendarData == null || isNullOrEmpty(calendarData.getICalendar())) {
org.tasks.caldav.Response response = new org.tasks.caldav.Response(true); throw new DavException("Received CalDAV GET response without CalendarData for " + url);
new DavResource(httpClient, url).get("text/calendar", response);
processVTodo(vCard.hrefName(), caldavCalendar, eTag.getETag(), response.getBody());
} else {
ArrayList<HttpUrl> urls = newArrayList(Iterables.transform(items, Response::getHref));
ResponseList responses = new ResponseList(HrefRelation.MEMBER);
davCalendar.multiget(urls, responses);
Timber.d("MULTI %s", urls);
for (Response vCard : responses) {
GetETag eTag = vCard.get(GetETag.class);
HttpUrl url = vCard.getHref();
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
throw new DavException("Received CalDAV GET response without ETag for " + url);
}
CalendarData calendarData = vCard.get(CalendarData.class);
if (calendarData == null || isNullOrEmpty(calendarData.getICalendar())) {
throw new DavException(
"Received CalDAV GET response without CalendarData for " + url);
}
processVTodo(
vCard.hrefName(), caldavCalendar, eTag.getETag(), calendarData.getICalendar());
} }
}
}
List<String> deleted = processVTodo(
newArrayList( vCard.hrefName(), caldavCalendar, eTag.getETag(), calendarData.getICalendar());
difference( }
newHashSet(caldavDao.getObjects(caldavCalendar.getUuid())),
newHashSet(remoteObjects)));
if (deleted.size() > 0) {
Timber.d("DELETED %s", deleted);
taskDeleter.delete(caldavDao.getTasks(caldavCalendar.getUuid(), deleted));
} }
}
caldavCalendar.setCtag(remoteCtag); List<String> deleted =
Timber.d("UPDATE %s", caldavCalendar); newArrayList(
caldavDao.update(caldavCalendar); difference(
} catch (Exception e) { newHashSet(caldavDao.getObjects(caldavCalendar.getUuid())),
tracker.reportException(e); newHashSet(remoteObjects)));
if (deleted.size() > 0) {
Timber.d("DELETED %s", deleted);
taskDeleter.delete(caldavDao.getTasks(caldavCalendar.getUuid(), deleted));
} }
caldavCalendar.setCtag(remoteCtag);
Timber.d("UPDATE %s", caldavCalendar);
caldavDao.update(caldavCalendar);
localBroadcastManager.broadcastRefresh(); localBroadcastManager.broadcastRefresh();
} }

@ -28,11 +28,15 @@ import com.todoroo.astrid.service.TaskCreator;
import com.todoroo.astrid.service.TaskDeleter; import com.todoroo.astrid.service.TaskDeleter;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.net.ssl.SSLException;
import org.tasks.LocalBroadcastManager; import org.tasks.LocalBroadcastManager;
import org.tasks.R; import org.tasks.R;
import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracker;
@ -139,6 +143,9 @@ public class GoogleTaskSynchronizer {
} else { } else {
account.setError(context.getString(R.string.requires_pro_subscription)); account.setError(context.getString(R.string.requires_pro_subscription));
} }
} catch (SocketTimeoutException | SSLException | ConnectException | UnknownHostException e) {
Timber.e(e);
account.setError(e.getMessage());
} catch (UserRecoverableAuthIOException e) { } catch (UserRecoverableAuthIOException e) {
Timber.e(e); Timber.e(e);
sendNotification(context, e.getIntent()); sendNotification(context, e.getIntent());
@ -322,10 +329,21 @@ public class GoogleTaskSynchronizer {
: null; : null;
String previous = String previous =
googleTaskDao.getPrevious(listId, gtasksMetadata.getParent(), gtasksMetadata.getOrder()); Strings.isNullOrEmpty(localParent)
? null
: googleTaskDao.getPrevious(
listId, gtasksMetadata.getParent(), gtasksMetadata.getOrder());
com.google.api.services.tasks.model.Task created = com.google.api.services.tasks.model.Task created;
gtasksInvoker.createGtask(listId, remoteModel, localParent, previous); try {
created = gtasksInvoker.createGtask(listId, remoteModel, localParent, previous);
} catch (GoogleJsonResponseException e) {
if (e.getStatusCode() == 404) {
created = gtasksInvoker.createGtask(listId, remoteModel, null, null);
} else {
throw e;
}
}
if (created != null) { if (created != null) {
// Update the metadata for the newly created task // Update the metadata for the newly created task

@ -12,10 +12,14 @@ import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.drive.model.File; import com.google.api.services.drive.model.File;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.net.ssl.SSLException;
import org.tasks.R; import org.tasks.R;
import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracker;
import org.tasks.drive.DriveInvoker; import org.tasks.drive.DriveInvoker;
@ -23,6 +27,7 @@ import org.tasks.injection.ForApplication;
import org.tasks.injection.InjectingWorker; import org.tasks.injection.InjectingWorker;
import org.tasks.injection.JobComponent; import org.tasks.injection.JobComponent;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import timber.log.Timber;
public class DriveUploader extends InjectingWorker { public class DriveUploader extends InjectingWorker {
@ -65,11 +70,22 @@ public class DriveUploader extends InjectingWorker {
if (inputData.getBoolean(EXTRA_PURGE, false)) { if (inputData.getBoolean(EXTRA_PURGE, false)) {
List<File> files = drive.getFilesByPrefix(folder.getId(), "auto."); List<File> files = drive.getFilesByPrefix(folder.getId(), "auto.");
for (File file : getDeleteList(files)) { for (File file : getDeleteList(files)) {
drive.delete(file); try {
drive.delete(file);
} catch (GoogleJsonResponseException e) {
if (e.getStatusCode() == 404) {
Timber.e(e);
} else {
throw e;
}
}
} }
} }
return Result.success(); return Result.success();
} catch (SocketTimeoutException | SSLException | ConnectException | UnknownHostException e) {
Timber.e(e);
return Result.retry();
} catch (IOException e) { } catch (IOException e) {
tracker.reportException(e); tracker.reportException(e);
return Result.failure(); return Result.failure();

Loading…
Cancel
Save