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 android.content.Context;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential; 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.http.javanet.NetHttpTransport;
import com.google.api.client.json.GenericJson; import com.google.api.client.json.GenericJson;
import com.google.api.client.json.jackson2.JacksonFactory; 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.AccountManager;
import org.tasks.BuildConfig; import org.tasks.BuildConfig;
import org.tasks.analytics.Tracker;
import org.tasks.gtasks.GoogleTasksUnsuccessfulResponseHandler;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import java.io.IOException; import java.io.IOException;
@ -36,13 +39,15 @@ import timber.log.Timber;
@Singleton @Singleton
public class GtasksInvoker { public class GtasksInvoker {
private AccountManager accountManager; private final Context context;
private final Tracker tracker;
private final GoogleAccountCredential credential; private final GoogleAccountCredential credential;
private Tasks service; private final Tasks service;
@Inject @Inject
public GtasksInvoker(@ForApplication Context context, GtasksPreferenceService preferenceService, AccountManager accountManager) { public GtasksInvoker(@ForApplication Context context, GtasksPreferenceService preferenceService, Tracker tracker) {
this.accountManager = accountManager; this.context = context;
this.tracker = tracker;
credential = GoogleAccountCredential.usingOAuth2(context, Collections.singletonList(TasksScopes.TASKS)); credential = GoogleAccountCredential.usingOAuth2(context, Collections.singletonList(TasksScopes.TASKS));
setUserName(preferenceService.getUserName()); setUserName(preferenceService.getUserName());
service = new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential) service = new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
@ -54,25 +59,6 @@ public class GtasksInvoker {
credential.setSelectedAccountName(username); 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 { public TaskLists allGtaskLists(String pageToken) throws IOException {
return execute(service return execute(service
.tasklists() .tasklists()
@ -141,10 +127,13 @@ public class GtasksInvoker {
Timber.d("%s request: %s", caller, request); Timber.d("%s request: %s", caller, request);
T response; T response;
try { 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) { } catch (IOException e) {
handleException(e); tracker.reportException(e);
response = request.execute(); throw e;
} }
Timber.d("%s response: %s", caller, prettyPrint(response)); Timber.d("%s response: %s", caller, prettyPrint(response));
return 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.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException; 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.accounts.GoogleAccountManager;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.services.tasks.TasksScopes; import com.google.api.services.tasks.TasksScopes;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -31,18 +30,6 @@ import static java.util.Arrays.asList;
public class AccountManager { 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 { public interface AuthResultHandler {
void authenticationSuccessful(String accountName); void authenticationSuccessful(String accountName);
void authenticationFailed(String message); void authenticationFailed(String message);

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