From 31bb7b1abde1652dc178309df5910de762512372 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Tue, 28 May 2019 14:59:44 -0500 Subject: [PATCH] Add Flipper network interceptors --- app/src/debug/java/org/tasks/BuildSetup.java | 2 + .../tasks/ChainedHttpExecuteInterceptor.java | 21 +++++ .../org/tasks/DebugNetworkInterceptor.java | 39 ++++++++ .../org/tasks/FlipperHttpInterceptor.java | 92 +++++++++++++++++++ .../astrid/gtasks/api/GtasksInvoker.java | 57 ++++++++++-- .../activities/AddCaldavAccountViewModel.java | 4 +- .../activities/CreateCalendarViewModel.java | 5 +- .../tasks/activities/CreateListViewModel.java | 5 +- .../activities/DeleteCalendarViewModel.java | 6 +- .../tasks/activities/DeleteListViewModel.java | 5 +- .../GoogleTaskListSettingsActivity.java | 9 +- .../tasks/activities/RenameListViewModel.java | 8 +- .../UpdateCaldavAccountViewModel.java | 5 +- .../caldav/CaldavAccountSettingsActivity.java | 7 +- .../CaldavCalendarSettingsActivity.java | 9 +- .../java/org/tasks/caldav/CaldavClient.java | 68 ++++++++++---- .../org/tasks/caldav/CaldavSynchronizer.java | 11 +-- .../java/org/tasks/data/CaldavAccount.java | 5 + .../tasks/gtasks/GoogleTaskSynchronizer.java | 7 +- .../org/tasks/preferences/Preferences.java | 5 + .../org/tasks/DebugNetworkInterceptor.java | 16 ++++ 21 files changed, 319 insertions(+), 67 deletions(-) create mode 100644 app/src/debug/java/org/tasks/ChainedHttpExecuteInterceptor.java create mode 100644 app/src/debug/java/org/tasks/DebugNetworkInterceptor.java create mode 100644 app/src/debug/java/org/tasks/FlipperHttpInterceptor.java create mode 100644 app/src/release/java/org/tasks/DebugNetworkInterceptor.java diff --git a/app/src/debug/java/org/tasks/BuildSetup.java b/app/src/debug/java/org/tasks/BuildSetup.java index 42da00c1a..7cf376ee6 100644 --- a/app/src/debug/java/org/tasks/BuildSetup.java +++ b/app/src/debug/java/org/tasks/BuildSetup.java @@ -9,6 +9,7 @@ import com.facebook.flipper.core.FlipperClient; import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; import com.facebook.flipper.plugins.inspector.DescriptorMapping; import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.soloader.SoLoader; import com.squareup.leakcanary.LeakCanary; @@ -35,6 +36,7 @@ public class BuildSetup { FlipperClient client = AndroidFlipperClient.getInstance(context); client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new NetworkFlipperPlugin()); client.addPlugin(new SharedPreferencesFlipperPlugin(context)); client.start(); } diff --git a/app/src/debug/java/org/tasks/ChainedHttpExecuteInterceptor.java b/app/src/debug/java/org/tasks/ChainedHttpExecuteInterceptor.java new file mode 100644 index 000000000..60c1e5948 --- /dev/null +++ b/app/src/debug/java/org/tasks/ChainedHttpExecuteInterceptor.java @@ -0,0 +1,21 @@ +package org.tasks; + +import com.google.api.client.http.HttpExecuteInterceptor; +import com.google.api.client.http.HttpRequest; +import java.io.IOException; + +public class ChainedHttpExecuteInterceptor implements HttpExecuteInterceptor { + + private final HttpExecuteInterceptor[] interceptors; + + ChainedHttpExecuteInterceptor(HttpExecuteInterceptor... interceptors) { + this.interceptors = interceptors; + } + + @Override + public void intercept(HttpRequest request) throws IOException { + for (HttpExecuteInterceptor interceptor : interceptors) { + interceptor.intercept(request); + } + } +} diff --git a/app/src/debug/java/org/tasks/DebugNetworkInterceptor.java b/app/src/debug/java/org/tasks/DebugNetworkInterceptor.java new file mode 100644 index 000000000..d3902fb16 --- /dev/null +++ b/app/src/debug/java/org/tasks/DebugNetworkInterceptor.java @@ -0,0 +1,39 @@ +package org.tasks; + +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.google.api.client.http.HttpRequest; +import java.io.IOException; +import javax.inject.Inject; +import okhttp3.OkHttpClient; +import org.tasks.injection.ForApplication; + +public class DebugNetworkInterceptor { + + private final Context context; + + @Inject + public DebugNetworkInterceptor(@ForApplication Context context) { + this.context = context; + } + + private static NetworkFlipperPlugin getNetworkPlugin(Context context) { + return AndroidFlipperClient.getInstance(context).getPlugin(NetworkFlipperPlugin.ID); + } + + public void add(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(getNetworkPlugin(context))); + } + + public T execute(HttpRequest request, Class responseClass) throws IOException { + FlipperHttpInterceptor interceptor = + new FlipperHttpInterceptor<>(getNetworkPlugin(context), responseClass); + request + .setInterceptor(new ChainedHttpExecuteInterceptor(request.getInterceptor(), interceptor)) + .setResponseInterceptor(interceptor) + .execute(); + return interceptor.getResponse(); + } +} diff --git a/app/src/debug/java/org/tasks/FlipperHttpInterceptor.java b/app/src/debug/java/org/tasks/FlipperHttpInterceptor.java new file mode 100644 index 000000000..7dd5e6821 --- /dev/null +++ b/app/src/debug/java/org/tasks/FlipperHttpInterceptor.java @@ -0,0 +1,92 @@ +package org.tasks; + +import static com.todoroo.andlib.utility.DateUtilities.now; + +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.network.NetworkReporter.Header; +import com.facebook.flipper.plugins.network.NetworkReporter.RequestInfo; +import com.facebook.flipper.plugins.network.NetworkReporter.ResponseInfo; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.HttpExecuteInterceptor; +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpResponseInterceptor; +import com.google.api.client.json.GenericJson; +import com.todoroo.astrid.helper.UUIDHelper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import timber.log.Timber; + +public class FlipperHttpInterceptor implements HttpExecuteInterceptor, HttpResponseInterceptor { + + private final Class responseClass; + private final String requestId = UUIDHelper.newUUID(); + private final NetworkFlipperPlugin plugin; + private T body; + + FlipperHttpInterceptor(NetworkFlipperPlugin plugin, Class responseClass) { + this.responseClass = responseClass; + this.plugin = plugin; + } + + @Override + public void intercept(HttpRequest request) { + RequestInfo requestInfo = new RequestInfo(); + requestInfo.method = request.getRequestMethod(); + requestInfo.body = bodyToByteArray(request.getContent()); + requestInfo.headers = getHeaders(request.getHeaders()); + requestInfo.requestId = requestId; + requestInfo.timeStamp = now(); + requestInfo.uri = request.getUrl().toString(); + plugin.reportRequest(requestInfo); + } + + @Override + public void interceptResponse(HttpResponse response) throws IOException { + ResponseInfo responseInfo = new ResponseInfo(); + responseInfo.timeStamp = now(); + responseInfo.headers = getHeaders(response.getHeaders()); + responseInfo.requestId = requestId; + responseInfo.statusCode = response.getStatusCode(); + responseInfo.statusReason = response.getStatusMessage(); + body = response.parseAs(responseClass); + if (body instanceof GenericJson) { + try { + responseInfo.body = ((GenericJson) body).toPrettyString().getBytes(); + } catch (IOException e) { + Timber.e(e); + } + } + plugin.reportResponse(responseInfo); + } + + public T getResponse() { + return body; + } + + private List
getHeaders(HttpHeaders headers) { + List
result = new ArrayList<>(); + for (Map.Entry entry : headers.entrySet()) { + result.add(new Header(entry.getKey(), entry.getValue().toString())); + } + return result; + } + + private byte[] bodyToByteArray(HttpContent content) { + if (content == null) { + return null; + } + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try { + content.writeTo(output); + } catch (IOException e) { + Timber.e(e); + return null; + } + return output.toByteArray(); + } +} diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java b/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java index f66c46d91..e6a90d96a 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java +++ b/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.java @@ -1,10 +1,11 @@ package com.todoroo.astrid.gtasks.api; import android.accounts.AccountManager; +import android.content.Context; import android.os.Bundle; import androidx.annotation.Nullable; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; -import com.google.api.client.googleapis.services.AbstractGoogleClientRequest; +import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpResponseException; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.GenericJson; @@ -16,10 +17,13 @@ import com.google.api.services.tasks.model.Task; import com.google.api.services.tasks.model.TaskList; import com.google.api.services.tasks.model.TaskLists; import com.google.common.base.Strings; -import com.google.gson.GsonBuilder; import java.io.IOException; +import javax.inject.Inject; import org.tasks.BuildConfig; +import org.tasks.DebugNetworkInterceptor; import org.tasks.gtasks.GoogleAccountManager; +import org.tasks.injection.ForApplication; +import org.tasks.preferences.Preferences; import timber.log.Timber; /** @@ -30,20 +34,50 @@ import timber.log.Timber; */ public class GtasksInvoker { - private final String account; + private final Context context; private final GoogleAccountManager googleAccountManager; - private final Tasks service; + private final Preferences preferences; + private final DebugNetworkInterceptor interceptor; private final GoogleCredential credential = new GoogleCredential(); + private final String account; + private final Tasks service; - public GtasksInvoker(String account, GoogleAccountManager googleAccountManager) { - this.account = account; + @Inject + public GtasksInvoker( + @ForApplication Context context, + GoogleAccountManager googleAccountManager, + Preferences preferences, + DebugNetworkInterceptor interceptor) { + this.context = context; + this.googleAccountManager = googleAccountManager; + this.preferences = preferences; + this.interceptor = interceptor; + account = null; + service = null; + } + + private GtasksInvoker( + Context context, + GoogleAccountManager googleAccountManager, + Preferences preferences, + DebugNetworkInterceptor interceptor, + String account) { + this.context = context; this.googleAccountManager = googleAccountManager; + this.preferences = preferences; + this.interceptor = interceptor; + this.account = account; + service = new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential) .setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME)) .build(); } + public GtasksInvoker forAccount(String account) { + return new GtasksInvoker(context, googleAccountManager, preferences, interceptor, account); + } + private void checkToken() { if (Strings.isNullOrEmpty(credential.getAccessToken())) { Bundle bundle = googleAccountManager.getAccessToken(account, TasksScopes.TASKS); @@ -115,10 +149,15 @@ public class GtasksInvoker { private synchronized @Nullable T execute(TasksRequest request, boolean retry) throws IOException { checkToken(); - Timber.d("%s request: %s", getCaller(retry), prettyPrint(request)); T response; try { - response = request.execute(); + HttpRequest httpRequest = request.buildHttpRequest(); + Timber.d("%s", httpRequest.getUrl()); + if (preferences.isFlipperEnabled()) { + response = interceptor.execute(httpRequest, request.getResponseClass()); + } else { + response = httpRequest.execute().parseAs(request.getResponseClass()); + } } catch (HttpResponseException e) { if (e.getStatusCode() == 401 && !retry) { googleAccountManager.invalidateToken(credential.getAccessToken()); @@ -136,8 +175,6 @@ public class GtasksInvoker { if (BuildConfig.DEBUG) { if (object instanceof GenericJson) { return ((GenericJson) object).toPrettyString(); - } else if (object instanceof AbstractGoogleClientRequest) { - return new GsonBuilder().setPrettyPrinting().create().toJson(object); } } return object; diff --git a/app/src/main/java/org/tasks/activities/AddCaldavAccountViewModel.java b/app/src/main/java/org/tasks/activities/AddCaldavAccountViewModel.java index c597b0fb6..21e1b3f92 100644 --- a/app/src/main/java/org/tasks/activities/AddCaldavAccountViewModel.java +++ b/app/src/main/java/org/tasks/activities/AddCaldavAccountViewModel.java @@ -4,7 +4,7 @@ import org.tasks.caldav.CaldavClient; import org.tasks.ui.CompletableViewModel; public class AddCaldavAccountViewModel extends CompletableViewModel { - public void addAccount(String url, String username, String password) { - run(() -> new CaldavClient(url, username, password).getHomeSet()); + public void addAccount(CaldavClient client, String url, String username, String password) { + run(() -> client.forUrl(url, username, password).getHomeSet()); } } diff --git a/app/src/main/java/org/tasks/activities/CreateCalendarViewModel.java b/app/src/main/java/org/tasks/activities/CreateCalendarViewModel.java index 0846c055a..8916bcba7 100644 --- a/app/src/main/java/org/tasks/activities/CreateCalendarViewModel.java +++ b/app/src/main/java/org/tasks/activities/CreateCalendarViewModel.java @@ -2,11 +2,10 @@ package org.tasks.activities; import org.tasks.caldav.CaldavClient; import org.tasks.data.CaldavAccount; -import org.tasks.security.Encryption; import org.tasks.ui.CompletableViewModel; public class CreateCalendarViewModel extends CompletableViewModel { - public void createCalendar(CaldavAccount caldavAccount, Encryption encryption, String name) { - run(() -> new CaldavClient(caldavAccount, encryption).makeCollection(name)); + public void createCalendar(CaldavClient client, CaldavAccount account, String name) { + run(() -> client.forAccount(account).makeCollection(name)); } } diff --git a/app/src/main/java/org/tasks/activities/CreateListViewModel.java b/app/src/main/java/org/tasks/activities/CreateListViewModel.java index 706935bf0..6d9c34a81 100644 --- a/app/src/main/java/org/tasks/activities/CreateListViewModel.java +++ b/app/src/main/java/org/tasks/activities/CreateListViewModel.java @@ -2,12 +2,11 @@ package org.tasks.activities; import com.google.api.services.tasks.model.TaskList; import com.todoroo.astrid.gtasks.api.GtasksInvoker; -import org.tasks.gtasks.GoogleAccountManager; import org.tasks.ui.CompletableViewModel; @SuppressWarnings("WeakerAccess") public class CreateListViewModel extends CompletableViewModel { - void createList(GoogleAccountManager googleAccountManager, String account, String name) { - run(() -> new GtasksInvoker(account, googleAccountManager).createGtaskList(name)); + void createList(GtasksInvoker invoker, String account, String name) { + run(() -> invoker.forAccount(account).createGtaskList(name)); } } diff --git a/app/src/main/java/org/tasks/activities/DeleteCalendarViewModel.java b/app/src/main/java/org/tasks/activities/DeleteCalendarViewModel.java index a7b52652f..4dc8a0891 100644 --- a/app/src/main/java/org/tasks/activities/DeleteCalendarViewModel.java +++ b/app/src/main/java/org/tasks/activities/DeleteCalendarViewModel.java @@ -3,12 +3,10 @@ package org.tasks.activities; import org.tasks.caldav.CaldavClient; import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavCalendar; -import org.tasks.security.Encryption; import org.tasks.ui.ActionViewModel; public class DeleteCalendarViewModel extends ActionViewModel { - public void deleteCalendar( - CaldavAccount caldavAccount, Encryption encryption, CaldavCalendar calendar) { - run(() -> new CaldavClient(caldavAccount, calendar, encryption).deleteCollection()); + public void deleteCalendar(CaldavClient client, CaldavAccount account, CaldavCalendar calendar) { + run(() -> client.forCalendar(account, calendar).deleteCollection()); } } diff --git a/app/src/main/java/org/tasks/activities/DeleteListViewModel.java b/app/src/main/java/org/tasks/activities/DeleteListViewModel.java index 9678994d9..606a2f22e 100644 --- a/app/src/main/java/org/tasks/activities/DeleteListViewModel.java +++ b/app/src/main/java/org/tasks/activities/DeleteListViewModel.java @@ -2,12 +2,11 @@ package org.tasks.activities; import com.todoroo.astrid.gtasks.api.GtasksInvoker; import org.tasks.data.GoogleTaskList; -import org.tasks.gtasks.GoogleAccountManager; import org.tasks.ui.ActionViewModel; @SuppressWarnings("WeakerAccess") public class DeleteListViewModel extends ActionViewModel { - void deleteList(GoogleAccountManager googleAccountManager, GoogleTaskList list) { - run(() -> new GtasksInvoker(list.getAccount(), googleAccountManager).deleteGtaskList(list.getRemoteId())); + void deleteList(GtasksInvoker invoker, GoogleTaskList list) { + run(() -> invoker.forAccount(list.getAccount()).deleteGtaskList(list.getRemoteId())); } } diff --git a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java index b7e846109..871e0bbdc 100644 --- a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java +++ b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java @@ -24,6 +24,7 @@ import com.todoroo.astrid.activity.MainActivity; import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.api.GtasksFilter; import com.todoroo.astrid.gtasks.GtasksListService; +import com.todoroo.astrid.gtasks.api.GtasksInvoker; import com.todoroo.astrid.service.TaskDeleter; import javax.inject.Inject; import org.tasks.R; @@ -59,7 +60,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi @Inject ThemeCache themeCache; @Inject ThemeColor themeColor; @Inject TaskDeleter taskDeleter; - @Inject GoogleAccountManager googleAccountManager; + @Inject GtasksInvoker gtasksInvoker; @BindView(R.id.name) TextInputEditText name; @@ -206,10 +207,10 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi if (isNewList) { showProgressIndicator(); - createListViewModel.createList(googleAccountManager, gtasksList.getAccount(), newName); + createListViewModel.createList(gtasksInvoker, gtasksList.getAccount(), newName); } else if (nameChanged()) { showProgressIndicator(); - renameListViewModel.renameList(googleAccountManager, gtasksList, newName); + renameListViewModel.renameList(gtasksInvoker, gtasksList, newName); } else { if (colorChanged()) { gtasksList.setColor(selectedTheme); @@ -250,7 +251,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi R.string.delete, (dialog, which) -> { showProgressIndicator(); - deleteListViewModel.deleteList(googleAccountManager, gtasksList); + deleteListViewModel.deleteList(gtasksInvoker, gtasksList); }) .setNegativeButton(android.R.string.cancel, null) .show(); diff --git a/app/src/main/java/org/tasks/activities/RenameListViewModel.java b/app/src/main/java/org/tasks/activities/RenameListViewModel.java index ec098516a..4f741eb17 100644 --- a/app/src/main/java/org/tasks/activities/RenameListViewModel.java +++ b/app/src/main/java/org/tasks/activities/RenameListViewModel.java @@ -3,15 +3,11 @@ package org.tasks.activities; import com.google.api.services.tasks.model.TaskList; import com.todoroo.astrid.gtasks.api.GtasksInvoker; import org.tasks.data.GoogleTaskList; -import org.tasks.gtasks.GoogleAccountManager; import org.tasks.ui.CompletableViewModel; @SuppressWarnings("WeakerAccess") public class RenameListViewModel extends CompletableViewModel { - void renameList(GoogleAccountManager googleAccountManager, GoogleTaskList list, String name) { - run( - () -> - new GtasksInvoker(list.getAccount(), googleAccountManager) - .renameGtaskList(list.getRemoteId(), name)); + void renameList(GtasksInvoker invoker, GoogleTaskList list, String name) { + run(() -> invoker.forAccount(list.getAccount()).renameGtaskList(list.getRemoteId(), name)); } } diff --git a/app/src/main/java/org/tasks/activities/UpdateCaldavAccountViewModel.java b/app/src/main/java/org/tasks/activities/UpdateCaldavAccountViewModel.java index 01b5716d9..563629676 100644 --- a/app/src/main/java/org/tasks/activities/UpdateCaldavAccountViewModel.java +++ b/app/src/main/java/org/tasks/activities/UpdateCaldavAccountViewModel.java @@ -4,7 +4,8 @@ import org.tasks.caldav.CaldavClient; import org.tasks.ui.CompletableViewModel; public class UpdateCaldavAccountViewModel extends CompletableViewModel { - public void updateCaldavAccount(String url, String username, String password) { - run(() -> new CaldavClient(url, username, password).getHomeSet()); + public void updateCaldavAccount( + CaldavClient client, String url, String username, String password) { + run(() -> client.forUrl(url, username, password).getHomeSet()); } } diff --git a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java index b6d7090a6..b882843fe 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java +++ b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java @@ -38,6 +38,7 @@ import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavDao; import org.tasks.dialogs.DialogBuilder; import org.tasks.injection.ActivityComponent; +import org.tasks.injection.ForApplication; import org.tasks.injection.ThemedInjectingAppCompatActivity; import org.tasks.preferences.Preferences; import org.tasks.security.Encryption; @@ -51,6 +52,7 @@ public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActiv public static final String EXTRA_CALDAV_DATA = "caldavData"; // $NON-NLS-1$ private static final String PASSWORD_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; + @Inject @ForApplication Context context; @Inject DialogBuilder dialogBuilder; @Inject Preferences preferences; @Inject Tracker tracker; @@ -58,6 +60,7 @@ public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActiv @Inject SyncAdapters syncAdapters; @Inject TaskDeleter taskDeleter; @Inject Encryption encryption; + @Inject CaldavClient client; @BindView(R.id.root_layout) LinearLayout root; @@ -296,10 +299,10 @@ public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActiv if (caldavAccount == null) { showProgressIndicator(); - addCaldavAccountViewModel.addAccount(url, username, password); + addCaldavAccountViewModel.addAccount(client, url, username, password); } else if (needsValidation()) { showProgressIndicator(); - updateCaldavAccountViewModel.updateCaldavAccount(url, username, password); + updateCaldavAccountViewModel.updateCaldavAccount(client, url, username, password); } else if (hasChanges()) { updateAccount(caldavAccount.getUrl()); } else { diff --git a/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java index b3a5fb7a0..832d3cd72 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java +++ b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java @@ -43,9 +43,9 @@ import org.tasks.data.CaldavCalendar; import org.tasks.data.CaldavDao; import org.tasks.dialogs.DialogBuilder; import org.tasks.injection.ActivityComponent; +import org.tasks.injection.ForApplication; import org.tasks.injection.ThemedInjectingAppCompatActivity; import org.tasks.preferences.Preferences; -import org.tasks.security.Encryption; import org.tasks.sync.SyncAdapters; import org.tasks.themes.ThemeCache; import org.tasks.themes.ThemeColor; @@ -59,6 +59,7 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi public static final String EXTRA_CALDAV_ACCOUNT = "extra_caldav_account"; private static final String EXTRA_SELECTED_THEME = "extra_selected_theme"; private static final int REQUEST_COLOR_PICKER = 10109; + @Inject @ForApplication Context context; @Inject DialogBuilder dialogBuilder; @Inject Preferences preferences; @Inject ThemeCache themeCache; @@ -66,8 +67,8 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi @Inject Tracker tracker; @Inject CaldavDao caldavDao; @Inject SyncAdapters syncAdapters; - @Inject Encryption encryption; @Inject TaskDeleter taskDeleter; + @Inject CaldavClient client; @BindView(R.id.root_layout) LinearLayout root; @@ -223,7 +224,7 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi if (caldavCalendar == null) { showProgressIndicator(); - createCalendarViewModel.createCalendar(caldavAccount, encryption, name); + createCalendarViewModel.createCalendar(client, caldavAccount, name); } else if (hasChanges()) { updateAccount(); } else { @@ -393,7 +394,7 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi R.string.delete, (dialog, which) -> { showProgressIndicator(); - deleteCalendarViewModel.deleteCalendar(caldavAccount, encryption, caldavCalendar); + deleteCalendarViewModel.deleteCalendar(client, caldavAccount, caldavCalendar); }) .setNegativeButton(android.R.string.cancel, null) .show(); diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.java b/app/src/main/java/org/tasks/caldav/CaldavClient.java index f3ea08035..87a64434b 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavClient.java +++ b/app/src/main/java/org/tasks/caldav/CaldavClient.java @@ -6,6 +6,7 @@ import static at.bitfire.dav4android.XmlUtils.NS_CARDDAV; import static at.bitfire.dav4android.XmlUtils.NS_WEBDAV; import static java.util.Arrays.asList; +import android.content.Context; import at.bitfire.dav4android.BasicDigestAuthHandler; import at.bitfire.dav4android.DavResource; import at.bitfire.dav4android.DavResponse; @@ -24,11 +25,16 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import javax.inject.Inject; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; +import okhttp3.OkHttpClient.Builder; +import org.tasks.DebugNetworkInterceptor; import org.tasks.R; import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavCalendar; +import org.tasks.injection.ForApplication; +import org.tasks.preferences.Preferences; import org.tasks.security.Encryption; import org.tasks.ui.DisplayableException; import org.xmlpull.v1.XmlPullParserException; @@ -38,28 +44,43 @@ import timber.log.Timber; public class CaldavClient { + private final Context context; + private final Encryption encryption; + private final Preferences preferences; + private final DebugNetworkInterceptor interceptor; private final OkHttpClient httpClient; private final HttpUrl httpUrl; - public CaldavClient(CaldavAccount caldavAccount, Encryption encryption) { - this( - caldavAccount.getUrl(), - caldavAccount.getUsername(), - encryption.decrypt(caldavAccount.getPassword())); - } - + @Inject public CaldavClient( - CaldavAccount caldavAccount, CaldavCalendar caldavCalendar, Encryption encryption) { - this( - caldavCalendar.getUrl(), - caldavAccount.getUsername(), - encryption.decrypt(caldavAccount.getPassword())); + @ForApplication Context context, + Encryption encryption, + Preferences preferences, + DebugNetworkInterceptor interceptor) { + this.context = context; + this.encryption = encryption; + this.preferences = preferences; + this.interceptor = interceptor; + httpClient = null; + httpUrl = null; } - public CaldavClient(String url, String username, String password) { + private CaldavClient( + Context context, + Encryption encryption, + Preferences preferences, + DebugNetworkInterceptor interceptor, + String url, + String username, + String password) { + this.context = context; + this.encryption = encryption; + this.preferences = preferences; + this.interceptor = interceptor; + BasicDigestAuthHandler basicDigestAuthHandler = new BasicDigestAuthHandler(null, username, password); - httpClient = + Builder builder = new OkHttpClient() .newBuilder() .addNetworkInterceptor(basicDigestAuthHandler) @@ -67,11 +88,26 @@ public class CaldavClient { .cookieJar(new MemoryCookieStore()) .followRedirects(false) .followSslRedirects(true) - .readTimeout(30, TimeUnit.SECONDS) - .build(); + .readTimeout(30, TimeUnit.SECONDS); + if (preferences.isFlipperEnabled()) { + interceptor.add(builder); + } + httpClient = builder.build(); httpUrl = HttpUrl.parse(url); } + public CaldavClient forAccount(CaldavAccount account) { + return forUrl(account.getUrl(), account.getUsername(), account.getPassword(encryption)); + } + + public CaldavClient forCalendar(CaldavAccount account, CaldavCalendar calendar) { + return forUrl(calendar.getUrl(), account.getUsername(), account.getPassword(encryption)); + } + + public CaldavClient forUrl(String url, String username, String password) { + return new CaldavClient(context, encryption, preferences, interceptor, url, username, password); + } + private String tryFindPrincipal() throws DavException, IOException { for (String link : asList("", "/.well-known/caldav")) { HttpUrl url = httpUrl.resolve(link); diff --git a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java index dd90059e5..348215c0d 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java +++ b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java @@ -54,7 +54,6 @@ import org.tasks.data.CaldavCalendar; import org.tasks.data.CaldavDao; import org.tasks.data.CaldavTask; import org.tasks.injection.ForApplication; -import org.tasks.security.Encryption; import timber.log.Timber; public class CaldavSynchronizer { @@ -69,9 +68,9 @@ public class CaldavSynchronizer { private final LocalBroadcastManager localBroadcastManager; private final TaskCreator taskCreator; private final TaskDeleter taskDeleter; - private final Encryption encryption; private final Inventory inventory; private final Tracker tracker; + private final CaldavClient client; private final Context context; @Inject @@ -82,18 +81,18 @@ public class CaldavSynchronizer { LocalBroadcastManager localBroadcastManager, TaskCreator taskCreator, TaskDeleter taskDeleter, - Encryption encryption, Inventory inventory, - Tracker tracker) { + Tracker tracker, + CaldavClient client) { this.context = context; this.caldavDao = caldavDao; this.taskDao = taskDao; this.localBroadcastManager = localBroadcastManager; this.taskCreator = taskCreator; this.taskDeleter = taskDeleter; - this.encryption = encryption; this.inventory = inventory; this.tracker = tracker; + this.client = client; } public void sync() { @@ -108,7 +107,7 @@ public class CaldavSynchronizer { setError(account, context.getString(R.string.password_required)); continue; } - CaldavClient caldavClient = new CaldavClient(account, encryption); + CaldavClient caldavClient = client.forAccount(account); List resources; try { resources = caldavClient.getCalendars(); diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.java b/app/src/main/java/org/tasks/data/CaldavAccount.java index fb4e36484..536d5209f 100644 --- a/app/src/main/java/org/tasks/data/CaldavAccount.java +++ b/app/src/main/java/org/tasks/data/CaldavAccount.java @@ -8,6 +8,7 @@ import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.Ignore; import androidx.room.PrimaryKey; +import org.tasks.security.Encryption; @Entity(tableName = "caldav_account") public class CaldavAccount implements Parcelable { @@ -109,6 +110,10 @@ public class CaldavAccount implements Parcelable { this.password = password; } + public String getPassword(Encryption encryption) { + return encryption.decrypt(password); + } + public String getError() { return error; } diff --git a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java index 89eb9dda8..9aae6aa9f 100644 --- a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java +++ b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.java @@ -78,6 +78,7 @@ public class GoogleTaskSynchronizer { private final LocalBroadcastManager localBroadcastManager; private final Inventory inventory; private final TaskDeleter taskDeleter; + private final GtasksInvoker gtasksInvoker; @Inject public GoogleTaskSynchronizer( @@ -95,7 +96,8 @@ public class GoogleTaskSynchronizer { GoogleAccountManager googleAccountManager, LocalBroadcastManager localBroadcastManager, Inventory inventory, - TaskDeleter taskDeleter) { + TaskDeleter taskDeleter, + GtasksInvoker gtasksInvoker) { this.context = context; this.googleTaskListDao = googleTaskListDao; this.gtasksListService = gtasksListService; @@ -111,6 +113,7 @@ public class GoogleTaskSynchronizer { this.localBroadcastManager = localBroadcastManager; this.inventory = inventory; this.taskDeleter = taskDeleter; + this.gtasksInvoker = gtasksInvoker; } public static void mergeDates(long remoteDueDate, Task local) { @@ -182,7 +185,7 @@ public class GoogleTaskSynchronizer { return; } - GtasksInvoker gtasksInvoker = new GtasksInvoker(account.getAccount(), googleAccountManager); + GtasksInvoker gtasksInvoker = this.gtasksInvoker.forAccount(account.getAccount()); pushLocalChanges(account, gtasksInvoker); List gtaskLists = new ArrayList<>(); diff --git a/app/src/main/java/org/tasks/preferences/Preferences.java b/app/src/main/java/org/tasks/preferences/Preferences.java index f0e0a5743..b531d2d51 100644 --- a/app/src/main/java/org/tasks/preferences/Preferences.java +++ b/app/src/main/java/org/tasks/preferences/Preferences.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; +import org.tasks.BuildConfig; import org.tasks.R; import org.tasks.billing.Purchase; import org.tasks.data.TaskAttachment; @@ -506,4 +507,8 @@ public class Preferences { } return result; } + + public boolean isFlipperEnabled() { + return BuildConfig.DEBUG && getBoolean(R.string.p_flipper, false); + } } diff --git a/app/src/release/java/org/tasks/DebugNetworkInterceptor.java b/app/src/release/java/org/tasks/DebugNetworkInterceptor.java new file mode 100644 index 000000000..ae127791e --- /dev/null +++ b/app/src/release/java/org/tasks/DebugNetworkInterceptor.java @@ -0,0 +1,16 @@ +package org.tasks; + +import com.google.api.client.http.HttpRequest; +import javax.inject.Inject; +import okhttp3.OkHttpClient; + +public class DebugNetworkInterceptor { + @Inject + public DebugNetworkInterceptor() {} + + public void add(OkHttpClient.Builder builder) {} + + public T execute(HttpRequest request, Class responseClass) { + return null; + } +}