Add Google Tasks unsuccessful response handler

pull/437/head
Alex Baker 9 years ago
parent ee72f95127
commit 84c3a4228c

@ -3,7 +3,8 @@ package com.todoroo.astrid.gtasks.api;
import android.content.Context;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.jackson2.JacksonFactory;
@ -17,6 +18,8 @@ import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import org.tasks.AccountManager;
import org.tasks.BuildConfig;
import org.tasks.analytics.Tracker;
import org.tasks.gtasks.GoogleTasksUnsuccessfulResponseHandler;
import org.tasks.injection.ForApplication;
import java.io.IOException;
@ -36,13 +39,15 @@ import timber.log.Timber;
@Singleton
public class GtasksInvoker {
private AccountManager accountManager;
private final Context context;
private final Tracker tracker;
private final GoogleAccountCredential credential;
private Tasks service;
private final Tasks service;
@Inject
public GtasksInvoker(@ForApplication Context context, GtasksPreferenceService preferenceService, AccountManager accountManager) {
this.accountManager = accountManager;
public GtasksInvoker(@ForApplication Context context, GtasksPreferenceService preferenceService, Tracker tracker) {
this.context = context;
this.tracker = tracker;
credential = GoogleAccountCredential.usingOAuth2(context, Collections.singletonList(TasksScopes.TASKS));
setUserName(preferenceService.getUserName());
service = new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
@ -54,25 +59,6 @@ public class GtasksInvoker {
credential.setSelectedAccountName(username);
}
//If we get a 401 or 403, try revalidating the auth token before bailing
private synchronized void handleException(IOException e) throws IOException {
Timber.e(e, e.getMessage());
if (e instanceof HttpResponseException) {
HttpResponseException h = (HttpResponseException) e;
int statusCode = h.getStatusCode();
if (statusCode == 401 || statusCode == 403) {
accountManager.clearToken(credential);
} else if (statusCode == 400 || statusCode == 500) {
throw h;
} else if (statusCode == 404) {
throw new HttpNotFoundException(h);
} else {
Timber.e(e, "%s: %s", statusCode, h.getStatusMessage());
}
// 503 errors are generally either 1) quota limit reached or 2) problems on Google's end
}
}
public TaskLists allGtaskLists(String pageToken) throws IOException {
return execute(service
.tasklists()
@ -141,10 +127,13 @@ public class GtasksInvoker {
Timber.d("%s request: %s", caller, request);
T response;
try {
response = request.execute();
HttpRequest httpRequest = request.buildHttpRequest();
httpRequest.setUnsuccessfulResponseHandler(new GoogleTasksUnsuccessfulResponseHandler(context, credential));
HttpResponse httpResponse = httpRequest.execute();
response = httpResponse.parseAs(request.getResponseClass());
} catch (IOException e) {
handleException(e);
response = request.execute();
tracker.reportException(e);
throw e;
}
Timber.d("%s response: %s", caller, prettyPrint(response));
return response;

@ -8,7 +8,6 @@ import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.api.client.googleapis.extensions.android.accounts.GoogleAccountManager;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.services.tasks.TasksScopes;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
@ -31,18 +30,6 @@ import static java.util.Arrays.asList;
public class AccountManager {
public void clearToken(GoogleAccountCredential credential) throws IOException {
try {
String token = credential.getToken();
Timber.d("Invalidating %s", token);
GoogleAuthUtil.clearToken(context, token);
GoogleAuthUtil.getTokenWithNotification(context, credential.getSelectedAccount(), "oauth2:" + TasksScopes.TASKS, null);
} catch (GoogleAuthException e) {
Timber.e(e, e.getMessage());
throw new IOException(e);
}
}
public interface AuthResultHandler {
void authenticationSuccessful(String accountName);
void authenticationFailed(String message);

@ -54,6 +54,7 @@ import com.todoroo.astrid.service.TaskService;
import org.tasks.Broadcaster;
import org.tasks.R;
import org.tasks.analytics.Tracker;
import org.tasks.injection.InjectingAbstractThreadedSyncAdapter;
import org.tasks.injection.SyncAdapterComponent;
import org.tasks.preferences.Preferences;
@ -97,6 +98,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter
@Inject TaskDao taskDao;
@Inject MetadataDao metadataDao;
@Inject GtasksMetadata gtasksMetadataFactory;
@Inject Tracker tracker;
public GoogleTaskSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
@ -139,38 +141,33 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter
synchronize();
gtasksPreferenceService.recordSuccessfulSync();
} catch (Exception e) {
Timber.e(e, e.getMessage());
tracker.reportException(e);
} finally {
callback.finished();
Timber.d("%s: end sync", account);
}
}
private void synchronize() {
private void synchronize() throws IOException {
pushLocalChanges();
List<TaskList> gtaskLists = new ArrayList<>();
try {
String nextPageToken = null;
do {
TaskLists remoteLists = gtasksInvoker.allGtaskLists(nextPageToken);
if (remoteLists == null) {
break;
}
List<TaskList> items = remoteLists.getItems();
if (items != null) {
gtaskLists.addAll(items);
}
nextPageToken = remoteLists.getNextPageToken();
} while (nextPageToken != null);
gtasksListService.updateLists(gtaskLists);
if (gtasksListService.getList(gtasksPreferenceService.getDefaultList()) == null) {
gtasksPreferenceService.setDefaultList(null);
String nextPageToken = null;
do {
TaskLists remoteLists = gtasksInvoker.allGtaskLists(nextPageToken);
if (remoteLists == null) {
break;
}
} catch (IOException e) {
Timber.e(e, e.getMessage());
List<TaskList> items = remoteLists.getItems();
if (items != null) {
gtaskLists.addAll(items);
}
nextPageToken = remoteLists.getNextPageToken();
} while (nextPageToken != null);
gtasksListService.updateLists(gtaskLists);
if (gtasksListService.getList(gtasksPreferenceService.getDefaultList()) == null) {
gtasksPreferenceService.setDefaultList(null);
}
for (final GtasksList list : gtasksListService.getListsToUpdate(gtaskLists)) {
fetchAndApplyRemoteChanges(list);
}
@ -184,7 +181,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter
try {
pushTask(task, task.getMergedValues(), gtasksInvoker);
} catch (IOException e) {
Timber.e(e, e.getMessage());
tracker.reportException(e);
}
}
}
@ -268,7 +265,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter
try {
invoker.updateGtask(listId, remoteModel);
} catch(HttpNotFoundException e) {
Timber.e("Received 404 response, deleting %s", gtasksMetadata);
tracker.reportException(e);
metadataDao.delete(gtasksMetadata.getId());
return;
}
@ -336,7 +333,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter
gtasksTaskListUpdater.correctOrderAndIndentForList(listId);
}
} catch (IOException e) {
Timber.e(e, e.getMessage());
tracker.reportException(e);
}
}

@ -0,0 +1,66 @@
package org.tasks.gtasks;
import android.content.Context;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
import com.google.api.client.util.BackOff;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.tasks.TasksScopes;
import com.todoroo.astrid.gtasks.api.HttpNotFoundException;
import java.io.IOException;
import timber.log.Timber;
public class GoogleTasksUnsuccessfulResponseHandler implements HttpUnsuccessfulResponseHandler {
private static final BackOff BACKOFF = new ExponentialBackOff.Builder().build();
private final Context context;
private final GoogleAccountCredential googleAccountCredential;
private final HttpBackOffUnsuccessfulResponseHandler backoffHandler = new HttpBackOffUnsuccessfulResponseHandler(BACKOFF);
public GoogleTasksUnsuccessfulResponseHandler(Context context, GoogleAccountCredential googleAccountCredential) {
this.context = context;
this.googleAccountCredential = googleAccountCredential;
}
@Override
public boolean handleResponse(HttpRequest request, HttpResponse response, boolean supportsRetry) throws IOException {
HttpResponseException httpResponseException = new HttpResponseException(response);
Timber.e(httpResponseException, httpResponseException.getMessage());
if (!supportsRetry) {
return false;
}
int statusCode = response.getStatusCode();
if ((statusCode == HttpStatusCodes.STATUS_CODE_UNAUTHORIZED || statusCode == HttpStatusCodes.STATUS_CODE_FORBIDDEN)) {
clearToken(googleAccountCredential);
} else if (statusCode == 400) { // bad request
throw httpResponseException;
} else if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
throw new HttpNotFoundException(httpResponseException);
}
return backoffHandler.handleResponse(request, response, supportsRetry);
}
private void clearToken(GoogleAccountCredential credential) throws IOException {
try {
String token = credential.getToken();
Timber.d("Invalidating %s", token);
GoogleAuthUtil.clearToken(context, token);
GoogleAuthUtil.getTokenWithNotification(context, credential.getSelectedAccount(), "oauth2:" + TasksScopes.TASKS, null);
} catch (GoogleAuthException e) {
Timber.e(e, e.getMessage());
throw new IOException(e);
}
}
}
Loading…
Cancel
Save