Update ical4android and dav4android

pull/513/head
Alex Baker 6 years ago
parent 83a783d4a2
commit 2a9fbcfc32

@ -116,8 +116,10 @@ final STETHO_VERSION = '1.5.0'
final TESTING_SUPPORT_VERSION = '1.0.0'
dependencies {
implementation project(':ical4android')
implementation project(':dav4android')
implementation ":dav4android:"
implementation (":ical4android:") {
exclude group: 'org.threeten', module: 'threetenbp'
}
annotationProcessor "com.google.dagger:dagger-compiler:${DAGGER_VERSION}"
implementation "com.google.dagger:dagger:${DAGGER_VERSION}"

@ -5,7 +5,7 @@
package="org.tasks">
<uses-sdk tools:overrideLibrary="timber.log"/>
<uses-sdk tools:overrideLibrary="timber.log, at.bitfire.ical4android"/>
<!-- ================================================== Used Permissions = -->

@ -8,7 +8,7 @@ import static java.util.Arrays.asList;
import at.bitfire.dav4android.BasicDigestAuthHandler;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.PropertyCollection;
import at.bitfire.dav4android.DavResponse;
import at.bitfire.dav4android.XmlUtils;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
@ -36,13 +36,15 @@ import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavCalendar;
import org.tasks.security.Encryption;
import org.tasks.ui.DisplayableException;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
import timber.log.Timber;
class CaldavClient {
private final DavResource davResource;
private HttpUrl httpUrl;
private final OkHttpClient httpClient;
private final HttpUrl httpUrl;
CaldavClient(CaldavAccount caldavAccount, Encryption encryption) {
this(
@ -61,7 +63,7 @@ class CaldavClient {
CaldavClient(String url, String username, String password) {
BasicDigestAuthHandler basicDigestAuthHandler =
new BasicDigestAuthHandler(null, username, password);
OkHttpClient httpClient =
httpClient =
new OkHttpClient()
.newBuilder()
.addNetworkInterceptor(basicDigestAuthHandler)
@ -73,18 +75,18 @@ class CaldavClient {
.build();
URI uri = URI.create(url);
httpUrl = HttpUrl.get(uri);
davResource = new DavResource(httpClient, httpUrl);
}
private String tryFindPrincipal() throws DavException, IOException, HttpException {
private String tryFindPrincipal() throws DavException, IOException {
for (String link : asList("", "/.well-known/caldav")) {
HttpUrl url = httpUrl.resolve(link);
Timber.d("Checking for principal: %s", url);
davResource.setLocation(url);
DavResource davResource = new DavResource(httpClient, url);
DavResponse response = null;
try {
davResource.propfind(0, CurrentUserPrincipal.NAME);
response = davResource.propfind(0, CurrentUserPrincipal.NAME);
} catch (HttpException e) {
switch (e.getStatus()) {
switch (e.getCode()) {
case 405:
Timber.w(e);
break;
@ -92,22 +94,23 @@ class CaldavClient {
throw e;
}
}
PropertyCollection properties = davResource.getProperties();
CurrentUserPrincipal currentUserPrincipal = properties.get(CurrentUserPrincipal.class);
if (currentUserPrincipal != null) {
String href = currentUserPrincipal.getHref();
if (!isEmpty(href)) {
return href;
if (response != null) {
CurrentUserPrincipal currentUserPrincipal = response.get(CurrentUserPrincipal.class);
if (currentUserPrincipal != null) {
String href = currentUserPrincipal.getHref();
if (!isEmpty(href)) {
return href;
}
}
}
}
return null;
}
private String findHomeset() throws DavException, IOException, HttpException {
davResource.propfind(0, CalendarHomeSet.NAME);
PropertyCollection properties = davResource.getProperties();
CalendarHomeSet calendarHomeSet = properties.get(CalendarHomeSet.class);
private String findHomeset(HttpUrl httpUrl) throws DavException, IOException {
DavResource davResource = new DavResource(httpClient, httpUrl);
DavResponse response = davResource.propfind(0, CalendarHomeSet.NAME);
CalendarHomeSet calendarHomeSet = response.get(CalendarHomeSet.class);
if (calendarHomeSet == null) {
throw new DisplayableException(R.string.caldav_home_set_not_found);
}
@ -122,32 +125,35 @@ class CaldavClient {
return davResource.getLocation().resolve(homeSet).toString();
}
public Single<String> getHomeSet() {
Single<String> getHomeSet() {
return Single.fromCallable(
() -> {
String principal = tryFindPrincipal();
if (!isEmpty(principal)) {
davResource.setLocation(httpUrl.resolve(principal));
}
return findHomeset();
return findHomeset(isEmpty(principal) ? httpUrl : httpUrl.resolve(principal));
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
public List<DavResource> getCalendars() throws IOException, HttpException, DavException {
davResource.propfind(
1, ResourceType.NAME, DisplayName.NAME, SupportedCalendarComponentSet.NAME, GetCTag.NAME);
List<DavResource> urls = new ArrayList<>();
for (DavResource member : davResource.getMembers()) {
PropertyCollection properties = member.getProperties();
ResourceType resourceType = properties.get(ResourceType.class);
if (resourceType == null || !resourceType.getTypes().contains(ResourceType.CALENDAR)) {
public List<DavResponse> getCalendars() throws IOException, DavException {
DavResource davResource = new DavResource(httpClient, httpUrl);
DavResponse response =
davResource.propfind(
1,
ResourceType.NAME,
DisplayName.NAME,
SupportedCalendarComponentSet.NAME,
GetCTag.NAME);
List<DavResponse> urls = new ArrayList<>();
for (DavResponse member : response.getMembers()) {
ResourceType resourceType = member.get(ResourceType.class);
if (resourceType == null
|| !resourceType.getTypes().contains(ResourceType.Companion.getCALENDAR())) {
Timber.d("%s is not a calendar", member);
continue;
}
SupportedCalendarComponentSet supportedCalendarComponentSet =
properties.get(SupportedCalendarComponentSet.class);
member.get(SupportedCalendarComponentSet.class);
if (supportedCalendarComponentSet == null
|| !supportedCalendarComponentSet.getSupportsTasks()) {
Timber.d("%s does not support tasks", member);
@ -158,17 +164,17 @@ class CaldavClient {
return urls;
}
public Completable deleteCollection() {
return Completable.fromAction(() -> davResource.delete(null))
Completable deleteCollection() {
return Completable.fromAction(() -> new DavResource(httpClient, httpUrl).delete(null))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
public Single<String> makeCollection(String displayName) {
Single<String> makeCollection(String displayName) {
return Single.fromCallable(
() -> {
davResource.setLocation(
davResource.getLocation().resolve(UUIDHelper.newUUID() + "/"));
DavResource davResource =
new DavResource(httpClient, httpUrl.resolve(UUIDHelper.newUUID() + "/"));
String mkcolString = getMkcolString(displayName);
davResource.mkCol(mkcolString);
return davResource.getLocation().toString();
@ -177,9 +183,10 @@ class CaldavClient {
.observeOn(AndroidSchedulers.mainThread());
}
private String getMkcolString(String displayName) throws IOException {
private String getMkcolString(String displayName) throws IOException, XmlPullParserException {
XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
XmlSerializer xml = xmlPullParserFactory.newSerializer();
StringWriter stringWriter = new StringWriter();
XmlSerializer xml = XmlUtils.newSerializer();
xml.setOutput(stringWriter);
xml.startDocument("UTF-8", null);
xml.setPrefix("", NS_WEBDAV);
@ -209,4 +216,8 @@ class CaldavClient {
xml.flush();
return stringWriter.toString();
}
public OkHttpClient getHttpClient() {
return httpClient;
}
}

@ -4,11 +4,9 @@ import static com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY;
import static com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY_TIME;
import static org.tasks.date.DateTimeUtils.newDateTime;
import at.bitfire.ical4android.InvalidCalendarException;
import com.google.common.base.Strings;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Task.Priority;
import java.io.IOException;
import java.io.StringReader;
import java.text.DateFormat;
import java.text.ParseException;
@ -94,14 +92,14 @@ public class CaldavConverter {
return remotePriority > 5 ? Math.min(9, remotePriority) : 9;
}
public static at.bitfire.ical4android.Task toCaldav(CaldavTask caldavTask, Task task) {
static at.bitfire.ical4android.Task toCaldav(CaldavTask caldavTask, Task task) {
at.bitfire.ical4android.Task remote = null;
try {
if (!Strings.isNullOrEmpty(caldavTask.getVtodo())) {
remote =
at.bitfire.ical4android.Task.fromReader(new StringReader(caldavTask.getVtodo())).get(0);
at.bitfire.ical4android.Task.Companion.fromReader(new StringReader(caldavTask.getVtodo())).get(0);
}
} catch (IOException | InvalidCalendarException e) {
} catch (Exception e) {
Timber.e(e);
}
if (remote == null) {

@ -3,7 +3,6 @@ package org.tasks.caldav;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.partition;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Sets.difference;
@ -13,15 +12,14 @@ import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.content.Context;
import at.bitfire.dav4android.DavCalendar;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.PropertyCollection;
import at.bitfire.dav4android.DavResponse;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.CalendarData;
import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.ical4android.InvalidCalendarException;
import at.bitfire.ical4android.iCalendar;
import at.bitfire.ical4android.ICalendar;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@ -61,7 +59,7 @@ import timber.log.Timber;
public class CaldavSynchronizer {
static {
iCalendar.Companion.setProdId(
ICalendar.Companion.setProdId(
new ProdId("+//IDN tasks.org//android-" + BuildConfig.VERSION_CODE + "//EN"));
}
@ -112,35 +110,35 @@ public class CaldavSynchronizer {
continue;
}
CaldavClient caldavClient = new CaldavClient(account, encryption);
List<DavResource> resources;
List<DavResponse> resources;
try {
resources = caldavClient.getCalendars();
} catch (IOException | DavException | HttpException e) {
} catch (IOException | DavException e) {
account.setError(e.getMessage());
caldavDao.update(account);
localBroadcastManager.broadcastRefreshList();
Timber.e(e);
continue;
}
Set<String> urls = newHashSet(transform(resources, c -> c.getLocation().toString()));
Set<String> urls = newHashSet(transform(resources, c -> c.getUrl().toString()));
Timber.d("Found calendars: %s", urls);
for (CaldavCalendar calendar :
caldavDao.findDeletedCalendars(account.getUuid(), newArrayList(urls))) {
taskDeleter.delete(calendar);
}
for (DavResource resource : resources) {
String url = resource.getLocation().toString();
PropertyCollection properties = resource.getProperties();
for (DavResponse resource : resources) {
String url = resource.getUrl().toString();
CaldavCalendar calendar = caldavDao.getCalendarByUrl(account.getUuid(), url);
if (calendar == null) {
calendar = new CaldavCalendar();
calendar.setName(properties.get(DisplayName.class).getDisplayName());
calendar.setName(resource.get(DisplayName.class).getDisplayName());
calendar.setAccount(account.getUuid());
calendar.setUrl(url);
calendar.setUuid(UUIDHelper.newUUID());
calendar.setId(caldavDao.insert(calendar));
}
sync(calendar, resource);
sync(calendar, resource, caldavClient.getHttpClient());
}
account.setError("");
caldavDao.update(account);
@ -148,15 +146,13 @@ public class CaldavSynchronizer {
}
}
private void sync(CaldavCalendar caldavCalendar, DavResource resource) {
private void sync(CaldavCalendar caldavCalendar, DavResponse resource, OkHttpClient httpClient) {
Timber.d("sync(%s)", caldavCalendar);
OkHttpClient httpClient = resource.getHttpClient();
HttpUrl httpUrl = resource.getLocation();
HttpUrl httpUrl = resource.getUrl();
try {
pushLocalChanges(caldavCalendar, httpClient, httpUrl);
PropertyCollection properties = resource.getProperties();
String remoteName = properties.get(DisplayName.class).getDisplayName();
String remoteName = resource.get(DisplayName.class).getDisplayName();
if (!caldavCalendar.getName().equals(remoteName)) {
Timber.d("%s -> %s", caldavCalendar.getName(), remoteName);
caldavCalendar.setName(remoteName);
@ -164,7 +160,7 @@ public class CaldavSynchronizer {
localBroadcastManager.broadcastRefreshList();
}
String remoteCtag = properties.get(GetCTag.class).getCTag();
String remoteCtag = resource.get(GetCTag.class).getCTag();
String localCtag = caldavCalendar.getCtag();
if (localCtag != null && localCtag.equals(remoteCtag)) {
@ -174,16 +170,15 @@ public class CaldavSynchronizer {
DavCalendar davCalendar = new DavCalendar(httpClient, httpUrl);
davCalendar.calendarQuery("VTODO", null, null);
List<DavResponse> members = davCalendar.calendarQuery("VTODO", null, null).getMembers();
Set<String> remoteObjects =
newHashSet(transform(davCalendar.getMembers(), DavResource::fileName));
Set<String> remoteObjects = newHashSet(transform(members, DavResponse::fileName));
Iterable<DavResource> changed =
Iterable<DavResponse> changed =
filter(
ImmutableSet.copyOf(davCalendar.getMembers()),
ImmutableSet.copyOf(members),
vCard -> {
GetETag eTag = (GetETag) vCard.getProperties().get(GetETag.NAME);
GetETag eTag = vCard.get(GetETag.class);
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
return false;
}
@ -192,17 +187,17 @@ public class CaldavSynchronizer {
return caldavTask == null || !eTag.getETag().equals(caldavTask.getEtag());
});
for (List<DavResource> items : partition(changed, 30)) {
for (List<DavResponse> items : partition(changed, 30)) {
if (items.size() == 1) {
DavResource vCard = items.get(0);
PropertyCollection vcardProperties = vCard.getProperties();
GetETag eTag = (GetETag) vcardProperties.get(GetETag.NAME);
DavResponse vCard = items.get(0);
GetETag eTag = vCard.get(GetETag.class);
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
throw new DavException(
"Received CalDAV GET response without ETag for " + vCard.getLocation());
"Received CalDAV GET response without ETag for " + vCard.getUrl());
}
Timber.d("SINGLE %s", vCard.getLocation());
ResponseBody responseBody = vCard.get("text/calendar");
Timber.d("SINGLE %s", vCard.getUrl());
DavResponse response = new DavResource(httpClient, vCard.getUrl()).get("text/calendar");
ResponseBody responseBody = response.getBody();
Reader reader = null;
try {
reader = responseBody.charStream();
@ -215,23 +210,21 @@ public class CaldavSynchronizer {
}
} else {
ArrayList<HttpUrl> urls =
newArrayList(Iterables.transform(items, DavResource::getLocation));
davCalendar.multiget(urls);
newArrayList(Iterables.transform(items, DavResponse::getUrl));
DavResponse response = davCalendar.multiget(urls);
Timber.d("MULTI %s", urls);
for (DavResource vCard : davCalendar.getMembers()) {
PropertyCollection vcardProperties = vCard.getProperties();
GetETag eTag = (GetETag) vcardProperties.get(GetETag.NAME);
for (DavResponse vCard : response.getMembers()) {
GetETag eTag = vCard.get(GetETag.class);
if (eTag == null || isNullOrEmpty(eTag.getETag())) {
throw new DavException(
"Received CalDAV GET response without ETag for " + vCard.getLocation());
"Received CalDAV GET response without ETag for " + vCard.getUrl());
}
CalendarData calendarData = (CalendarData) vcardProperties.get(CalendarData.NAME);
CalendarData calendarData = vCard.get(CalendarData.class);
if (calendarData == null || isNullOrEmpty(calendarData.getICalendar())) {
throw new DavException(
"Received CalDAV GET response without CalendarData for " + vCard.getLocation());
"Received CalDAV GET response without CalendarData for " + vCard.getUrl());
}
processVTodo(
@ -253,7 +246,7 @@ public class CaldavSynchronizer {
caldavCalendar.setCtag(remoteCtag);
Timber.d("UPDATE %s", caldavCalendar);
caldavDao.update(caldavCalendar);
} catch (IOException | HttpException | DavException e) {
} catch (IOException | DavException e) {
Timber.e(e);
} catch (Exception e) {
Timber.e(e);
@ -284,7 +277,7 @@ public class CaldavSynchronizer {
remote.delete(null);
}
} catch (HttpException e) {
if (e.getStatus() != 404) {
if (e.getCode() != 404) {
Timber.e(e);
return false;
}
@ -334,14 +327,14 @@ public class CaldavSynchronizer {
ByteArrayOutputStream os = new ByteArrayOutputStream();
remoteModel.write(os);
byte[] data = os.toByteArray();
RequestBody requestBody = RequestBody.create(DavCalendar.MIME_ICALENDAR, data);
RequestBody requestBody = RequestBody.create(DavCalendar.Companion.getMIME_ICALENDAR(), data);
try {
DavResource remote =
new DavResource(
httpClient, httpUrl.newBuilder().addPathSegment(caldavTask.getObject()).build());
remote.put(requestBody, null, false);
GetETag getETag = remote.getProperties().get(GetETag.class);
DavResponse response = remote.put(requestBody, null, false);
GetETag getETag = response.get(GetETag.class);
if (getETag != null && !isNullOrEmpty(getETag.getETag())) {
caldavTask.setEtag(getETag.getETag());
caldavTask.setVtodo(new String(data));
@ -357,12 +350,11 @@ public class CaldavSynchronizer {
}
private void processVTodo(
String fileName, CaldavCalendar caldavCalendar, String eTag, String vtodo)
throws IOException {
String fileName, CaldavCalendar caldavCalendar, String eTag, String vtodo) {
List<at.bitfire.ical4android.Task> tasks;
try {
tasks = at.bitfire.ical4android.Task.fromReader(new StringReader(vtodo));
} catch (InvalidCalendarException e) {
tasks = at.bitfire.ical4android.Task.Companion.fromReader(new StringReader(vtodo));
} catch (Exception e) {
Timber.e(e);
return;
}

@ -1 +1 @@
Subproject commit e00c8fa36990a1a6184fb6c8a7ab6229b3117d42
Subproject commit d026e6d7dbf4a0fdecc7bda28c9cf581a6a7e405

@ -1 +1 @@
Subproject commit 4c1ff076c12b577e7d8daaf35bcc6f09b80c5554
Subproject commit c615f117365a0b84734f447c37ca4081bec40456

@ -1 +1,4 @@
include 'app', 'ical4android', 'dav4android'
include 'app'
includeBuild 'dav4android'
includeBuild 'ical4android'

Loading…
Cancel
Save