Add EteSyncAccountSettingsActivity

pull/898/head
Alex Baker 4 years ago
parent f97df9e884
commit 744a673c7b

@ -12,6 +12,7 @@ repositories {
jcenter() jcenter()
google() google()
maven(url = "https://jitpack.io") maven(url = "https://jitpack.io")
maven(url = "https://oss.sonatype.org/content/repositories/snapshots")
} }
android { android {
@ -200,6 +201,7 @@ dependencies {
implementation("androidx.work:work-runtime:${Versions.work}") implementation("androidx.work:work-runtime:${Versions.work}")
implementation("com.mapbox.mapboxsdk:mapbox-android-sdk:7.3.0") implementation("com.mapbox.mapboxsdk:mapbox-android-sdk:7.3.0")
implementation("com.mapbox.mapboxsdk:mapbox-sdk-services:4.6.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.crashlytics.sdk.android:crashlytics:${Versions.crashlytics}")
googleplayImplementation("com.google.firebase:firebase-analytics:${Versions.firebase}") googleplayImplementation("com.google.firebase:firebase-analytics:${Versions.firebase}")

@ -326,6 +326,10 @@
android:name=".caldav.CaldavAccountSettingsActivity" android:name=".caldav.CaldavAccountSettingsActivity"
android:theme="@style/Tasks"/> android:theme="@style/Tasks"/>
<activity
android:name=".etesync.EteSyncAccountSettingsActivity"
android:theme="@style/Tasks" />
<activity <activity
android:name=".caldav.CaldavCalendarSettingsActivity" android:name=".caldav.CaldavCalendarSettingsActivity"
android:theme="@style/Tasks"/> android:theme="@style/Tasks"/>

@ -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<Pair<String, String>> {
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);
});
}
}

@ -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<Pair<String, String>> {
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));
}
}

