Use remote display name for caldav account

pull/645/head
Alex Baker 6 years ago
parent 7df27f9353
commit b55af6650f

@ -134,7 +134,7 @@ public class TasksJsonImporter {
} }
} }
for (CaldavAccount account : backupContainer.caldavAccounts) { for (CaldavAccount account : backupContainer.caldavAccounts) {
if (caldavDao.getAccountByName(account.getName()) == null) { if (caldavDao.getByUuid(account.getUuid()) == null) {
caldavDao.insert(account); caldavDao.insert(account);
} }
} }

@ -1,5 +1,6 @@
package org.tasks.caldav; package org.tasks.caldav;
import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@ -36,13 +37,16 @@ import org.tasks.preferences.Preferences;
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;
import org.tasks.ui.DisplayableException;
import java.net.ConnectException;
import java.net.IDN; import java.net.IDN;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import javax.inject.Inject; import javax.inject.Inject;
import at.bitfire.dav4android.exception.HttpException;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
@ -83,11 +87,9 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
@Inject SyncAdapters syncAdapters; @Inject SyncAdapters syncAdapters;
@BindView(R.id.root_layout) LinearLayout root; @BindView(R.id.root_layout) LinearLayout root;
@BindView(R.id.name) TextInputEditText name;
@BindView(R.id.url) TextInputEditText url; @BindView(R.id.url) TextInputEditText url;
@BindView(R.id.user) TextInputEditText user; @BindView(R.id.user) TextInputEditText user;
@BindView(R.id.password) TextInputEditText password; @BindView(R.id.password) TextInputEditText password;
@BindView(R.id.name_layout) TextInputLayout nameLayout;
@BindView(R.id.url_layout) TextInputLayout urlLayout; @BindView(R.id.url_layout) TextInputLayout urlLayout;
@BindView(R.id.user_layout) TextInputLayout userLayout; @BindView(R.id.user_layout) TextInputLayout userLayout;
@BindView(R.id.password_layout) TextInputLayout passwordLayout; @BindView(R.id.password_layout) TextInputLayout passwordLayout;
@ -101,6 +103,7 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_caldav_settings); setContentView(R.layout.activity_caldav_settings);
ButterKnife.bind(this); ButterKnife.bind(this);
caldavAccount = getIntent().getParcelableExtra(EXTRA_CALDAV_DATA); caldavAccount = getIntent().getParcelableExtra(EXTRA_CALDAV_DATA);
@ -111,9 +114,10 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
} }
if (savedInstanceState == null) { if (savedInstanceState == null) {
if (caldavAccount != null) { if (caldavAccount == null) {
selectedTheme = themeColor.getIndex();
} else {
selectedTheme = caldavAccount.getColor(); selectedTheme = caldavAccount.getColor();
name.setText(caldavAccount.getName());
url.setText(caldavAccount.getUrl()); url.setText(caldavAccount.getUrl());
user.setText(caldavAccount.getUsername()); user.setText(caldavAccount.getUsername());
} }
@ -141,19 +145,14 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
if (caldavAccount == null) { if (caldavAccount == null) {
toolbar.getMenu().findItem(R.id.delete).setVisible(false); toolbar.getMenu().findItem(R.id.delete).setVisible(false);
name.requestFocus(); url.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT); imm.showSoftInput(url, InputMethodManager.SHOW_IMPLICIT);
} }
updateTheme(); updateTheme();
} }
@OnTextChanged(R.id.name)
void onNameChanged(CharSequence text) {
nameLayout.setError(null);
}
@OnTextChanged(R.id.url) @OnTextChanged(R.id.url)
void onUrlChanged(CharSequence text) { void onUrlChanged(CharSequence text) {
urlLayout.setError(null); urlLayout.setError(null);
@ -217,10 +216,6 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
component.inject(this); component.inject(this);
} }
private String getNewName() {
return name.getText().toString().trim();
}
private String getNewURL() { private String getNewURL() {
return url.getText().toString().trim(); return url.getText().toString().trim();
} }
@ -234,27 +229,13 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
return localAccount == null || !PASSWORD_MASK.equals(input) ? input : localAccount.getPassword(); 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() { private void save() {
String newName = getNewName();
String username = getNewUsername(); String username = getNewUsername();
String url = getNewURL(); String url = getNewURL();
String password = getNewPassword(); String password = getNewPassword();
boolean failed = false; 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)) { if (isEmpty(url)) {
urlLayout.setError(getString(R.string.url_required)); urlLayout.setError(getString(R.string.url_required));
failed = true; failed = true;
@ -302,51 +283,88 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
} }
if (caldavAccount == null) { if (caldavAccount == null) {
CaldavAccount newAccount = new CaldavAccount(newName, UUIDHelper.newUUID()); CaldavClient client = new CaldavClient(url, username, password);
newAccount.setColor(selectedTheme); ProgressDialog dialog = dialogBuilder.newProgressDialog(R.string.contacting_server);
newAccount.setUrl(url); dialog.show();
newAccount.setUsername(username); client.getDisplayName()
if (caldavAccountManager.addAccount(newAccount, password)) { .doAfterTerminate(dialog::dismiss)
Account account = caldavAccountManager.getAccount(newAccount.getUuid()); .subscribe(this::addAccount, this::getDisplayNameFailed);
if (account == null) { } else if (needsValidation()) {
showErrorSnackbar(); CaldavClient client = new CaldavClient(url, username, password);
} else { ProgressDialog dialog = dialogBuilder.newProgressDialog(R.string.contacting_server);
newAccount.setId(caldavDao.insert(newAccount)); dialog.show();
setResult(RESULT_OK, new Intent().putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(newAccount))); client.getDisplayName()
finish(); .doAfterTerminate(dialog::dismiss)
} .subscribe(this::updateAccount, this::getDisplayNameFailed);
} else {
showErrorSnackbar();
}
} else if (hasChanges()) { } else if (hasChanges()) {
if (localAccount == null) { updateAccount(caldavAccount.getName());
if (!caldavAccountManager.addAccount(caldavAccount, password)) { } else {
localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid()); finish();
if (localAccount == null) { }
showErrorSnackbar(); }
return;
} 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); private void updateAccount(String name) {
caldavAccount.setColor(selectedTheme); if (localAccount == null) {
caldavAccount.setUrl(url); if (caldavAccountManager.addAccount(caldavAccount, getNewPassword())) {
caldavAccount.setUsername(username); localAccount = caldavAccountManager.getAccount(caldavAccount.getUuid());
caldavDao.update(caldavAccount); } else {
if (!isEmpty(password)) { showGenericError();
localAccount.setPassword(password); return;
} }
localAccount.setSynchronizationEnabled(backgroundSync.isChecked()); }
setResult(RESULT_OK, new Intent(ACTION_RELOAD).putExtra(TaskListActivity.OPEN_FILTER, new CaldavFilter(caldavAccount))); caldavAccount.setName(name);
finish(); 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 { } else {
finish(); showGenericError();
} }
} }
private void showErrorSnackbar() { private void showGenericError() {
Snackbar snackbar = Snackbar.make(root, getString(R.string.error_adding_account), 8000) 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)); .setActionTextColor(ContextCompat.getColor(this, R.color.snackbar_text_color));
snackbar.getView().setBackgroundColor(ContextCompat.getColor(this, R.color.snackbar_background)); snackbar.getView().setBackgroundColor(ContextCompat.getColor(this, R.color.snackbar_background));
snackbar.show(); snackbar.show();
@ -354,23 +372,26 @@ public class CalDAVSettingsActivity extends ThemedInjectingAppCompatActivity
private boolean hasChanges() { private boolean hasChanges() {
if (caldavAccount == null) { if (caldavAccount == null) {
return selectedTheme >= 0 || !isEmpty(getNewName()) || return selectedTheme >= 0 ||
!isEmpty(getNewPassword()) || !isEmpty(getNewURL()) || !isEmpty(getNewPassword()) || !isEmpty(getNewURL()) ||
!isEmpty(getNewUsername()) || !backgroundSync.isChecked(); !isEmpty(getNewUsername()) || !backgroundSync.isChecked();
} }
return localAccount == null || return localAccount == null ||
selectedTheme != caldavAccount.getColor() || selectedTheme != caldavAccount.getColor() ||
!getNewName().equals(caldavAccount.getName()) || needsValidation() ||
!getNewURL().equals(caldavAccount.getUrl()) ||
!getNewUsername().equals(caldavAccount.getUsername()) ||
!getNewPassword().equals(localAccount.getPassword()) ||
backgroundSync.isChecked() != localAccount.isBackgroundSyncEnabled(); backgroundSync.isChecked() != localAccount.isBackgroundSyncEnabled();
} }
private boolean needsValidation() {
return !getNewURL().equals(caldavAccount.getUrl()) ||
!getNewUsername().equals(caldavAccount.getUsername()) ||
!getNewPassword().equals(localAccount.getPassword());
}
@Override @Override
public void finish() { public void finish() {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(name.getWindowToken(), 0); imm.hideSoftInputFromWindow(url.getWindowToken(), 0);
super.finish(); super.finish();
} }

