diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index fc9aca623..465ecd4b8 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -12,6 +12,7 @@ repositories {
jcenter()
google()
maven(url = "https://jitpack.io")
+ maven(url = "https://oss.sonatype.org/content/repositories/snapshots")
}
android {
@@ -200,6 +201,7 @@ dependencies {
implementation("androidx.work:work-runtime:${Versions.work}")
implementation("com.mapbox.mapboxsdk:mapbox-android-sdk:7.3.0")
implementation("com.mapbox.mapboxsdk:mapbox-sdk-services:4.6.0")
+ implementation("com.etesync:journalmanager:1.0.2-SNAPSHOT")
googleplayImplementation("com.crashlytics.sdk.android:crashlytics:${Versions.crashlytics}")
googleplayImplementation("com.google.firebase:firebase-analytics:${Versions.firebase}")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 078b736da..110ffb73b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -326,6 +326,10 @@
android:name=".caldav.CaldavAccountSettingsActivity"
android:theme="@style/Tasks"/>
+
+
diff --git a/app/src/main/java/org/tasks/activities/AddEteSyncAccountViewModel.java b/app/src/main/java/org/tasks/activities/AddEteSyncAccountViewModel.java
new file mode 100644
index 000000000..46e2b925e
--- /dev/null
+++ b/app/src/main/java/org/tasks/activities/AddEteSyncAccountViewModel.java
@@ -0,0 +1,29 @@
+package org.tasks.activities;
+
+import android.content.Context;
+import androidx.core.util.Pair;
+import com.etesync.journalmanager.Crypto.AsymmetricKeyPair;
+import org.tasks.etesync.EteSyncClient;
+import org.tasks.gtasks.PlayServices;
+import org.tasks.ui.CompletableViewModel;
+
+public class AddEteSyncAccountViewModel
+ extends CompletableViewModel> {
+ public void addAccount(
+ PlayServices playServices,
+ Context context,
+ EteSyncClient client,
+ String url,
+ String username,
+ String password,
+ String encryptionPassword) {
+ run(
+ () -> {
+ playServices.updateSecurityProvider(context);
+ return client
+ .setForeground()
+ .forUrl(url, username, encryptionPassword, null)
+ .getKeyAndToken(password);
+ });
+ }
+}
diff --git a/app/src/main/java/org/tasks/activities/UpdateEteSyncAccountViewModel.java b/app/src/main/java/org/tasks/activities/UpdateEteSyncAccountViewModel.java
new file mode 100644
index 000000000..0351c3a23
--- /dev/null
+++ b/app/src/main/java/org/tasks/activities/UpdateEteSyncAccountViewModel.java
@@ -0,0 +1,23 @@
+package org.tasks.activities;
+
+import android.content.Context;
+import androidx.core.util.Pair;
+import org.tasks.etesync.EteSyncClient;
+import org.tasks.ui.CompletableViewModel;
+
+public class UpdateEteSyncAccountViewModel extends CompletableViewModel> {
+ public void addAccount(
+ Context context,
+ EteSyncClient client,
+ String url,
+ String username,
+ String password,
+ String encryptionPassword) {
+ run(
+ () ->
+ client
+ .setForeground()
+ .forUrl(url, username, encryptionPassword, null)
+ .getKeyAndToken(password));
+ }
+}
diff --git a/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.java b/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.java
index 279807d10..18d6ced3c 100644
--- a/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.java
+++ b/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.java
@@ -30,6 +30,7 @@ import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavDao;
import org.tasks.databinding.ActivityCaldavAccountSettingsBinding;
import org.tasks.dialogs.DialogBuilder;
+import org.tasks.etesync.EteSyncAccountSettingsActivity;
import org.tasks.injection.ThemedInjectingAppCompatActivity;
import org.tasks.preferences.Preferences;
import org.tasks.security.Encryption;
@@ -41,7 +42,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
implements Toolbar.OnMenuItemClickListener {
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";
+ protected static final String PASSWORD_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
@Inject protected Tracker tracker;
@Inject protected CaldavDao caldavDao;
@Inject protected Encryption encryption;
@@ -71,6 +72,9 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
if (!isEmpty(caldavAccount.getPassword())) {
binding.password.setText(PASSWORD_MASK);
}
+ if (!isEmpty(caldavAccount.getEncryptionKey())) {
+ binding.encryptionPassword.setText(PASSWORD_MASK);
+ }
binding.repeat.setChecked(caldavAccount.isSuppressRepeatingTasks());
}
}
@@ -168,7 +172,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
return binding.user.getText().toString().trim();
}
- private boolean passwordChanged() {
+ protected boolean passwordChanged() {
return caldavAccount == null || !PASSWORD_MASK.equals(binding.password.getText().toString().trim());
}
@@ -177,6 +181,13 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
return PASSWORD_MASK.equals(input) ? encryption.decrypt(caldavAccount.getPassword()) : input;
}
+ protected String getNewEncryptionPassword() {
+ String input = binding.encryptionPassword.getText().toString().trim();
+ return PASSWORD_MASK.equals(input)
+ ? null
+ : input;
+ }
+
private void save() {
if (requestInProgress()) {
return;
@@ -186,6 +197,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
String username = getNewUsername();
String url = getNewURL();
String password = getNewPassword();
+ String encryptionPassword = getNewEncryptionPassword();
boolean failed = false;
@@ -242,6 +254,13 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
failed = true;
}
+ if (this instanceof EteSyncAccountSettingsActivity) {
+ if (caldavAccount == null && isEmpty(encryptionPassword)) {
+ binding.encryptionPassword.setError(getString(R.string.password_required));
+ failed = true;
+ }
+ }
+
if (failed) {
return;
}
@@ -253,7 +272,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
showProgressIndicator();
updateAccount(url, username, password);
} else if (hasChanges()) {
- updateAccount(caldavAccount.getUrl());
+ updateAccount();
} else {
finish();
}
@@ -263,20 +282,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
protected abstract void updateAccount(String url, String username, String password);
- protected void updateAccount(String principal) {
- caldavAccount.setName(getNewName());
- caldavAccount.setUrl(principal);
- caldavAccount.setUsername(getNewUsername());
- caldavAccount.setError("");
- if (passwordChanged()) {
- caldavAccount.setPassword(encryption.encrypt(getNewPassword()));
- }
- caldavAccount.setSuppressRepeatingTasks(binding.repeat.isChecked());
- caldavDao.update(caldavAccount);
-
- setResult(RESULT_OK);
- finish();
- }
+ protected abstract void updateAccount();
protected void requestFailed(Throwable t) {
hideProgressIndicator();
@@ -321,7 +327,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
|| binding.repeat.isChecked() != caldavAccount.isSuppressRepeatingTasks();
}
- private boolean needsValidation() {
+ protected boolean needsValidation() {
return !getNewURL().equals(caldavAccount.getUrl())
|| !getNewUsername().equals(caldavAccount.getUsername())
|| passwordChanged();
diff --git a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java
index 91fdd7663..0ff60643a 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java
+++ b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.java
@@ -54,6 +54,21 @@ public class CaldavAccountSettingsActivity extends BaseCaldavAccountSettingsActi
finish();
}
+ protected void updateAccount(String principal) {
+ caldavAccount.setName(getNewName());
+ caldavAccount.setUrl(principal);
+ caldavAccount.setUsername(getNewUsername());
+ caldavAccount.setError("");
+ if (passwordChanged()) {
+ caldavAccount.setPassword(encryption.encrypt(getNewPassword()));
+ }
+ caldavAccount.setSuppressRepeatingTasks(binding.repeat.isChecked());
+ caldavDao.update(caldavAccount);
+
+ setResult(RESULT_OK);
+ finish();
+ }
+
@Override
protected void addAccount(String url, String username, String password) {
addCaldavAccountViewModel.addAccount(playServices, context, client, url, username, password);
@@ -64,6 +79,11 @@ public class CaldavAccountSettingsActivity extends BaseCaldavAccountSettingsActi
updateCaldavAccountViewModel.updateCaldavAccount(client, url, username, password);
}
+ @Override
+ protected void updateAccount() {
+ updateAccount(caldavAccount.getUrl());
+ }
+
@Override
public void inject(ActivityComponent component) {
component.inject(this);
diff --git a/app/src/main/java/org/tasks/caldav/MemoryCookieStore.java b/app/src/main/java/org/tasks/caldav/MemoryCookieStore.java
index e79c6588b..fd600acac 100644
--- a/app/src/main/java/org/tasks/caldav/MemoryCookieStore.java
+++ b/app/src/main/java/org/tasks/caldav/MemoryCookieStore.java
@@ -23,7 +23,7 @@ import org.apache.commons.collections4.map.MultiKeyMap;
* session cookies.
*/
@SuppressWarnings("all")
-class MemoryCookieStore implements CookieJar {
+public class MemoryCookieStore implements CookieJar {
/**
* Stored cookies. The multi-key consists of three parts: name, domain, and path. This ensures
diff --git a/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.java b/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.java
new file mode 100644
index 000000000..cbcd99f4d
--- /dev/null
+++ b/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.java
@@ -0,0 +1,126 @@
+package org.tasks.etesync;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.util.Pair;
+import androidx.lifecycle.ViewModelProviders;
+import butterknife.OnFocusChange;
+import butterknife.OnTextChanged;
+import com.todoroo.astrid.helper.UUIDHelper;
+import javax.inject.Inject;
+import org.tasks.R;
+import org.tasks.activities.AddEteSyncAccountViewModel;
+import org.tasks.activities.UpdateEteSyncAccountViewModel;
+import org.tasks.analytics.Tracking.Events;
+import org.tasks.caldav.BaseCaldavAccountSettingsActivity;
+import org.tasks.data.CaldavAccount;
+import org.tasks.gtasks.PlayServices;
+import org.tasks.injection.ActivityComponent;
+import org.tasks.injection.ForApplication;
+
+public class EteSyncAccountSettingsActivity extends BaseCaldavAccountSettingsActivity
+ implements Toolbar.OnMenuItemClickListener {
+
+ @Inject @ForApplication Context context;
+ @Inject PlayServices playServices;
+ @Inject EteSyncClient eteSyncClient;
+
+ private AddEteSyncAccountViewModel addAccountViewModel;
+ private UpdateEteSyncAccountViewModel updateAccountViewModel;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ binding.repeat.setVisibility(View.GONE);
+ binding.encryptionPasswordLayout.setVisibility(View.VISIBLE);
+
+ addAccountViewModel = ViewModelProviders.of(this).get(AddEteSyncAccountViewModel.class);
+ updateAccountViewModel = ViewModelProviders.of(this).get(UpdateEteSyncAccountViewModel.class);
+
+ if (savedInstanceState == null) {
+ if (caldavAccount == null) {
+ binding.url.setText(R.string.etesync_url);
+ }
+ }
+
+ addAccountViewModel.observe(this, this::addAccount, this::requestFailed);
+ updateAccountViewModel.observe(this, this::updateAccount, this::requestFailed);
+ }
+
+ private void addAccount(Pair authentication) {
+ CaldavAccount newAccount = new CaldavAccount();
+ newAccount.setAccountType(CaldavAccount.TYPE_ETESYNC);
+ newAccount.setUuid(UUIDHelper.newUUID());
+ applyTo(newAccount, authentication);
+ newAccount.setId(caldavDao.insert(newAccount));
+
+ tracker.reportEvent(Events.CALDAV_ACCOUNT_ADDED);
+
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ private void updateAccount(Pair authentication) {
+ applyTo(caldavAccount, authentication);
+ caldavAccount.setError("");
+ caldavDao.update(caldavAccount);
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ private void applyTo(CaldavAccount account, @Nullable Pair authentication) {
+ account.setName(getNewName());
+ account.setUrl(getNewURL());
+ account.setUsername(getNewUsername());
+ if (authentication != null) {
+ account.setPassword(encryption.encrypt(authentication.first));
+ account.setEncryptionKey(encryption.encrypt(authentication.second));
+ }
+ }
+
+ @Override
+ protected boolean needsValidation() {
+ return super.needsValidation() || encryptionPasswordChanged();
+ }
+
+ protected boolean encryptionPasswordChanged() {
+ return caldavAccount == null
+ || !PASSWORD_MASK.equals(binding.encryptionPassword.getText().toString().trim());
+ }
+
+ @Override
+ protected void addAccount(String url, String username, String password) {
+ addAccountViewModel.addAccount(
+ playServices, context, eteSyncClient, url, username, password, getNewEncryptionPassword());
+ }
+
+ @Override
+ protected void updateAccount(String url, String username, String password) {
+ updateAccountViewModel.addAccount(
+ context, eteSyncClient, url, username, password, getNewEncryptionPassword());
+ }
+
+ @Override
+ protected void updateAccount() {
+ updateAccount(null);
+ }
+
+ @Override
+ public void inject(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @OnTextChanged(R.id.encryption_password)
+ void onEncryptionPasswordChanged(CharSequence text) {
+ binding.encryptionPasswordLayout.setError(null);
+ }
+
+ @OnFocusChange(R.id.encryption_password)
+ void onEncryptionPasswordFocused(boolean hasFocus) {
+ changePasswordFocus(binding.encryptionPassword, hasFocus);
+ }
+}
diff --git a/app/src/main/java/org/tasks/etesync/EteSyncClient.java b/app/src/main/java/org/tasks/etesync/EteSyncClient.java
new file mode 100644
index 000000000..5c47957a3
--- /dev/null
+++ b/app/src/main/java/org/tasks/etesync/EteSyncClient.java
@@ -0,0 +1,173 @@
+package org.tasks.etesync;
+
+import android.content.Context;
+import androidx.annotation.Nullable;
+import androidx.core.util.Pair;
+import at.bitfire.cert4android.CustomCertManager;
+import at.bitfire.cert4android.CustomCertManager.CustomHostnameVerifier;
+import com.etesync.journalmanager.Crypto;
+import com.etesync.journalmanager.Crypto.CryptoManager;
+import com.etesync.journalmanager.Exceptions;
+import com.etesync.journalmanager.Exceptions.IntegrityException;
+import com.etesync.journalmanager.Exceptions.VersionTooNewException;
+import com.etesync.journalmanager.JournalAuthenticator;
+import com.etesync.journalmanager.JournalManager;
+import com.etesync.journalmanager.JournalManager.Journal;
+import com.etesync.journalmanager.UserInfoManager;
+import com.etesync.journalmanager.UserInfoManager.UserInfo;
+import com.etesync.journalmanager.model.CollectionInfo;
+import com.etesync.journalmanager.util.TokenAuthenticator;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.OkHttpClient.Builder;
+import okhttp3.internal.tls.OkHostnameVerifier;
+import org.tasks.DebugNetworkInterceptor;
+import org.tasks.caldav.MemoryCookieStore;
+import org.tasks.injection.ForApplication;
+import org.tasks.preferences.Preferences;
+import org.tasks.security.Encryption;
+import timber.log.Timber;
+
+public class EteSyncClient {
+
+ private final Encryption encryption;
+ private final Preferences preferences;
+ private final DebugNetworkInterceptor interceptor;
+ private final String username;
+ private final String encryptionPassword;
+ private final OkHttpClient httpClient;
+ private final HttpUrl httpUrl;
+ private final Context context;
+ private final JournalManager journalManager;
+ private boolean foreground;
+
+ @Inject
+ public EteSyncClient(
+ @ForApplication Context context,
+ Encryption encryption,
+ Preferences preferences,
+ DebugNetworkInterceptor interceptor) {
+ this.context = context;
+ this.encryption = encryption;
+ this.preferences = preferences;
+ this.interceptor = interceptor;
+ username = null;
+ encryptionPassword = null;
+ httpClient = null;
+ httpUrl = null;
+ journalManager = null;
+ }
+
+ private EteSyncClient(
+ Context context,
+ Encryption encryption,
+ Preferences preferences,
+ DebugNetworkInterceptor interceptor,
+ String url,
+ String username,
+ String encryptionPassword,
+ String token,
+ boolean foreground)
+ throws NoSuchAlgorithmException, KeyManagementException {
+ this.context = context;
+ this.encryption = encryption;
+ this.preferences = preferences;
+ this.interceptor = interceptor;
+ this.username = username;
+ this.encryptionPassword = encryptionPassword;
+
+ CustomCertManager customCertManager = new CustomCertManager(context);
+ customCertManager.setAppInForeground(foreground);
+ CustomHostnameVerifier hostnameVerifier =
+ customCertManager.hostnameVerifier(OkHostnameVerifier.INSTANCE);
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, new TrustManager[] {customCertManager}, null);
+
+ Builder builder =
+ new OkHttpClient()
+ .newBuilder()
+ .addNetworkInterceptor(new TokenAuthenticator(null, token))
+ .cookieJar(new MemoryCookieStore())
+ .followRedirects(false)
+ .followSslRedirects(true)
+ .sslSocketFactory(sslContext.getSocketFactory(), customCertManager)
+ .hostnameVerifier(hostnameVerifier)
+ .readTimeout(30, TimeUnit.SECONDS);
+ if (preferences.isFlipperEnabled()) {
+ interceptor.add(builder);
+ }
+ httpClient = builder.build();
+ httpUrl = HttpUrl.parse(url);
+ journalManager = new JournalManager(httpClient, httpUrl);
+ }
+
+ public EteSyncClient forUrl(String url, String username, String encryptionPassword, String token)
+ throws KeyManagementException, NoSuchAlgorithmException {
+ return new EteSyncClient(
+ context,
+ encryption,
+ preferences,
+ interceptor,
+ url,
+ username,
+ encryptionPassword,
+ token,
+ foreground);
+ }
+
+ public Pair getKeyAndToken(String password)
+ throws IOException, Exceptions.HttpException, VersionTooNewException, IntegrityException {
+ JournalAuthenticator journalAuthenticator = new JournalAuthenticator(httpClient, httpUrl);
+ String token = journalAuthenticator.getAuthToken(username, password);
+ UserInfoManager userInfoManager = new UserInfoManager(httpClient, httpUrl);
+ UserInfo userInfo = userInfoManager.fetch(username);
+ String key = Crypto.deriveKey(username, encryptionPassword);
+ CryptoManager cryptoManager = new CryptoManager(userInfo.getVersion(), key, "userInfo");
+ userInfo.verify(cryptoManager);
+ return Pair.create(token, key);
+ }
+
+ public CryptoManager getCrypto(Journal journal)
+ throws VersionTooNewException, IntegrityException {
+ return new CryptoManager(journal.getVersion(), encryptionPassword, journal.getUid());
+ }
+
+ private @Nullable CollectionInfo convertJournalToCollection(Journal journal) {
+ try {
+ CryptoManager cryptoManager = getCrypto(journal);
+ journal.verify(cryptoManager);
+ CollectionInfo collection =
+ CollectionInfo.Companion.fromJson(journal.getContent(cryptoManager));
+ collection.updateFromJournal(journal);
+ return collection;
+ } catch (IntegrityException | VersionTooNewException e) {
+ Timber.e(e);
+ return null;
+ }
+ }
+
+ public Map getCalendars() throws Exceptions.HttpException {
+ Map result = new HashMap<>();
+ for (Journal journal : journalManager.list()) {
+ CollectionInfo collection = convertJournalToCollection(journal);
+ if (collection != null && collection.getType().equals("TASKS")) {
+ result.put(journal, collection);
+ }
+ }
+ return result;
+ }
+
+ public EteSyncClient setForeground() {
+ foreground = true;
+ return this;
+ }
+}
diff --git a/app/src/main/java/org/tasks/injection/ActivityComponent.java b/app/src/main/java/org/tasks/injection/ActivityComponent.java
index bb7bf52e5..2cf9d73b3 100644
--- a/app/src/main/java/org/tasks/injection/ActivityComponent.java
+++ b/app/src/main/java/org/tasks/injection/ActivityComponent.java
@@ -19,6 +19,7 @@ import org.tasks.activities.DatePickerActivity;
import org.tasks.activities.FilterSelectionActivity;
import org.tasks.activities.FilterSettingsActivity;
import org.tasks.activities.GoogleTaskListSettingsActivity;
+import org.tasks.etesync.EteSyncAccountSettingsActivity;
import org.tasks.tags.TagPickerActivity;
import org.tasks.activities.TagSettingsActivity;
import org.tasks.activities.TimePickerActivity;
@@ -134,6 +135,8 @@ public interface ActivityComponent {
void inject(CaldavAccountSettingsActivity caldavAccountSettingsActivity);
+ void inject(EteSyncAccountSettingsActivity eteSyncAccountSettingsActivity);
+
void inject(DriveLoginActivity driveLoginActivity);
void inject(DebugPreferences debugPreferences);
diff --git a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java
index 632fc8fa0..42cad66fd 100644
--- a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java
+++ b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java
@@ -17,6 +17,7 @@ import android.preference.Preference;
import android.preference.PreferenceCategory;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
+import com.google.common.base.Strings;
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity;
import com.todoroo.astrid.service.TaskDeleter;
import javax.inject.Inject;
@@ -26,6 +27,7 @@ import org.tasks.analytics.Tracking;
import org.tasks.billing.Inventory;
import org.tasks.billing.PurchaseActivity;
import org.tasks.caldav.CaldavAccountSettingsActivity;
+import org.tasks.etesync.EteSyncAccountSettingsActivity;
import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskAccount;
@@ -48,6 +50,8 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
private static final int REQUEST_CALDAV_SETTINGS = 101;
private static final int REQUEST_CALDAV_SUBSCRIBE = 102;
private static final int REQUEST_GOOGLE_TASKS_SUBSCRIBE = 103;
+ private static final int REQUEST_ETESYNC_SETTINGS = 104;
+ private static final int REQUEST_ETESYNC_SUBSCRIBE = 105;
@Inject ActivityPermissionRequestor permissionRequestor;
@Inject PermissionChecker permissionChecker;
@@ -130,6 +134,23 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
return false;
});
}
+
+ Preference addEteSyncAccount = findPreference(R.string.p_add_etesync_account);
+ if (inventory.hasPro()) {
+ addEteSyncAccount.setOnPreferenceClickListener(
+ preference -> {
+ addEteSyncAccount();
+ return false;
+ });
+ } else {
+ addEteSyncAccount.setSummary(R.string.requires_pro_subscription);
+ addEteSyncAccount.setOnPreferenceClickListener(
+ preference -> {
+ startActivityForResult(
+ new Intent(this, PurchaseActivity.class), REQUEST_ETESYNC_SUBSCRIBE);
+ return false;
+ });
+ }
}
private void logoutConfirmation(GoogleTaskAccount account) {
@@ -201,19 +222,34 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
private void addCaldavAccounts() {
PreferenceCategory caldavPreferences =
(PreferenceCategory) findPreference(getString(R.string.CalDAV));
+ PreferenceCategory eteSyncPreferences =
+ (PreferenceCategory) findPreference(R.string.etesync);
caldavPreferences.removeAll();
+ eteSyncPreferences.removeAll();
for (CaldavAccount caldavAccount : caldavDao.getAccounts()) {
Preference accountPreferences = new Preference(this);
accountPreferences.setTitle(caldavAccount.getName());
accountPreferences.setSummary(caldavAccount.getError());
- accountPreferences.setOnPreferenceClickListener(
- preference -> {
- Intent intent = new Intent(this, CaldavAccountSettingsActivity.class);
- intent.putExtra(CaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, caldavAccount);
- startActivityForResult(intent, REQUEST_CALDAV_SETTINGS);
- return false;
- });
- caldavPreferences.addPreference(accountPreferences);
+ if (caldavAccount.isCaldavAccount()) {
+ accountPreferences.setOnPreferenceClickListener(
+ preference -> {
+ Intent intent = new Intent(this, CaldavAccountSettingsActivity.class);
+ intent.putExtra(CaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, caldavAccount);
+ startActivityForResult(intent, REQUEST_CALDAV_SETTINGS);
+ return false;
+ });
+ caldavPreferences.addPreference(accountPreferences);
+ } else if (caldavAccount.isEteSyncAccount()) {
+ accountPreferences.setOnPreferenceClickListener(
+ preference -> {
+ Intent intent = new Intent(this, EteSyncAccountSettingsActivity.class);
+ intent.putExtra(CaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, caldavAccount);
+ startActivityForResult(intent, REQUEST_ETESYNC_SETTINGS);
+ return false;
+ }
+ );
+ eteSyncPreferences.addPreference(accountPreferences);
+ }
}
}
@@ -222,6 +258,11 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
new Intent(this, CaldavAccountSettingsActivity.class), REQUEST_CALDAV_SETTINGS);
}
+ private void addEteSyncAccount() {
+ startActivityForResult(
+ new Intent(this, EteSyncAccountSettingsActivity.class), REQUEST_ETESYNC_SETTINGS);
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_LOGIN) {
@@ -233,7 +274,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
} else if (data != null) {
toaster.longToast(data.getStringExtra(GtasksLoginActivity.EXTRA_ERROR));
}
- } else if (requestCode == REQUEST_CALDAV_SETTINGS) {
+ } else if (requestCode == REQUEST_CALDAV_SETTINGS || requestCode == REQUEST_ETESYNC_SETTINGS) {
if (resultCode == RESULT_OK) {
workManager.updateBackgroundSync();
restart();
@@ -246,6 +287,10 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
if (inventory.hasPro()) {
requestLogin();
}
+ } else if (requestCode == REQUEST_ETESYNC_SUBSCRIBE) {
+ if (inventory.hasPro()) {
+ addEteSyncAccount();
+ }
} else {
super.onActivityResult(requestCode, resultCode, data);
}
diff --git a/app/src/main/res/layout/activity_caldav_account_settings.xml b/app/src/main/res/layout/activity_caldav_account_settings.xml
index a578fd940..7a6459d6e 100644
--- a/app/src/main/res/layout/activity_caldav_account_settings.xml
+++ b/app/src/main/res/layout/activity_caldav_account_settings.xml
@@ -80,6 +80,22 @@
+
+
+
+
+
+
Tasks Shortcut
+ EteSync
+ https://api.etesync.com
date_shortcut_morning
date_shortcut_afternoon
@@ -303,6 +305,7 @@
preference_screen
add_google_task_account
add_caldav_account
+ add_etesync_account
google_tasks_add_to_top
google_tasks_position_hack
Custom order synchronization fix
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 122843ddf..b3853ef7f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -378,6 +378,7 @@ File %1$s contained %2$s.\n\n
Name cannot be empty
Username required
Password required
+ Encryption password required
URL required
Host name required
Must begin with http(s)://
@@ -447,6 +448,7 @@ File %1$s contained %2$s.\n\n
Add account
User
Password
+ Encryption password
URL
Error: %s
Calendar settings
diff --git a/app/src/main/res/xml/preferences_synchronization.xml b/app/src/main/res/xml/preferences_synchronization.xml
index ffbc0290d..436388f44 100644
--- a/app/src/main/res/xml/preferences_synchronization.xml
+++ b/app/src/main/res/xml/preferences_synchronization.xml
@@ -31,6 +31,14 @@
android:key="@string/p_add_caldav_account"
android:title="@string/add_account"/>
+
+
+
+