From b55af6650f9a3c90aa5cd4975c4cfb2339b7ee25 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Wed, 14 Mar 2018 11:39:43 -0500 Subject: [PATCH] Use remote display name for caldav account --- .../org/tasks/backup/TasksJsonImporter.java | 2 +- .../tasks/caldav/CalDAVSettingsActivity.java | 165 ++++++++++-------- .../org/tasks/caldav/CalDAVSyncAdapter.java | 20 ++- .../java/org/tasks/caldav/CaldavClient.java | 54 ++++++ .../java/org/tasks/data/CaldavAccount.java | 28 +++ .../main/java/org/tasks/data/CaldavDao.java | 3 - .../org/tasks/ui/DisplayableException.java | 13 ++ .../res/layout/activity_caldav_settings.xml | 18 +- app/src/main/res/values/strings.xml | 3 + 9 files changed, 212 insertions(+), 94 deletions(-) create mode 100644 app/src/main/java/org/tasks/caldav/CaldavClient.java create mode 100644 app/src/main/java/org/tasks/ui/DisplayableException.java diff --git a/app/src/main/java/org/tasks/backup/TasksJsonImporter.java b/app/src/main/java/org/tasks/backup/TasksJsonImporter.java index ce98b4842..55c5cdba6 100644 --- a/app/src/main/java/org/tasks/backup/TasksJsonImporter.java +++ b/app/src/main/java/org/tasks/backup/TasksJsonImporter.java @@ -134,7 +134,7 @@ public class TasksJsonImporter { } } for (CaldavAccount account : backupContainer.caldavAccounts) { - if (caldavDao.getAccountByName(account.getName()) == null) { + if (caldavDao.getByUuid(account.getUuid()) == null) { caldavDao.insert(account); } } diff --git a/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java index c9d90039d..cf77a1888 100644 --- a/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java +++ b/app/src/main/java/org/tasks/caldav/CalDAVSettingsActivity.java @@ -1,5 +1,6 @@ package org.tasks.caldav; +import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -36,13 +37,16 @@ import org.tasks.preferences.Preferences; import org.tasks.sync.SyncAdapters; import org.tasks.themes.ThemeCache; import org.tasks.themes.ThemeColor; +import org.tasks.ui.DisplayableException; +import java.net.ConnectException; import java.net.IDN; import java.net.URI; import java.net.URISyntaxException; import javax.inject.Inject; +import at.bitfire.dav4android.exception.HttpException; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; @@ -83,11 +87,9 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity @Inject SyncAdapters syncAdapters; @BindView(R.id.root_layout) LinearLayout root; - @BindView(R.id.name) TextInputEditText name; @BindView(R.id.url) TextInputEditText url; @BindView(R.id.user) TextInputEditText user; @BindView(R.id.password) TextInputEditText password; - @BindView(R.id.name_layout) TextInputLayout nameLayout; @BindView(R.id.url_layout) TextInputLayout urlLayout; @BindView(R.id.user_layout) TextInputLayout userLayout; @BindView(R.id.password_layout) TextInputLayout passwordLayout; @@ -101,6 +103,7 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_caldav_settings); + ButterKnife.bind(this); caldavAccount = getIntent().getParcelableExtra(EXTRA_CALDAV_DATA); @@ -111,9 +114,10 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity } if (savedInstanceState == null) { - if (caldavAccount != null) { + if (caldavAccount == null) { + selectedTheme = themeColor.getIndex(); + } else { selectedTheme = caldavAccount.getColor(); - name.setText(caldavAccount.getName()); url.setText(caldavAccount.getUrl()); user.setText(caldavAccount.getUsername()); } @@ -141,19 +145,14 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity if (caldavAccount == null) { toolbar.getMenu().findItem(R.id.delete).setVisible(false); - name.requestFocus(); + url.requestFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT); + imm.showSoftInput(url, InputMethodManager.SHOW_IMPLICIT); } updateTheme(); } - @OnTextChanged(R.id.name) - void onNameChanged(CharSequence text) { - nameLayout.setError(null); - } - @OnTextChanged(R.id.url) void onUrlChanged(CharSequence text) { urlLayout.setError(null); @@ -217,10 +216,6 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity component.inject(this); } - private String getNewName() { - return name.getText().toString().trim(); - } - private String getNewURL() { return url.getText().toString().trim(); } @@ -234,27 +229,13 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity return localAccount == null || !PASSWORD_MASK.equals(input) ? input : localAccount.getPassword(); } - private boolean clashes(String newName) { - CaldavAccount existing = caldavDao.getAccountByName(newName); - return caldavAccount != null && existing != null && caldavAccount.getId() != existing.getId(); - } - private void save() { - String newName = getNewName(); String username = getNewUsername(); String url = getNewURL(); String password = getNewPassword(); boolean failed = false; - if (isEmpty(newName)) { - nameLayout.setError(getString(R.string.name_cannot_be_empty)); - failed = true; - } else if (clashes(newName)) { - nameLayout.setError(getString(R.string.tag_already_exists)); - failed = true; - } - if (isEmpty(url)) { urlLayout.setError(getString(R.string.url_required)); failed = true; @@ -302,51 +283,88 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity } if (caldavAccount == null) { - CaldavAccount newAccount = new CaldavAccount(newName, UUIDHelper.newUUID()); - newAccount.setColor(selectedTheme); - newAccount.setUrl(url); - newAccount.setUsername(username); - if (caldavAccountManager.addAccount(newAccount, password)) { - Account account = caldavAccountManager.getAccount(newAccount.getUuid()); - if (account == null) { - showErrorSnackbar(); - } else { - newAccount.setId(caldavDao.insert(newAccount)); - setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(newAccount))); - finish(); - } - } else { - showErrorSnackbar(); - } + CaldavClient client = new CaldavClient(url, username, password); + ProgressDialog dialog = dialogBuilder.newProgressDialog(R.string.contacting_server); + dialog.show(); + client.getDisplayName() + .doAfterTerminate(dialog::dismiss) + .subscribe(this::addAccount, this::getDisplayNameFailed); + } else if (needsValidation()) { + CaldavClient client = new CaldavClient(url, username, password); + ProgressDialog dialog = dialogBuilder.newProgressDialog(R.string.contacting_server); + dialog.show(); + client.getDisplayName() + .doAfterTerminate(dialog::dismiss) + .subscribe(this::updateAccount, this::getDisplayNameFailed); } else if (hasChanges()) { - if (localAccount == null) { - if (!caldavAccountManager.addAccount(caldavAccount, password)) { - localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid()); - if (localAccount == null) { - showErrorSnackbar(); - return; - } - } + updateAccount(caldavAccount.getName()); + } else { + finish(); + } + } + + private void addAccount(String name) { + CaldavAccount newAccount = new CaldavAccount(name, UUIDHelper.newUUID()); + newAccount.setColor(selectedTheme); + newAccount.setUrl(getNewURL()); + newAccount.setUsername(getNewUsername()); + if (caldavAccountManager.addAccount(newAccount, getNewPassword())) { + Account account = caldavAccountManager.getAccount(newAccount.getUuid()); + if (account == null) { + showGenericError(); + } else { + account.setSynchronizationEnabled(backgroundSync.isChecked()); + newAccount.setId(caldavDao.insert(newAccount)); + setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(newAccount))); + finish(); } + } else { + showGenericError(); + } + } - caldavAccount.setName(newName); - caldavAccount.setColor(selectedTheme); - caldavAccount.setUrl(url); - caldavAccount.setUsername(username); - caldavDao.update(caldavAccount); - if (!isEmpty(password)) { - localAccount.setPassword(password); + private void updateAccount(String name) { + if (localAccount == null) { + if (caldavAccountManager.addAccount(caldavAccount, getNewPassword())) { + localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid()); + } else { + showGenericError(); + return; } - localAccount.setSynchronizationEnabled(backgroundSync.isChecked()); - setResult(RESULT_OK, new Intent(ACTION_RELOAD).putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(caldavAccount))); - finish(); + } + caldavAccount.setName(name); + caldavAccount.setUrl(getNewURL()); + caldavAccount.setUsername(getNewUsername()); + caldavAccount.setColor(selectedTheme); + caldavDao.update(caldavAccount); + localAccount.setPassword(getNewPassword()); + localAccount.setSynchronizationEnabled(backgroundSync.isChecked()); + setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(caldavAccount))); + finish(); + } + + private void getDisplayNameFailed(Throwable t) { + if (t instanceof HttpException) { + showSnackbar(t.getMessage()); + } else if (t instanceof DisplayableException) { + showSnackbar(((DisplayableException) t).getResId()); + } else if (t instanceof ConnectException) { + showSnackbar(R.string.network_error); } else { - finish(); + showGenericError(); } } - private void showErrorSnackbar() { - Snackbar snackbar = Snackbar.make(root, getString(R.string.error_adding_account), 8000) + private void showGenericError() { + showSnackbar(R.string.error_adding_account); + } + + private void showSnackbar(int resId) { + showSnackbar(getString(resId)); + } + + private void showSnackbar(String message) { + Snackbar snackbar = Snackbar.make(root, message, 8000) .setActionTextColor(ContextCompat.getColor(this, R.color.snackbar_text_color)); snackbar.getView().setBackgroundColor(ContextCompat.getColor(this, R.color.snackbar_background)); snackbar.show(); @@ -354,23 +372,26 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity private boolean hasChanges() { if (caldavAccount == null) { - return selectedTheme >= 0 || !isEmpty(getNewName()) || + return selectedTheme >= 0 || !isEmpty(getNewPassword()) || !isEmpty(getNewURL()) || !isEmpty(getNewUsername()) || !backgroundSync.isChecked(); } return localAccount == null || selectedTheme != caldavAccount.getColor() || - !getNewName().equals(caldavAccount.getName()) || - !getNewURL().equals(caldavAccount.getUrl()) || - !getNewUsername().equals(caldavAccount.getUsername()) || - !getNewPassword().equals(localAccount.getPassword()) || + needsValidation() || backgroundSync.isChecked() != localAccount.isBackgroundSyncEnabled(); } + private boolean needsValidation() { + return !getNewURL().equals(caldavAccount.getUrl()) || + !getNewUsername().equals(caldavAccount.getUsername()) || + !getNewPassword().equals(localAccount.getPassword()); + } + @Override public void finish() { InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(name.getWindowToken(), 0); + imm.hideSoftInputFromWindow(url.getWindowToken(), 0); super.finish(); } diff --git a/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java b/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java index 321d46e8f..a7ab5b4f7 100644 --- a/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java +++ b/app/src/main/java/org/tasks/caldav/CalDAVSyncAdapter.java @@ -36,8 +36,10 @@ import javax.inject.Inject; import at.bitfire.dav4android.BasicDigestAuthHandler; import at.bitfire.dav4android.DavCalendar; import at.bitfire.dav4android.DavResource; +import at.bitfire.dav4android.PropertyCollection; import at.bitfire.dav4android.exception.DavException; import at.bitfire.dav4android.exception.HttpException; +import at.bitfire.dav4android.property.DisplayName; import at.bitfire.dav4android.property.GetCTag; import at.bitfire.dav4android.property.GetETag; import at.bitfire.ical4android.CalendarStorageException; @@ -99,11 +101,21 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { try { pushLocalChanges(caldavAccount, httpClient, httpUrl); - davCalendar.propfind(0, GetCTag.NAME); + davCalendar.propfind(0, GetCTag.NAME, DisplayName.NAME); - String remoteCtag = davCalendar.getProperties().get(GetCTag.class).getCTag(); + PropertyCollection properties = davCalendar.getProperties(); + String remoteName = properties.get(DisplayName.class).getDisplayName(); + if (!caldavAccount.getName().equals(remoteName)) { + Timber.d("%s -> %s", caldavAccount.getName(), remoteName); + caldavAccount.setName(remoteName); + caldavDao.update(caldavAccount); + localBroadcastManager.broadcastRefreshList(); + } + + String remoteCtag = properties.get(GetCTag.class).getCTag(); String localCtag = caldavAccount.getCtag(); + if (localCtag != null && localCtag.equals(remoteCtag)) { Timber.d("%s up to date", caldavAccount.getName()); return; @@ -139,7 +151,9 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter { caldavAccount.setCtag(remoteCtag); caldavDao.update(caldavAccount); } catch (IOException | HttpException | DavException | CalendarStorageException e) { - Timber.e(e.getMessage(), e); + Timber.e(e, e.getMessage()); + } catch (Exception e) { + Timber.e(e, e.getMessage()); } localBroadcastManager.broadcastRefresh(); diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.java b/app/src/main/java/org/tasks/caldav/CaldavClient.java new file mode 100644 index 000000000..2ec03dba2 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/CaldavClient.java @@ -0,0 +1,54 @@ +package org.tasks.caldav; + +import org.tasks.R; +import org.tasks.data.CaldavAccount; +import org.tasks.ui.DisplayableException; + +import java.net.URI; +import java.util.concurrent.Callable; + +import at.bitfire.dav4android.BasicDigestAuthHandler; +import at.bitfire.dav4android.DavCalendar; +import at.bitfire.dav4android.property.DisplayName; +import io.reactivex.Single; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; + +public class CaldavClient { + + private final DavCalendar davCalendar; + + public CaldavClient(CaldavAccount caldavAccount, Account localAccount) { + this(caldavAccount.getUrl(), caldavAccount.getUsername(), localAccount.getPassword()); + } + + public CaldavClient(String url, String username, String password) { + BasicDigestAuthHandler basicDigestAuthHandler = new BasicDigestAuthHandler(null, username, password); + OkHttpClient httpClient = new OkHttpClient().newBuilder() + .addNetworkInterceptor(basicDigestAuthHandler) + .authenticator(basicDigestAuthHandler) + .cookieJar(new MemoryCookieStore()) + .followRedirects(false) + .followSslRedirects(false) + .build(); + URI uri = URI.create(url); + HttpUrl httpUrl = HttpUrl.get(uri); + davCalendar = new DavCalendar(httpClient, httpUrl); + } + + public Single getDisplayName() { + Callable callable = () -> { + davCalendar.propfind(0, DisplayName.NAME); + DisplayName displayName = davCalendar.getProperties().get(DisplayName.class); + if (displayName == null) { + throw new DisplayableException(R.string.calendar_not_found); + } + return displayName.getDisplayName(); + }; + return Single.fromCallable(callable) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } +} diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.java b/app/src/main/java/org/tasks/data/CaldavAccount.java index 36efd0c0f..6c8c1bf00 100644 --- a/app/src/main/java/org/tasks/data/CaldavAccount.java +++ b/app/src/main/java/org/tasks/data/CaldavAccount.java @@ -152,4 +152,32 @@ public final class CaldavAccount implements Parcelable { ", username='" + username + '\'' + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CaldavAccount)) return false; + + CaldavAccount that = (CaldavAccount) o; + + if (id != that.id) return false; + if (color != that.color) return false; + if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) return false; + if (name != null ? !name.equals(that.name) : that.name != null) return false; + if (ctag != null ? !ctag.equals(that.ctag) : that.ctag != null) return false; + if (url != null ? !url.equals(that.url) : that.url != null) return false; + return username != null ? username.equals(that.username) : that.username == null; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (uuid != null ? uuid.hashCode() : 0); + result = 31 * result + (name != null ? name.hashCode() : 0); + result = 31 * result + color; + result = 31 * result + (ctag != null ? ctag.hashCode() : 0); + result = 31 * result + (url != null ? url.hashCode() : 0); + result = 31 * result + (username != null ? username.hashCode() : 0); + return result; + } } diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java index 88c3a8927..c7cb1e2d9 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.java +++ b/app/src/main/java/org/tasks/data/CaldavDao.java @@ -11,9 +11,6 @@ import java.util.List; @Dao public interface CaldavDao { - @Query("SELECT * FROM caldav_account WHERE name = :name COLLATE NOCASE LIMIT 1") - CaldavAccount getAccountByName(String name); - @Query("SELECT * FROM caldav_account WHERE uuid = :uuid LIMIT 1") CaldavAccount getByUuid(String uuid); diff --git a/app/src/main/java/org/tasks/ui/DisplayableException.java b/app/src/main/java/org/tasks/ui/DisplayableException.java new file mode 100644 index 000000000..ab48bd0b5 --- /dev/null +++ b/app/src/main/java/org/tasks/ui/DisplayableException.java @@ -0,0 +1,13 @@ +package org.tasks.ui; + +public class DisplayableException extends RuntimeException { + private final int resId; + + public DisplayableException(int resId) { + this.resId = resId; + } + + public int getResId() { + return resId; + } +} diff --git a/app/src/main/res/layout/activity_caldav_settings.xml b/app/src/main/res/layout/activity_caldav_settings.xml index 264b1583d..a3ed5d803 100644 --- a/app/src/main/res/layout/activity_caldav_settings.xml +++ b/app/src/main/res/layout/activity_caldav_settings.xml @@ -20,21 +20,6 @@ android:focusableInTouchMode="true" android:orientation="vertical"> - - - - - @@ -44,6 +29,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/url" + android:imeOptions="flagNoExtractUi" android:textColor="?attr/asTextColor" /> @@ -57,6 +43,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/user" + android:imeOptions="flagNoExtractUi" android:textColor="?attr/asTextColor" /> @@ -72,6 +59,7 @@ android:layout_height="wrap_content" android:hint="@string/password" android:textColor="?attr/asTextColor" + android:imeOptions="flagNoExtractUi" android:inputType="textPassword"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f03edf49f..b76f1a6e9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -825,6 +825,7 @@ File %1$s contained %2$s.\n\n Row settings Tasks requires permission Creating new list + Contacting server Deleting list Renaming list Clear completed tasks? @@ -889,4 +890,6 @@ File %1$s contained %2$s.\n\n Create task List notification Help + Calendar not found + Connection failed