diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.kt b/app/src/main/java/org/tasks/caldav/CaldavClient.kt index 34aef13ec..495a974c2 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavClient.kt +++ b/app/src/main/java/org/tasks/caldav/CaldavClient.kt @@ -1,8 +1,8 @@ package org.tasks.caldav -import androidx.annotation.WorkerThread import at.bitfire.cert4android.CustomCertManager import at.bitfire.dav4jvm.DavResource +import at.bitfire.dav4jvm.Property import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response.HrefRelation import at.bitfire.dav4jvm.XmlUtils.NS_APPLE_ICAL @@ -32,6 +32,7 @@ import java.io.StringWriter import java.security.KeyManagementException import java.security.NoSuchAlgorithmException import java.util.* +import kotlin.coroutines.suspendCoroutine open class CaldavClient( private val provider: CaldavClientProvider, @@ -43,49 +44,26 @@ open class CaldavClient( suspend fun forAccount(account: CaldavAccount) = provider.forAccount(account) - @WorkerThread - @Throws(DavException::class, IOException::class) - private fun tryFindPrincipal(link: String): String? { - val url = httpUrl!!.resolve(link) - Timber.d("Checking for principal: %s", url) - val davResource = DavResource(httpClient, url!!) - val responses = ArrayList() - davResource.propfind(0, CurrentUserPrincipal.NAME) { response, _ -> - responses.add(response) - } - if (responses.isNotEmpty()) { - val response = responses[0] - val currentUserPrincipal = response[CurrentUserPrincipal::class.java] - if (currentUserPrincipal != null) { - val href = currentUserPrincipal.href - if (!isNullOrEmpty(href)) { - return href - } - } - } - return null - } + private suspend fun tryFindPrincipal(link: String): String? = + httpUrl + ?.resolve(link) + ?.let { DavResource(httpClient, it) } + ?.propfind(0, CurrentUserPrincipal.NAME) + ?.firstOrNull() + ?.let { (response, _) -> response[CurrentUserPrincipal::class.java] } + ?.href + ?.takeIf { it.isNotBlank() } - @WorkerThread - @Throws(DavException::class, IOException::class) - private fun findHomeset(): String { + private suspend fun findHomeset(): String { val davResource = DavResource(httpClient, httpUrl!!) - val responses = ArrayList() - davResource.propfind(0, CalendarHomeSet.NAME) { response, _ -> - responses.add(response) - } - val response = responses[0] - val calendarHomeSet = response[CalendarHomeSet::class.java] + return davResource + .propfind(0, CalendarHomeSet.NAME) + .firstOrNull() + ?.let { (response, _) -> response[CalendarHomeSet::class.java] } + ?.href + ?.takeIf { it.isNotBlank() } + ?.let { davResource.location.resolve(it).toString() } ?: throw DisplayableException(R.string.caldav_home_set_not_found) - val hrefs: List = calendarHomeSet.hrefs - if (hrefs.size != 1) { - throw DisplayableException(R.string.caldav_home_set_not_found) - } - val homeSet = hrefs[0] - if (isNullOrEmpty(homeSet)) { - throw DisplayableException(R.string.caldav_home_set_not_found) - } - return davResource.location.resolve(homeSet).toString() } @Throws(IOException::class, DavException::class, NoSuchAlgorithmException::class, KeyManagementException::class) @@ -112,43 +90,15 @@ open class CaldavClient( .findHomeset() } - @Throws(IOException::class, DavException::class) - suspend fun calendars(): List = withContext(Dispatchers.IO) { - val davResource = DavResource(httpClient, httpUrl!!) - val responses = ArrayList() - davResource.propfind( - 1, - ResourceType.NAME, - DisplayName.NAME, - SupportedCalendarComponentSet.NAME, - GetCTag.NAME, - CalendarColor.NAME, - SyncToken.NAME, - ShareAccess.NAME, - Invite.NAME - ) { response: Response, relation: HrefRelation -> - if (relation == HrefRelation.MEMBER) { - responses.add(response) - } - } - val urls: MutableList = ArrayList() - for (member in responses) { - val resourceType = member[ResourceType::class.java] - if (resourceType == null - || !resourceType.types.contains(CALENDAR)) { - Timber.d("%s is not a calendar", member) - continue - } - val supportedCalendarComponentSet = member.get(SupportedCalendarComponentSet::class.java) - if (supportedCalendarComponentSet == null - || !supportedCalendarComponentSet.supportsTasks) { - Timber.d("%s does not support tasks", member) - continue - } - urls.add(member) - } - urls - } + suspend fun calendars(): List = + DavResource(httpClient, httpUrl!!) + .propfind(1, *calendarProperties) + .filter { (response, relation) -> + relation == HrefRelation.MEMBER && + response[ResourceType::class.java]?.types?.contains(CALENDAR) == true && + response[SupportedCalendarComponentSet::class.java]?.supportsTasks == true + } + .map { (response, _) -> response } @Throws(IOException::class, HttpException::class) suspend fun deleteCollection() = withContext(Dispatchers.IO) { @@ -259,4 +209,31 @@ open class CaldavClient( customCertManager.appInForeground = true return this } + + companion object { + private val calendarProperties = arrayOf( + ResourceType.NAME, + DisplayName.NAME, + SupportedCalendarComponentSet.NAME, + GetCTag.NAME, + CalendarColor.NAME, + SyncToken.NAME, + ShareAccess.NAME, + Invite.NAME + ) + + private suspend fun DavResource.propfind( + depth: Int, + vararg reqProp: Property.Name + ): List> = + withContext(Dispatchers.IO) { + suspendCoroutine { cont -> + val responses = ArrayList>() + propfind(depth, *reqProp) { response, relation -> + responses.add(Pair(response, relation)) + } + cont.resumeWith(Result.success(responses)) + } + } + } } \ No newline at end of file