From 904edd9b43e10e17c3784af261d3fedd8301360c Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Tue, 6 Sep 2016 18:28:39 -0500 Subject: [PATCH] Display notification for recoverable auth errors --- .../astrid/gtasks/api/GtasksInvoker.java | 19 +++-------- .../astrid/gtasks/sync/GtasksSyncService.java | 10 ++++-- .../tasks/gtasks/GoogleTaskSyncAdapter.java | 34 +++++++++++++++++-- ...oogleTasksUnsuccessfulResponseHandler.java | 15 +++++--- src/googleplay/res/values/strings.xml | 5 --- .../com/todoroo/astrid/utility/Constants.java | 1 + src/main/res/values/strings.xml | 1 + 7 files changed, 58 insertions(+), 27 deletions(-) delete mode 100755 src/googleplay/res/values/strings.xml diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java b/src/googleplay/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java index d8c39f34b..99d1f864b 100644 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java +++ b/src/googleplay/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java @@ -17,7 +17,6 @@ import com.google.api.services.tasks.model.TaskLists; import com.todoroo.astrid.gtasks.GtasksPreferenceService; import org.tasks.BuildConfig; -import org.tasks.analytics.Tracker; import org.tasks.gtasks.GoogleTasksUnsuccessfulResponseHandler; import org.tasks.injection.ApplicationScope; import org.tasks.injection.ForApplication; @@ -39,14 +38,12 @@ import timber.log.Timber; public class GtasksInvoker { private final Context context; - private final Tracker tracker; private final GoogleAccountCredential credential; private final Tasks service; @Inject - public GtasksInvoker(@ForApplication Context context, GtasksPreferenceService preferenceService, Tracker tracker) { + public GtasksInvoker(@ForApplication Context context, GtasksPreferenceService preferenceService) { 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) @@ -124,16 +121,10 @@ public class GtasksInvoker { private synchronized T execute(TasksRequest request) throws IOException { String caller = getCaller(); Timber.d("%s request: %s", caller, request); - T response; - try { - HttpRequest httpRequest = request.buildHttpRequest(); - httpRequest.setUnsuccessfulResponseHandler(new GoogleTasksUnsuccessfulResponseHandler(context, credential)); - HttpResponse httpResponse = httpRequest.execute(); - response = httpResponse.parseAs(request.getResponseClass()); - } catch (IOException e) { - tracker.reportException(e); - throw e; - } + HttpRequest httpRequest = request.buildHttpRequest(); + httpRequest.setUnsuccessfulResponseHandler(new GoogleTasksUnsuccessfulResponseHandler(context, credential)); + HttpResponse httpResponse = httpRequest.execute(); + T response = httpResponse.parseAs(request.getResponseClass()); Timber.d("%s response: %s", caller, prettyPrint(response)); return response; } diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java b/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java index e18f2e242..4810f6510 100644 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java +++ b/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java @@ -7,6 +7,7 @@ package com.todoroo.astrid.gtasks.sync; import android.text.TextUtils; +import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Field; import com.todoroo.andlib.sql.Functions; @@ -24,6 +25,7 @@ import com.todoroo.astrid.gtasks.OrderedMetadataListUpdater; import com.todoroo.astrid.gtasks.api.GtasksInvoker; import com.todoroo.astrid.gtasks.api.MoveRequest; +import org.tasks.analytics.Tracker; import org.tasks.gtasks.SyncAdapterHelper; import org.tasks.injection.ApplicationScope; @@ -46,17 +48,19 @@ public class GtasksSyncService { private final GtasksInvoker gtasksInvoker; private final LinkedBlockingQueue operationQueue = new LinkedBlockingQueue<>(); private final SyncAdapterHelper syncAdapterHelper; + private final Tracker tracker; @Inject public GtasksSyncService(MetadataDao metadataDao, TaskDao taskDao, GtasksPreferenceService gtasksPreferenceService, GtasksInvoker gtasksInvoker, - SyncAdapterHelper syncAdapterHelper) { + SyncAdapterHelper syncAdapterHelper, Tracker tracker) { this.metadataDao = metadataDao; this.taskDao = taskDao; this.gtasksPreferenceService = gtasksPreferenceService; this.gtasksInvoker = gtasksInvoker; this.syncAdapterHelper = syncAdapterHelper; + this.tracker = tracker; new OperationPushThread(operationQueue).start(); } @@ -110,8 +114,10 @@ public class GtasksSyncService { } try { op.op(gtasksInvoker); + } catch (UserRecoverableAuthIOException ignored) { + } catch (IOException e) { - Timber.e(e, e.getMessage()); + tracker.reportException(e); } } } diff --git a/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java b/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java index 85f7151f8..f50f7b85a 100644 --- a/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java +++ b/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java @@ -17,14 +17,18 @@ package org.tasks.gtasks; import android.accounts.Account; +import android.app.PendingIntent; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.SyncResult; import android.os.Bundle; +import android.support.v4.app.NotificationCompat; import android.text.TextUtils; +import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException; import com.google.api.services.tasks.model.TaskList; import com.google.api.services.tasks.model.TaskLists; import com.google.api.services.tasks.model.Tasks; @@ -50,12 +54,14 @@ import com.todoroo.astrid.gtasks.api.HttpNotFoundException; import com.todoroo.astrid.gtasks.sync.GtasksSyncService; import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer; import com.todoroo.astrid.service.TaskService; +import com.todoroo.astrid.utility.Constants; 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.notifications.NotificationManager; import org.tasks.preferences.Preferences; import org.tasks.sync.RecordSyncStatusCallback; import org.tasks.time.DateTime; @@ -98,6 +104,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter @Inject MetadataDao metadataDao; @Inject GtasksMetadata gtasksMetadataFactory; @Inject Tracker tracker; + @Inject NotificationManager notificationManager; public GoogleTaskSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); @@ -135,6 +142,11 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter callback.started(); synchronize(); gtasksPreferenceService.recordSuccessfulSync(); + } catch (UserRecoverableAuthIOException e) { + Timber.e(e, e.getMessage()); + sendNotification(getContext(), e.getIntent()); + } catch (IOException e) { + Timber.e(e, e.getMessage()); } catch (Exception e) { tracker.reportException(e); } finally { @@ -143,6 +155,20 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter } } + private void sendNotification(Context context, Intent intent) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_FROM_BACKGROUND); + + PendingIntent resolve = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setAutoCancel(true) + .setContentIntent(resolve) + .setContentTitle(context.getString(R.string.sync_error_permissions)) + .setContentText(context.getString(R.string.common_google_play_services_notification_ticker)) + .setAutoCancel(true) + .setSmallIcon(android.R.drawable.ic_dialog_alert) + .setTicker(context.getString(R.string.common_google_play_services_notification_ticker)); + notificationManager.notify(Constants.NOTIFICATION_SYNC_ERROR, builder.build()); + } + private void synchronize() throws IOException { pushLocalChanges(); @@ -168,13 +194,15 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter } } - private void pushLocalChanges() { + private void pushLocalChanges() throws UserRecoverableAuthIOException { List tasks = taskDao.toList(Query.select(Task.PROPERTIES) .join(Join.left(Metadata.TABLE, Criterion.and(MetadataDao.MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), Task.ID.eq(Metadata.TASK)))) .where(Criterion.or(Task.MODIFICATION_DATE.gt(GtasksMetadata.LAST_SYNC), GtasksMetadata.ID.eq("")))); for (Task task : tasks) { try { pushTask(task, task.getMergedValues(), gtasksInvoker); + } catch (UserRecoverableAuthIOException e) { + throw e; } catch (IOException e) { tracker.reportException(e); } @@ -292,7 +320,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter MetadataDao.MetadataCriteria.isDeleted())); } - private synchronized void fetchAndApplyRemoteChanges(GtasksList list) { + private synchronized void fetchAndApplyRemoteChanges(GtasksList list) throws UserRecoverableAuthIOException { String listId = list.getRemoteId(); long lastSyncDate = list.getLastSync(); @@ -327,6 +355,8 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter storeObjectDao.persist(list); gtasksTaskListUpdater.correctOrderAndIndentForList(listId); } + } catch (UserRecoverableAuthIOException e) { + throw e; } catch (IOException e) { tracker.reportException(e); } diff --git a/src/googleplay/java/org/tasks/gtasks/GoogleTasksUnsuccessfulResponseHandler.java b/src/googleplay/java/org/tasks/gtasks/GoogleTasksUnsuccessfulResponseHandler.java index 976732898..acb731850 100644 --- a/src/googleplay/java/org/tasks/gtasks/GoogleTasksUnsuccessfulResponseHandler.java +++ b/src/googleplay/java/org/tasks/gtasks/GoogleTasksUnsuccessfulResponseHandler.java @@ -42,7 +42,10 @@ public class GoogleTasksUnsuccessfulResponseHandler implements HttpUnsuccessfulR } int statusCode = response.getStatusCode(); if ((statusCode == HttpStatusCodes.STATUS_CODE_UNAUTHORIZED || statusCode == HttpStatusCodes.STATUS_CODE_FORBIDDEN)) { - clearToken(googleAccountCredential); + boolean shouldRetry = clearToken(googleAccountCredential); + if (!shouldRetry) { + return false; + } } else if (statusCode == 400) { // bad request throw httpResponseException; } else if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { @@ -52,15 +55,19 @@ public class GoogleTasksUnsuccessfulResponseHandler implements HttpUnsuccessfulR return backoffHandler.handleResponse(request, response, supportsRetry); } - private void clearToken(GoogleAccountCredential credential) throws IOException { + private boolean clearToken(GoogleAccountCredential credential){ try { String token = credential.getToken(); Timber.d("Invalidating %s", token); GoogleAuthUtil.clearToken(context, token); - GoogleAuthUtil.getTokenWithNotification(context, credential.getSelectedAccount(), "oauth2:" + TasksScopes.TASKS, null); + GoogleAuthUtil.getToken(context, credential.getSelectedAccount(), "oauth2:" + TasksScopes.TASKS, null); + return true; } catch (GoogleAuthException e) { Timber.e(e, e.getMessage()); - throw new IOException(e); + return false; + } catch (IOException e) { + Timber.e(e, e.getMessage()); + return true; } } } diff --git a/src/googleplay/res/values/strings.xml b/src/googleplay/res/values/strings.xml deleted file mode 100755 index 6c0b8a359..000000000 --- a/src/googleplay/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/main/java/com/todoroo/astrid/utility/Constants.java b/src/main/java/com/todoroo/astrid/utility/Constants.java index 856fd290c..9106d6836 100644 --- a/src/main/java/com/todoroo/astrid/utility/Constants.java +++ b/src/main/java/com/todoroo/astrid/utility/Constants.java @@ -18,4 +18,5 @@ public final class Constants { /** Notification Manager id for timing */ public static final int NOTIFICATION_TIMER = -2; + public static final int NOTIFICATION_SYNC_ERROR = -3; } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 18c51d189..b01e580bd 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -822,5 +822,6 @@ File %1$s contained %2$s.\n\n Row settings Notification shade Show task description + Tasks requires permission