@ -30,6 +30,7 @@ import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavDao; import org.tasks.data.CaldavDao;
import org.tasks.databinding.ActivityCaldavAccountSettingsBinding; import org.tasks.databinding.ActivityCaldavAccountSettingsBinding;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.etesync.EteSyncAccountSettingsActivity;
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;
@ -41,7 +42,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
implements Toolbar.OnMenuItemClickListener { implements Toolbar.OnMenuItemClickListener {
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"; protected static final String PASSWORD_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
@Inject protected Tracker tracker; @Inject protected Tracker tracker;
@Inject protected CaldavDao caldavDao; @Inject protected CaldavDao caldavDao;
@Inject protected Encryption encryption; @Inject protected Encryption encryption;
@ -71,6 +72,9 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
if (!isEmpty(caldavAccount.getPassword())) { if (!isEmpty(caldavAccount.getPassword())) {
binding.password.setText(PASSWORD_MASK); binding.password.setText(PASSWORD_MASK);
} }
if (!isEmpty(caldavAccount.getEncryptionKey())) {
binding.encryptionPassword.setText(PASSWORD_MASK);
}
binding.repeat.setChecked(caldavAccount.isSuppressRepeatingTasks()); binding.repeat.setChecked(caldavAccount.isSuppressRepeatingTasks());
} }
} }
@ -168,7 +172,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
return binding.user.getText().toString().trim(); return binding.user.getText().toString().trim();
} }
private boolean passwordChanged() { protected boolean passwordChanged() {
return caldavAccount == null || !PASSWORD_MASK.equals(binding.password.getText().toString().trim()); 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; 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() { private void save() {
if (requestInProgress()) { if (requestInProgress()) {
return; return;
@ -186,6 +197,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
String username = getNewUsername(); String username = getNewUsername();
String url = getNewURL(); String url = getNewURL();
String password = getNewPassword(); String password = getNewPassword();
String encryptionPassword = getNewEncryptionPassword();
boolean failed = false; boolean failed = false;
@ -242,6 +254,13 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
failed = true; failed = true;
} }
if (this instanceof EteSyncAccountSettingsActivity) {
if (caldavAccount == null && isEmpty(encryptionPassword)) {
binding.encryptionPassword.setError(getString(R.string.password_required));
failed = true;
}
}
if (failed) { if (failed) {
return; return;
} }
@ -253,7 +272,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
showProgressIndicator(); showProgressIndicator();
updateAccount(url, username, password); updateAccount(url, username, password);
} else if (hasChanges()) { } else if (hasChanges()) {
updateAccount(caldavAccount.getUrl()); updateAccount();
} else { } else {
finish(); finish();
} }
@ -263,20 +282,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
protected abstract void updateAccount(String url, String username, String password); protected abstract void updateAccount(String url, String username, String password);
protected void updateAccount(String principal) { protected abstract void updateAccount();
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 void requestFailed(Throwable t) { protected void requestFailed(Throwable t) {
hideProgressIndicator(); hideProgressIndicator();
@ -321,7 +327,7 @@ public abstract class BaseCaldavAccountSettingsActivity extends ThemedInjectingA
|| binding.repeat.isChecked() != caldavAccount.isSuppressRepeatingTasks(); || binding.repeat.isChecked() != caldavAccount.isSuppressRepeatingTasks();
} }
private boolean needsValidation() { protected boolean needsValidation() {
return !getNewURL().equals(caldavAccount.getUrl()) return !getNewURL().equals(caldavAccount.getUrl())
|| !getNewUsername().equals(caldavAccount.getUsername()) || !getNewUsername().equals(caldavAccount.getUsername())
|| passwordChanged(); || passwordChanged();

@ -54,6 +54,21 @@ public class CaldavAccountSettingsActivity extends BaseCaldavAccountSettingsActi
finish(); 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 @Override
protected void addAccount(String url, String username, String password) { protected void addAccount(String url, String username, String password) {
addCaldavAccountViewModel.addAccount(playServices, context, client, url, username, password); addCaldavAccountViewModel.addAccount(playServices, context, client, url, username, password);
@ -64,6 +79,11 @@ public class CaldavAccountSettingsActivity extends BaseCaldavAccountSettingsActi
updateCaldavAccountViewModel.updateCaldavAccount(client, url, username, password); updateCaldavAccountViewModel.updateCaldavAccount(client, url, username, password);
} }
@Override
protected void updateAccount() {
updateAccount(caldavAccount.getUrl());
}
@Override @Override
public void inject(ActivityComponent component) { public void inject(ActivityComponent component) {
component.inject(this); component.inject(this);

@ -23,7 +23,7 @@ import org.apache.commons.collections4.map.MultiKeyMap;
* session cookies. * session cookies.
*/ */
@SuppressWarnings("all") @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 * Stored cookies. The multi-key consists of three parts: name, domain, and path. This ensures

@ -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<String, String> 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<String, String> authentication) {
applyTo(caldavAccount, authentication);
caldavAccount.setError("");
caldavDao.update(caldavAccount);
setResult(RESULT_OK);
finish();
}
private void applyTo(CaldavAccount account, @Nullable Pair<String, String> 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);
}
}

@ -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<String, String> 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<Journal, CollectionInfo> getCalendars() throws Exceptions.HttpException {
Map<Journal, CollectionInfo> 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;
}
}

@ -19,6 +19,7 @@ import org.tasks.activities.DatePickerActivity;
import org.tasks.activities.FilterSelectionActivity; import org.tasks.activities.FilterSelectionActivity;
import org.tasks.activities.FilterSettingsActivity; import org.tasks.activities.FilterSettingsActivity;
import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.GoogleTaskListSettingsActivity;
import org.tasks.etesync.EteSyncAccountSettingsActivity;
import org.tasks.tags.TagPickerActivity; import org.tasks.tags.TagPickerActivity;
import org.tasks.activities.TagSettingsActivity; import org.tasks.activities.TagSettingsActivity;
import org.tasks.activities.TimePickerActivity; import org.tasks.activities.TimePickerActivity;
@ -134,6 +135,8 @@ public interface ActivityComponent {
void inject(CaldavAccountSettingsActivity caldavAccountSettingsActivity); void inject(CaldavAccountSettingsActivity caldavAccountSettingsActivity);
void inject(EteSyncAccountSettingsActivity eteSyncAccountSettingsActivity);
void inject(DriveLoginActivity driveLoginActivity); void inject(DriveLoginActivity driveLoginActivity);
void inject(DebugPreferences debugPreferences); void inject(DebugPreferences debugPreferences);

@ -17,6 +17,7 @@ import android.preference.Preference;
import android.preference.PreferenceCategory; import android.preference.PreferenceCategory;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import com.google.common.base.Strings;
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity; import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity;
import com.todoroo.astrid.service.TaskDeleter; import com.todoroo.astrid.service.TaskDeleter;
import javax.inject.Inject; import javax.inject.Inject;
@ -26,6 +27,7 @@ import org.tasks.analytics.Tracking;
import org.tasks.billing.Inventory; import org.tasks.billing.Inventory;
import org.tasks.billing.PurchaseActivity; import org.tasks.billing.PurchaseActivity;
import org.tasks.caldav.CaldavAccountSettingsActivity; import org.tasks.caldav.CaldavAccountSettingsActivity;
import org.tasks.etesync.EteSyncAccountSettingsActivity;
import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavDao; import org.tasks.data.CaldavDao;
import org.tasks.data.GoogleTaskAccount; 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_SETTINGS = 101;
private static final int REQUEST_CALDAV_SUBSCRIBE = 102; private static final int REQUEST_CALDAV_SUBSCRIBE = 102;
private static final int REQUEST_GOOGLE_TASKS_SUBSCRIBE = 103; 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 ActivityPermissionRequestor permissionRequestor;
@Inject PermissionChecker permissionChecker; @Inject PermissionChecker permissionChecker;
@ -130,6 +134,23 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
return false; 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) { private void logoutConfirmation(GoogleTaskAccount account) {
@ -201,19 +222,34 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
private void addCaldavAccounts() { private void addCaldavAccounts() {
PreferenceCategory caldavPreferences = PreferenceCategory caldavPreferences =
(PreferenceCategory) findPreference(getString(R.string.CalDAV)); (PreferenceCategory) findPreference(getString(R.string.CalDAV));
PreferenceCategory eteSyncPreferences =
(PreferenceCategory) findPreference(R.string.etesync);
caldavPreferences.removeAll(); caldavPreferences.removeAll();
eteSyncPreferences.removeAll();
for (CaldavAccount caldavAccount : caldavDao.getAccounts()) { for (CaldavAccount caldavAccount : caldavDao.getAccounts()) {
Preference accountPreferences = new Preference(this); Preference accountPreferences = new Preference(this);
accountPreferences.setTitle(caldavAccount.getName()); accountPreferences.setTitle(caldavAccount.getName());
accountPreferences.setSummary(caldavAccount.getError()); accountPreferences.setSummary(caldavAccount.getError());
accountPreferences.setOnPreferenceClickListener( if (caldavAccount.isCaldavAccount()) {
preference -> { accountPreferences.setOnPreferenceClickListener(
Intent intent = new Intent(this, CaldavAccountSettingsActivity.class); preference -> {
intent.putExtra(CaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, caldavAccount); Intent intent = new Intent(this, CaldavAccountSettingsActivity.class);
startActivityForResult(intent, REQUEST_CALDAV_SETTINGS); intent.putExtra(CaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, caldavAccount);
return false; startActivityForResult(intent, REQUEST_CALDAV_SETTINGS);
}); return false;
caldavPreferences.addPreference(accountPreferences); });
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); new Intent(this, CaldavAccountSettingsActivity.class), REQUEST_CALDAV_SETTINGS);
} }
private void addEteSyncAccount() {
startActivityForResult(
new Intent(this, EteSyncAccountSettingsActivity.class), REQUEST_ETESYNC_SETTINGS);
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_LOGIN) { if (requestCode == REQUEST_LOGIN) {
@ -233,7 +274,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
} else if (data != null) { } else if (data != null) {
toaster.longToast(data.getStringExtra(GtasksLoginActivity.EXTRA_ERROR)); 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) { if (resultCode == RESULT_OK) {
workManager.updateBackgroundSync(); workManager.updateBackgroundSync();
restart(); restart();
@ -246,6 +287,10 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity {
if (inventory.hasPro()) { if (inventory.hasPro()) {
requestLogin(); requestLogin();
} }
} else if (requestCode == REQUEST_ETESYNC_SUBSCRIBE) {
if (inventory.hasPro()) {
addEteSyncAccount();
}
} else { } else {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
} }

@ -80,6 +80,22 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/encryption_password_layout"
style="@style/TagSettingsRow"
android:visibility="gone"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/encryption_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/encryption_password"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/repeat" android:id="@+id/repeat"
style="@style/TagSettingsRow" style="@style/TagSettingsRow"

@ -6,6 +6,8 @@
<resources> <resources>
<string name="FSA_label">Tasks Shortcut</string> <string name="FSA_label">Tasks Shortcut</string>
<string name="etesync">EteSync</string>
<string name="etesync_url">https://api.etesync.com</string>
<string name="p_date_shortcut_morning">date_shortcut_morning</string> <string name="p_date_shortcut_morning">date_shortcut_morning</string>
<string name="p_date_shortcut_afternoon">date_shortcut_afternoon</string> <string name="p_date_shortcut_afternoon">date_shortcut_afternoon</string>
@ -303,6 +305,7 @@
<string name="preference_screen">preference_screen</string> <string name="preference_screen">preference_screen</string>
<string name="p_add_google_task_account">add_google_task_account</string> <string name="p_add_google_task_account">add_google_task_account</string>
<string name="p_add_caldav_account">add_caldav_account</string> <string name="p_add_caldav_account">add_caldav_account</string>
<string name="p_add_etesync_account">add_etesync_account</string>
<string name="p_google_tasks_add_to_top">google_tasks_add_to_top</string> <string name="p_google_tasks_add_to_top">google_tasks_add_to_top</string>
<string name="p_google_tasks_position_hack">google_tasks_position_hack</string> <string name="p_google_tasks_position_hack">google_tasks_position_hack</string>
<string name="google_tasks_position_hack">Custom order synchronization fix</string> <string name="google_tasks_position_hack">Custom order synchronization fix</string>

@ -378,6 +378,7 @@ File %1$s contained %2$s.\n\n
<string name="name_cannot_be_empty">Name cannot be empty</string> <string name="name_cannot_be_empty">Name cannot be empty</string>
<string name="username_required">Username required</string> <string name="username_required">Username required</string>
<string name="password_required">Password required</string> <string name="password_required">Password required</string>
<string name="encryption_password_required">Encryption password required</string>
<string name="url_required">URL required</string> <string name="url_required">URL required</string>
<string name="url_host_name_required">Host name required</string> <string name="url_host_name_required">Host name required</string>
<string name="url_invalid_scheme">Must begin with http(s)://</string> <string name="url_invalid_scheme">Must begin with http(s)://</string>
@ -447,6 +448,7 @@ File %1$s contained %2$s.\n\n
<string name="add_account">Add account</string> <string name="add_account">Add account</string>
<string name="user">User</string> <string name="user">User</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="encryption_password">Encryption password</string>
<string name="url">URL</string> <string name="url">URL</string>
<string name="error_adding_account">Error: %s</string> <string name="error_adding_account">Error: %s</string>
<string name="calendar_settings">Calendar settings</string> <string name="calendar_settings">Calendar settings</string>

@ -31,6 +31,14 @@
android:key="@string/p_add_caldav_account" android:key="@string/p_add_caldav_account"
android:title="@string/add_account"/> android:title="@string/add_account"/>
<PreferenceCategory
android:key="@string/etesync"
android:title="@string/etesync" />
<Preference
android:key="@string/p_add_etesync_account"
android:title="@string/add_account"/>
<PreferenceCategory android:title="@string/sync_SPr_interval_title"> <PreferenceCategory android:title="@string/sync_SPr_interval_title">
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="true" android:defaultValue="true"

Loading…
Cancel
Save