Add Flipper network interceptors

pull/820/head
Alex Baker 7 years ago
parent 4997ec3f0e
commit 31bb7b1abd

@ -9,6 +9,7 @@ import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping; import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.soloader.SoLoader; import com.facebook.soloader.SoLoader;
import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.LeakCanary;
@ -35,6 +36,7 @@ public class BuildSetup {
FlipperClient client = AndroidFlipperClient.getInstance(context); FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new DatabasesFlipperPlugin(context)); client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new NetworkFlipperPlugin());
client.addPlugin(new SharedPreferencesFlipperPlugin(context)); client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.start(); client.start();
} }

@ -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);
}
}
}

@ -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> T execute(HttpRequest request, Class<T> responseClass) throws IOException {
FlipperHttpInterceptor<T> interceptor =
new FlipperHttpInterceptor<>(getNetworkPlugin(context), responseClass);
request
.setInterceptor(new ChainedHttpExecuteInterceptor(request.getInterceptor(), interceptor))
.setResponseInterceptor(interceptor)
.execute();
return interceptor.getResponse();
}
}

@ -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<T> implements HttpExecuteInterceptor, HttpResponseInterceptor {
private final Class<T> responseClass;
private final String requestId = UUIDHelper.newUUID();
private final NetworkFlipperPlugin plugin;
private T body;
FlipperHttpInterceptor(NetworkFlipperPlugin plugin, Class<T> 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<Header> getHeaders(HttpHeaders headers) {
List<Header> result = new ArrayList<>();
for (Map.Entry<String, Object> 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();
}
}

@ -1,10 +1,11 @@
package com.todoroo.astrid.gtasks.api; package com.todoroo.astrid.gtasks.api;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; 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.HttpResponseException;
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;
@ -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.TaskList;
import com.google.api.services.tasks.model.TaskLists; import com.google.api.services.tasks.model.TaskLists;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.gson.GsonBuilder;
import java.io.IOException; import java.io.IOException;
import javax.inject.Inject;
import org.tasks.BuildConfig; import org.tasks.BuildConfig;
import org.tasks.DebugNetworkInterceptor;
import org.tasks.gtasks.GoogleAccountManager; import org.tasks.gtasks.GoogleAccountManager;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import timber.log.Timber; import timber.log.Timber;
/** /**
@ -30,20 +34,50 @@ import timber.log.Timber;
*/ */
public class GtasksInvoker { public class GtasksInvoker {
private final String account; private final Context context;
private final GoogleAccountManager googleAccountManager; private final GoogleAccountManager googleAccountManager;
private final Tasks service; private final Preferences preferences;
private final DebugNetworkInterceptor interceptor;
private final GoogleCredential credential = new GoogleCredential(); private final GoogleCredential credential = new GoogleCredential();
private final String account;
private final Tasks service;
public GtasksInvoker(String account, GoogleAccountManager googleAccountManager) { @Inject
this.account = account; 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.googleAccountManager = googleAccountManager;
this.preferences = preferences;
this.interceptor = interceptor;
this.account = account;
service = service =
new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential) new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
.setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME)) .setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME))
.build(); .build();
} }
public GtasksInvoker forAccount(String account) {
return new GtasksInvoker(context, googleAccountManager, preferences, interceptor, account);
}
private void checkToken() { private void checkToken() {
if (Strings.isNullOrEmpty(credential.getAccessToken())) { if (Strings.isNullOrEmpty(credential.getAccessToken())) {
Bundle bundle = googleAccountManager.getAccessToken(account, TasksScopes.TASKS); Bundle bundle = googleAccountManager.getAccessToken(account, TasksScopes.TASKS);
@ -115,10 +149,15 @@ public class GtasksInvoker {
private synchronized @Nullable <T> T execute(TasksRequest<T> request, boolean retry) private synchronized @Nullable <T> T execute(TasksRequest<T> request, boolean retry)
throws IOException { throws IOException {
checkToken(); checkToken();
Timber.d("%s request: %s", getCaller(retry), prettyPrint(request));
T response; T response;
try { 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) { } catch (HttpResponseException e) {
if (e.getStatusCode() == 401 && !retry) { if (e.getStatusCode() == 401 && !retry) {
googleAccountManager.invalidateToken(credential.getAccessToken()); googleAccountManager.invalidateToken(credential.getAccessToken());
@ -136,8 +175,6 @@ public class GtasksInvoker {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
if (object instanceof GenericJson) { if (object instanceof GenericJson) {
return ((GenericJson) object).toPrettyString(); return ((GenericJson) object).toPrettyString();
} else if (object instanceof AbstractGoogleClientRequest) {
return new GsonBuilder().setPrettyPrinting().create().toJson(object);
} }
} }
return object; return object;

@ -4,7 +4,7 @@ import org.tasks.caldav.CaldavClient;
import org.tasks.ui.CompletableViewModel; import org.tasks.ui.CompletableViewModel;
public class AddCaldavAccountViewModel extends CompletableViewModel<String> { public class AddCaldavAccountViewModel extends CompletableViewModel<String> {
public void addAccount(String url, String username, String password) { public void addAccount(CaldavClient client, String url, String username, String password) {
run(() -> new CaldavClient(url, username, password).getHomeSet()); run(() -> client.forUrl(url, username, password).getHomeSet());
} }
} }

@ -2,11 +2,10 @@ package org.tasks.activities;
import org.tasks.caldav.CaldavClient; import org.tasks.caldav.CaldavClient;
import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavAccount;
import org.tasks.security.Encryption;
import org.tasks.ui.CompletableViewModel; import org.tasks.ui.CompletableViewModel;
public class CreateCalendarViewModel extends CompletableViewModel<String> { public class CreateCalendarViewModel extends CompletableViewModel<String> {
public void createCalendar(CaldavAccount caldavAccount, Encryption encryption, String name) { public void createCalendar(CaldavClient client, CaldavAccount account, String name) {
run(() -> new CaldavClient(caldavAccount, encryption).makeCollection(name)); run(() -> client.forAccount(account).makeCollection(name));
} }
} }

@ -2,12 +2,11 @@ package org.tasks.activities;
import com.google.api.services.tasks.model.TaskList; import com.google.api.services.tasks.model.TaskList;
import com.todoroo.astrid.gtasks.api.GtasksInvoker; import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import org.tasks.gtasks.GoogleAccountManager;
import org.tasks.ui.CompletableViewModel; import org.tasks.ui.CompletableViewModel;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class CreateListViewModel extends CompletableViewModel<TaskList> { public class CreateListViewModel extends CompletableViewModel<TaskList> {
void createList(GoogleAccountManager googleAccountManager, String account, String name) { void createList(GtasksInvoker invoker, String account, String name) {
run(() -> new GtasksInvoker(account, googleAccountManager).createGtaskList(name)); run(() -> invoker.forAccount(account).createGtaskList(name));
} }
} }

@ -3,12 +3,10 @@ package org.tasks.activities;
import org.tasks.caldav.CaldavClient; import org.tasks.caldav.CaldavClient;
import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavCalendar; import org.tasks.data.CaldavCalendar;
import org.tasks.security.Encryption;
import org.tasks.ui.ActionViewModel; import org.tasks.ui.ActionViewModel;
public class DeleteCalendarViewModel extends ActionViewModel { public class DeleteCalendarViewModel extends ActionViewModel {
public void deleteCalendar( public void deleteCalendar(CaldavClient client, CaldavAccount account, CaldavCalendar calendar) {
CaldavAccount caldavAccount, Encryption encryption, CaldavCalendar calendar) { run(() -> client.forCalendar(account, calendar).deleteCollection());
run(() -> new CaldavClient(caldavAccount, calendar, encryption).deleteCollection());
} }
} }

@ -2,12 +2,11 @@ package org.tasks.activities;
import com.todoroo.astrid.gtasks.api.GtasksInvoker; import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskList;
import org.tasks.gtasks.GoogleAccountManager;
import org.tasks.ui.ActionViewModel; import org.tasks.ui.ActionViewModel;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class DeleteListViewModel extends ActionViewModel { public class DeleteListViewModel extends ActionViewModel {
void deleteList(GoogleAccountManager googleAccountManager, GoogleTaskList list) { void deleteList(GtasksInvoker invoker, GoogleTaskList list) {
run(() -> new GtasksInvoker(list.getAccount(), googleAccountManager).deleteGtaskList(list.getRemoteId())); run(() -> invoker.forAccount(list.getAccount()).deleteGtaskList(list.getRemoteId()));
} }
} }

@ -24,6 +24,7 @@ import com.todoroo.astrid.activity.MainActivity;
import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.GtasksFilter; import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.gtasks.GtasksListService; import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import com.todoroo.astrid.service.TaskDeleter; import com.todoroo.astrid.service.TaskDeleter;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.R; import org.tasks.R;
@ -59,7 +60,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi
@Inject ThemeCache themeCache; @Inject ThemeCache themeCache;
@Inject ThemeColor themeColor; @Inject ThemeColor themeColor;
@Inject TaskDeleter taskDeleter; @Inject TaskDeleter taskDeleter;
@Inject GoogleAccountManager googleAccountManager; @Inject GtasksInvoker gtasksInvoker;
@BindView(R.id.name) @BindView(R.id.name)
TextInputEditText name; TextInputEditText name;
@ -206,10 +207,10 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi
if (isNewList) { if (isNewList) {
showProgressIndicator(); showProgressIndicator();
createListViewModel.createList(googleAccountManager, gtasksList.getAccount(), newName); createListViewModel.createList(gtasksInvoker, gtasksList.getAccount(), newName);
} else if (nameChanged()) { } else if (nameChanged()) {
showProgressIndicator(); showProgressIndicator();
renameListViewModel.renameList(googleAccountManager, gtasksList, newName); renameListViewModel.renameList(gtasksInvoker, gtasksList, newName);
} else { } else {
if (colorChanged()) { if (colorChanged()) {
gtasksList.setColor(selectedTheme); gtasksList.setColor(selectedTheme);
@ -250,7 +251,7 @@ public class GoogleTaskListSettingsActivity extends ThemedInjectingAppCompatActi
R.string.delete, R.string.delete,
(dialog, which) -> { (dialog, which) -> {
showProgressIndicator(); showProgressIndicator();
deleteListViewModel.deleteList(googleAccountManager, gtasksList); deleteListViewModel.deleteList(gtasksInvoker, gtasksList);
}) })
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show(); .show();

@ -3,15 +3,11 @@ package org.tasks.activities;
import com.google.api.services.tasks.model.TaskList; import com.google.api.services.tasks.model.TaskList;
import com.todoroo.astrid.gtasks.api.GtasksInvoker; import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskList;
import org.tasks.gtasks.GoogleAccountManager;
import org.tasks.ui.CompletableViewModel; import org.tasks.ui.CompletableViewModel;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class RenameListViewModel extends CompletableViewModel<TaskList> { public class RenameListViewModel extends CompletableViewModel<TaskList> {
void renameList(GoogleAccountManager googleAccountManager, GoogleTaskList list, String name) { void renameList(GtasksInvoker invoker, GoogleTaskList list, String name) {
run( run(() -> invoker.forAccount(list.getAccount()).renameGtaskList(list.getRemoteId(), name));
() ->
new GtasksInvoker(list.getAccount(), googleAccountManager)
.renameGtaskList(list.getRemoteId(), name));
} }
} }

@ -4,7 +4,8 @@ import org.tasks.caldav.CaldavClient;
import org.tasks.ui.CompletableViewModel; import org.tasks.ui.CompletableViewModel;
public class UpdateCaldavAccountViewModel extends CompletableViewModel<String> { public class UpdateCaldavAccountViewModel extends CompletableViewModel<String> {
public void updateCaldavAccount(String url, String username, String password) { public void updateCaldavAccount(
run(() -> new CaldavClient(url, username, password).getHomeSet()); CaldavClient client, String url, String username, String password) {
run(() -> client.forUrl(url, username, password).getHomeSet());
} }
} }

@ -38,6 +38,7 @@ import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavDao; import org.tasks.data.CaldavDao;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ActivityComponent; import org.tasks.injection.ActivityComponent;
import org.tasks.injection.ForApplication;
import org.tasks.injection.ThemedInjectingAppCompatActivity; import org.tasks.injection.ThemedInjectingAppCompatActivity;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.security.Encryption; 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$ 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"; private static final String PASSWORD_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
@Inject @ForApplication Context context;
@Inject DialogBuilder dialogBuilder; @Inject DialogBuilder dialogBuilder;
@Inject Preferences preferences; @Inject Preferences preferences;
@Inject Tracker tracker; @Inject Tracker tracker;
@ -58,6 +60,7 @@ public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActiv
@Inject SyncAdapters syncAdapters; @Inject SyncAdapters syncAdapters;
@Inject TaskDeleter taskDeleter; @Inject TaskDeleter taskDeleter;
@Inject Encryption encryption; @Inject Encryption encryption;
@Inject CaldavClient client;
@BindView(R.id.root_layout) @BindView(R.id.root_layout)
LinearLayout root; LinearLayout root;
@ -296,10 +299,10 @@ public class CaldavAccountSettingsActivity extends ThemedInjectingAppCompatActiv
if (caldavAccount == null) { if (caldavAccount == null) {
showProgressIndicator(); showProgressIndicator();
addCaldavAccountViewModel.addAccount(url, username, password); addCaldavAccountViewModel.addAccount(client, url, username, password);
} else if (needsValidation()) { } else if (needsValidation()) {
showProgressIndicator(); showProgressIndicator();
updateCaldavAccountViewModel.updateCaldavAccount(url, username, password); updateCaldavAccountViewModel.updateCaldavAccount(client, url, username, password);
} else if (hasChanges()) { } else if (hasChanges()) {
updateAccount(caldavAccount.getUrl()); updateAccount(caldavAccount.getUrl());
} else { } else {

@ -43,9 +43,9 @@ import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao; import org.tasks.data.CaldavDao;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ActivityComponent; import org.tasks.injection.ActivityComponent;
import org.tasks.injection.ForApplication;
import org.tasks.injection.ThemedInjectingAppCompatActivity; import org.tasks.injection.ThemedInjectingAppCompatActivity;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.security.Encryption;
import org.tasks.sync.SyncAdapters; import org.tasks.sync.SyncAdapters;
import org.tasks.themes.ThemeCache; import org.tasks.themes.ThemeCache;
import org.tasks.themes.ThemeColor; import org.tasks.themes.ThemeColor;
@ -59,6 +59,7 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi
public static final String EXTRA_CALDAV_ACCOUNT = "extra_caldav_account"; public static final String EXTRA_CALDAV_ACCOUNT = "extra_caldav_account";
private static final String EXTRA_SELECTED_THEME = "extra_selected_theme"; private static final String EXTRA_SELECTED_THEME = "extra_selected_theme";
private static final int REQUEST_COLOR_PICKER = 10109; private static final int REQUEST_COLOR_PICKER = 10109;
@Inject @ForApplication Context context;
@Inject DialogBuilder dialogBuilder; @Inject DialogBuilder dialogBuilder;
@Inject Preferences preferences; @Inject Preferences preferences;
@Inject ThemeCache themeCache; @Inject ThemeCache themeCache;
@ -66,8 +67,8 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi
@Inject Tracker tracker; @Inject Tracker tracker;
@Inject CaldavDao caldavDao; @Inject CaldavDao caldavDao;
@Inject SyncAdapters syncAdapters; @Inject SyncAdapters syncAdapters;
@Inject Encryption encryption;
@Inject TaskDeleter taskDeleter; @Inject TaskDeleter taskDeleter;
@Inject CaldavClient client;
@BindView(R.id.root_layout) @BindView(R.id.root_layout)
LinearLayout root; LinearLayout root;
@ -223,7 +224,7 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi
if (caldavCalendar == null) { if (caldavCalendar == null) {
showProgressIndicator(); showProgressIndicator();
createCalendarViewModel.createCalendar(caldavAccount, encryption, name); createCalendarViewModel.createCalendar(client, caldavAccount, name);
} else if (hasChanges()) { } else if (hasChanges()) {
updateAccount(); updateAccount();
} else { } else {
@ -393,7 +394,7 @@ public class CaldavCalendarSettingsActivity extends ThemedInjectingAppCompatActi
R.string.delete, R.string.delete,
(dialog, which) -> { (dialog, which) -> {
showProgressIndicator(); showProgressIndicator();
deleteCalendarViewModel.deleteCalendar(caldavAccount, encryption, caldavCalendar); deleteCalendarViewModel.deleteCalendar(client, caldavAccount, caldavCalendar);
}) })
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show(); .show();

@ -6,6 +6,7 @@ import static at.bitfire.dav4android.XmlUtils.NS_CARDDAV;
import static at.bitfire.dav4android.XmlUtils.NS_WEBDAV; import static at.bitfire.dav4android.XmlUtils.NS_WEBDAV;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import android.content.Context;
import at.bitfire.dav4android.BasicDigestAuthHandler; import at.bitfire.dav4android.BasicDigestAuthHandler;
import at.bitfire.dav4android.DavResource; import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.DavResponse; import at.bitfire.dav4android.DavResponse;
@ -24,11 +25,16 @@ import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import org.tasks.DebugNetworkInterceptor;
import org.tasks.R; import org.tasks.R;
import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavCalendar; import org.tasks.data.CaldavCalendar;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import org.tasks.security.Encryption; import org.tasks.security.Encryption;
import org.tasks.ui.DisplayableException; import org.tasks.ui.DisplayableException;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
@ -38,28 +44,43 @@ import timber.log.Timber;
public class CaldavClient { 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 OkHttpClient httpClient;
private final HttpUrl httpUrl; private final HttpUrl httpUrl;
public CaldavClient(CaldavAccount caldavAccount, Encryption encryption) { @Inject
this(
caldavAccount.getUrl(),
caldavAccount.getUsername(),
encryption.decrypt(caldavAccount.getPassword()));
}
public CaldavClient( public CaldavClient(
CaldavAccount caldavAccount, CaldavCalendar caldavCalendar, Encryption encryption) { @ForApplication Context context,
this( Encryption encryption,
caldavCalendar.getUrl(), Preferences preferences,
caldavAccount.getUsername(), DebugNetworkInterceptor interceptor) {
encryption.decrypt(caldavAccount.getPassword())); 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 = BasicDigestAuthHandler basicDigestAuthHandler =
new BasicDigestAuthHandler(null, username, password); new BasicDigestAuthHandler(null, username, password);
httpClient = Builder builder =
new OkHttpClient() new OkHttpClient()
.newBuilder() .newBuilder()
.addNetworkInterceptor(basicDigestAuthHandler) .addNetworkInterceptor(basicDigestAuthHandler)
@ -67,11 +88,26 @@ public class CaldavClient {
.cookieJar(new MemoryCookieStore()) .cookieJar(new MemoryCookieStore())
.followRedirects(false) .followRedirects(false)
.followSslRedirects(true) .followSslRedirects(true)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS);
.build(); if (preferences.isFlipperEnabled()) {
interceptor.add(builder);
}
httpClient = builder.build();
httpUrl = HttpUrl.parse(url); 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 { private String tryFindPrincipal() throws DavException, IOException {
for (String link : asList("", "/.well-known/caldav")) { for (String link : asList("", "/.well-known/caldav")) {
HttpUrl url = httpUrl.resolve(link); HttpUrl url = httpUrl.resolve(link);

@ -54,7 +54,6 @@ import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao; import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask; import org.tasks.data.CaldavTask;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import org.tasks.security.Encryption;
import timber.log.Timber; import timber.log.Timber;
public class CaldavSynchronizer { public class CaldavSynchronizer {
@ -69,9 +68,9 @@ public class CaldavSynchronizer {
private final LocalBroadcastManager localBroadcastManager; private final LocalBroadcastManager localBroadcastManager;
private final TaskCreator taskCreator; private final TaskCreator taskCreator;
private final TaskDeleter taskDeleter; private final TaskDeleter taskDeleter;
private final Encryption encryption;
private final Inventory inventory; private final Inventory inventory;
private final Tracker tracker; private final Tracker tracker;
private final CaldavClient client;
private final Context context; private final Context context;
@Inject @Inject
@ -82,18 +81,18 @@ public class CaldavSynchronizer {
LocalBroadcastManager localBroadcastManager, LocalBroadcastManager localBroadcastManager,
TaskCreator taskCreator, TaskCreator taskCreator,
TaskDeleter taskDeleter, TaskDeleter taskDeleter,
Encryption encryption,
Inventory inventory, Inventory inventory,
Tracker tracker) { Tracker tracker,
CaldavClient client) {
this.context = context; this.context = context;
this.caldavDao = caldavDao; this.caldavDao = caldavDao;
this.taskDao = taskDao; this.taskDao = taskDao;
this.localBroadcastManager = localBroadcastManager; this.localBroadcastManager = localBroadcastManager;
this.taskCreator = taskCreator; this.taskCreator = taskCreator;
this.taskDeleter = taskDeleter; this.taskDeleter = taskDeleter;
this.encryption = encryption;
this.inventory = inventory; this.inventory = inventory;
this.tracker = tracker; this.tracker = tracker;
this.client = client;
} }
public void sync() { public void sync() {
@ -108,7 +107,7 @@ public class CaldavSynchronizer {
setError(account, context.getString(R.string.password_required)); setError(account, context.getString(R.string.password_required));
continue; continue;
} }
CaldavClient caldavClient = new CaldavClient(account, encryption); CaldavClient caldavClient = client.forAccount(account);
List<DavResponse> resources; List<DavResponse> resources;
try { try {
resources = caldavClient.getCalendars(); resources = caldavClient.getCalendars();

@ -8,6 +8,7 @@ import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.Ignore; import androidx.room.Ignore;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import org.tasks.security.Encryption;
@Entity(tableName = "caldav_account") @Entity(tableName = "caldav_account")
public class CaldavAccount implements Parcelable { public class CaldavAccount implements Parcelable {
@ -109,6 +110,10 @@ public class CaldavAccount implements Parcelable {
this.password = password; this.password = password;
} }
public String getPassword(Encryption encryption) {
return encryption.decrypt(password);
}
public String getError() { public String getError() {
return error; return error;
} }

@ -78,6 +78,7 @@ public class GoogleTaskSynchronizer {
private final LocalBroadcastManager localBroadcastManager; private final LocalBroadcastManager localBroadcastManager;
private final Inventory inventory; private final Inventory inventory;
private final TaskDeleter taskDeleter; private final TaskDeleter taskDeleter;
private final GtasksInvoker gtasksInvoker;
@Inject @Inject
public GoogleTaskSynchronizer( public GoogleTaskSynchronizer(
@ -95,7 +96,8 @@ public class GoogleTaskSynchronizer {
GoogleAccountManager googleAccountManager, GoogleAccountManager googleAccountManager,
LocalBroadcastManager localBroadcastManager, LocalBroadcastManager localBroadcastManager,
Inventory inventory, Inventory inventory,
TaskDeleter taskDeleter) { TaskDeleter taskDeleter,
GtasksInvoker gtasksInvoker) {
this.context = context; this.context = context;
this.googleTaskListDao = googleTaskListDao; this.googleTaskListDao = googleTaskListDao;
this.gtasksListService = gtasksListService; this.gtasksListService = gtasksListService;
@ -111,6 +113,7 @@ public class GoogleTaskSynchronizer {
this.localBroadcastManager = localBroadcastManager; this.localBroadcastManager = localBroadcastManager;
this.inventory = inventory; this.inventory = inventory;
this.taskDeleter = taskDeleter; this.taskDeleter = taskDeleter;
this.gtasksInvoker = gtasksInvoker;
} }
public static void mergeDates(long remoteDueDate, Task local) { public static void mergeDates(long remoteDueDate, Task local) {
@ -182,7 +185,7 @@ public class GoogleTaskSynchronizer {
return; return;
} }
GtasksInvoker gtasksInvoker = new GtasksInvoker(account.getAccount(), googleAccountManager); GtasksInvoker gtasksInvoker = this.gtasksInvoker.forAccount(account.getAccount());
pushLocalChanges(account, gtasksInvoker); pushLocalChanges(account, gtasksInvoker);
List<TaskList> gtaskLists = new ArrayList<>(); List<TaskList> gtaskLists = new ArrayList<>();

@ -32,6 +32,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.tasks.BuildConfig;
import org.tasks.R; import org.tasks.R;
import org.tasks.billing.Purchase; import org.tasks.billing.Purchase;
import org.tasks.data.TaskAttachment; import org.tasks.data.TaskAttachment;
@ -506,4 +507,8 @@ public class Preferences {
} }
return result; return result;
} }
public boolean isFlipperEnabled() {
return BuildConfig.DEBUG && getBoolean(R.string.p_flipper, false);
}
} }

@ -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> T execute(HttpRequest request, Class<T> responseClass) {
return null;
}
}
Loading…
Cancel
Save