diff --git a/app/build.gradle b/app/build.gradle index 691048626..b3e7fc2f8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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}" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 84028ca9f..382d37a0f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ package="org.tasks"> - + diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.java b/app/src/main/java/org/tasks/caldav/CaldavClient.java index a2c290e17..8c42b6cfe 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavClient.java +++ b/app/src/main/java/org/tasks/caldav/CaldavClient.java @@ -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 getHomeSet() { + Single 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 getCalendars() throws IOException, HttpException, DavException { - davResource.propfind( - 1, ResourceType.NAME, DisplayName.NAME, SupportedCalendarComponentSet.NAME, GetCTag.NAME); - List 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 getCalendars() throws IOException, DavException { + DavResource davResource = new DavResource(httpClient, httpUrl); + DavResponse response = + davResource.propfind( + 1, + ResourceType.NAME, + DisplayName.NAME, + SupportedCalendarComponentSet.NAME, + GetCTag.NAME); + List 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 makeCollection(String displayName) { + Single 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; + } } diff --git a/app/src/main/java/org/tasks/caldav/CaldavConverter.java b/app/src/main/java/org/tasks/caldav/CaldavConverter.java index 434d66058..9c0f35705 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavConverter.java +++ b/app/src/main/java/org/tasks/caldav/CaldavConverter.java @@ -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) { diff --git a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java index 1ed42759b..ab4daa7f7 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java +++ b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.java @@ -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 resources; + List 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 urls = newHashSet(transform(resources, c -> c.getLocation().toString())); + Set 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 members = davCalendar.calendarQuery("VTODO", null, null).getMembers(); - Set remoteObjects = - newHashSet(transform(davCalendar.getMembers(), DavResource::fileName)); + Set remoteObjects = newHashSet(transform(members, DavResponse::fileName)); - Iterable changed = + Iterable 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 items : partition(changed, 30)) { + for (List 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 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 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; } diff --git a/dav4android b/dav4android index e00c8fa36..d026e6d7d 160000 --- a/dav4android +++ b/dav4android @@ -1 +1 @@ -Subproject commit e00c8fa36990a1a6184fb6c8a7ab6229b3117d42 +Subproject commit d026e6d7dbf4a0fdecc7bda28c9cf581a6a7e405 diff --git a/ical4android b/ical4android index 4c1ff076c..c615f1173 160000 --- a/ical4android +++ b/ical4android @@ -1 +1 @@ -Subproject commit 4c1ff076c12b577e7d8daaf35bcc6f09b80c5554 +Subproject commit c615f117365a0b84734f447c37ca4081bec40456 diff --git a/settings.gradle b/settings.gradle index f5f47dbc9..a8f498869 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,4 @@ -include 'app', 'ical4android', 'dav4android' +include 'app' + +includeBuild 'dav4android' +includeBuild 'ical4android'