@ -36,8 +36,10 @@ import javax.inject.Inject;
import at.bitfire.dav4android.BasicDigestAuthHandler; import at.bitfire.dav4android.BasicDigestAuthHandler;
import at.bitfire.dav4android.DavCalendar; import at.bitfire.dav4android.DavCalendar;
import at.bitfire.dav4android.DavResource; import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.PropertyCollection;
import at.bitfire.dav4android.exception.DavException; import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException; import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag; import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetETag; import at.bitfire.dav4android.property.GetETag;
import at.bitfire.ical4android.CalendarStorageException; import at.bitfire.ical4android.CalendarStorageException;
@ -99,11 +101,21 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter {
try { try {
pushLocalChanges(caldavAccount, httpClient, httpUrl); 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(); String localCtag = caldavAccount.getCtag();
if (localCtag != null && localCtag.equals(remoteCtag)) { if (localCtag != null && localCtag.equals(remoteCtag)) {
Timber.d("%s up to date", caldavAccount.getName()); Timber.d("%s up to date", caldavAccount.getName());
return; return;
@ -139,7 +151,9 @@ public class CalDAVSyncAdapter extends InjectingAbstractThreadedSyncAdapter {
caldavAccount.setCtag(remoteCtag); caldavAccount.setCtag(remoteCtag);
caldavDao.update(caldavAccount); caldavDao.update(caldavAccount);
} catch (IOException | HttpException | DavException | CalendarStorageException e) { } 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(); localBroadcastManager.broadcastRefresh();

@ -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<String> getDisplayName() {
Callable<String> 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());
}
}

@ -152,4 +152,32 @@ public final class CaldavAccount implements Parcelable {
", username='" + username + '\'' + ", 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;
}
} }

@ -11,9 +11,6 @@ import java.util.List;
@Dao @Dao
public interface CaldavDao { 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") @Query("SELECT * FROM caldav_account WHERE uuid = :uuid LIMIT 1")
CaldavAccount getByUuid(String uuid); CaldavAccount getByUuid(String uuid);

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

@ -20,21 +20,6 @@
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:orientation="vertical"> android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:id="@+id/name_layout"
style="@style/TagSettingsRow">
<android.support.design.widget.TextInputEditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/name"
android:imeOptions="flagNoExtractUi"
android:inputType="textCapSentences|textFilter"
android:maxLines="1"
android:textColor="?attr/asTextColor" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout <android.support.design.widget.TextInputLayout
android:id="@+id/url_layout" android:id="@+id/url_layout"
style="@style/TagSettingsRow"> style="@style/TagSettingsRow">
@ -44,6 +29,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/url" android:hint="@string/url"
android:imeOptions="flagNoExtractUi"
android:textColor="?attr/asTextColor" /> android:textColor="?attr/asTextColor" />
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@ -57,6 +43,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/user" android:hint="@string/user"
android:imeOptions="flagNoExtractUi"
android:textColor="?attr/asTextColor" /> android:textColor="?attr/asTextColor" />
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@ -72,6 +59,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/password" android:hint="@string/password"
android:textColor="?attr/asTextColor" android:textColor="?attr/asTextColor"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword"/> android:inputType="textPassword"/>
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>

@ -825,6 +825,7 @@ File %1$s contained %2$s.\n\n
<string name="widget_row_settings">Row settings</string> <string name="widget_row_settings">Row settings</string>
<string name="sync_error_permissions">Tasks requires permission</string> <string name="sync_error_permissions">Tasks requires permission</string>
<string name="creating_new_list">Creating new list</string> <string name="creating_new_list">Creating new list</string>
<string name="contacting_server">Contacting server</string>
<string name="deleting_list">Deleting list</string> <string name="deleting_list">Deleting list</string>
<string name="renaming_list">Renaming list</string> <string name="renaming_list">Renaming list</string>
<string name="clear_completed_tasks_confirmation">Clear completed tasks?</string> <string name="clear_completed_tasks_confirmation">Clear completed tasks?</string>
@ -889,4 +890,6 @@ File %1$s contained %2$s.\n\n
<string name="tasker_create_task">Create task</string> <string name="tasker_create_task">Create task</string>
<string name="tasker_list_notification">List notification</string> <string name="tasker_list_notification">List notification</string>
<string name="help">Help</string> <string name="help">Help</string>
<string name="calendar_not_found">Calendar not found</string>
<string name="network_error">Connection failed</string>
</resources> </resources>

Loading…
Cancel
Save