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) {
if (caldavDao.getAccountByName(account.getName()) == null) {
if (caldavDao.getByUuid(account.getUuid()) == null) {
caldavDao.insert(account);
}
}

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

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

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

@ -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: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:id="@+id/url_layout"
style="@style/TagSettingsRow">
@ -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" />
</android.support.design.widget.TextInputLayout>
@ -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" />
</android.support.design.widget.TextInputLayout>
@ -72,6 +59,7 @@
android:layout_height="wrap_content"
android:hint="@string/password"
android:textColor="?attr/asTextColor"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword"/>
</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="sync_error_permissions">Tasks requires permission</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="renaming_list">Renaming list</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_list_notification">List notification</string>
<string name="help">Help</string>
<string name="calendar_not_found">Calendar not found</string>
<string name="network_error">Connection failed</string>
</resources>

Loading…
Cancel
Save