Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 62a0ba35 authored by Ricki Hirner's avatar Ricki Hirner Committed by GitHub
Browse files

Update synctools (#1579)



* [WIP] Update synctools

* Refactor LocalEvent to use LegacyAndroidCalendar for event operations

* Refactor LocalEvent to use LegacyAndroidCalendar for event operations

* Update cert4android to get 16 kB page size support over Conscrypt 2.5.3 (#1581)

* Move SyncState to resource package because it's not in the database (#1585)

* Update dependencies, including dav4jvm that updates okhttp to 5.x (#1593)

* Update dependencies, including dav4jvm that updates okhttp to 5.x

* Update mockk and okhttp

* Bump version to 4.5.2-beta.1

* Move Insert/update to DAO (#1587)

* Move homeset insert/update logic from repository to DAO; add thread-safety test

* Rename insertOrUpdateByUrlRememberSync

* Fix exceptions being fetched as `Parcelable` instead of `Serializable` in `DebugInfoActivity` (#1597)

Typo: replace `getParcelableExtra` with `getSerializableExtra`

Signed-off-by: default avatarArnau Mora <arnyminerz@proton.me>

* Bump version to 4.5.2

* Fetch translations from Transifex

* Add documentation and handle missing event in LocalEvent

---------

Signed-off-by: default avatarArnau Mora <arnyminerz@proton.me>
Co-authored-by: default avatarSunik Kupfer <kupfer@bitfire.at>
Co-authored-by: default avatarArnau Mora <arnyminerz@proton.me>
parent 71f3558b
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -12,8 +12,8 @@ import android.provider.CalendarContract
import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL
import android.provider.CalendarContract.Events
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.ical4android.AndroidEvent
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.LegacyAndroidCalendar
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
import at.bitfire.ical4android.util.MiscUtils.closeCompat
import at.bitfire.synctools.storage.calendar.AndroidCalendarProvider
@@ -92,8 +92,9 @@ class LocalCalendarTest {
                status = Status.VEVENT_CANCELLED
            })
        }
        val localEvent = AndroidEvent(calendar.androidCalendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT)
        localEvent.add()
        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!
        val eventId = localEvent.id!!

        // set event as dirty
@@ -122,8 +123,9 @@ class LocalCalendarTest {
            summary = "Event with 3 instances"
            rRules.add(RRule("FREQ=DAILY;COUNT=3"))
        }
        val localEvent = AndroidEvent(calendar.androidCalendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT)
        localEvent.add()
        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!
        val eventId = localEvent.id!!

        // set event as dirty
+17 −11
Original line number Diff line number Diff line
@@ -14,8 +14,8 @@ import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL
import android.provider.CalendarContract.Events
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import at.bitfire.ical4android.AndroidEvent
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.LegacyAndroidCalendar
import at.bitfire.ical4android.util.MiscUtils.closeCompat
import at.bitfire.synctools.storage.calendar.AndroidCalendarProvider
import at.techbee.jtx.JtxContract.asSyncAdapter
@@ -74,8 +74,10 @@ class LocalEventTest {
            dtStart = DtStart("20220120T010203Z")
            summary = "Event without uid"
        }
        val localEvent = LocalEvent(AndroidEvent(calendar.androidCalendar, event, null))
        localEvent.add()    // save it to calendar storage

        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!

        // prepare for upload - this should generate a new random uuid, returned as filename
        val fileNameWithSuffix = localEvent.prepareForUpload()
@@ -102,8 +104,9 @@ class LocalEventTest {
            summary = "Event with normal uid"
            uid = "some-event@hostname.tld"     // old UID format, UUID would be new format
        }
        val localEvent = LocalEvent(AndroidEvent(calendar.androidCalendar, event, null))
        localEvent.add() // save it to calendar storage
        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!

        // prepare for upload - this should use the UID for the file name
        val fileNameWithSuffix = localEvent.prepareForUpload()
@@ -129,8 +132,9 @@ class LocalEventTest {
            summary = "Event with funny uid"
            uid = "https://www.example.com/events/asdfewfe-cxyb-ewrws-sadfrwerxyvser-asdfxye-"
        }
        val localEvent = LocalEvent(AndroidEvent(calendar.androidCalendar, event, null))
        localEvent.add() // save it to calendar storage
        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!

        // prepare for upload - this should generate a new random uuid, returned as filename
        val fileNameWithSuffix = localEvent.prepareForUpload()
@@ -181,8 +185,9 @@ class LocalEventTest {
                status = Status.VEVENT_CANCELLED
            })
        }
        val localEvent = LocalEvent(AndroidEvent(calendar.androidCalendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT))
        localEvent.add()
        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!
        val eventId = localEvent.id!!

        // set event as dirty
@@ -210,8 +215,9 @@ class LocalEventTest {
            summary = "Event with 3 instances"
            rRules.add(RRule("FREQ=DAILY;COUNT=3"))
        }
        val localEvent = LocalEvent(AndroidEvent(calendar.androidCalendar, event, "filename.ics", null, null, LocalResource.FLAG_REMOTELY_PRESENT))
        localEvent.add()
        val legacyCalendar = LegacyAndroidCalendar(calendar.androidCalendar)
        legacyCalendar.add(event = event, syncId = "filename.ics", flags = LocalResource.FLAG_REMOTELY_PRESENT)
        val localEvent = calendar.findByName("filename.ics")!!
        val eventId = localEvent.id!!

        // set event as dirty
+35 −27
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import at.bitfire.ical4android.AndroidEvent
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
import at.bitfire.synctools.storage.BatchOperation
import at.bitfire.synctools.storage.calendar.AndroidCalendar
import at.bitfire.synctools.storage.calendar.AndroidEvent2
import at.bitfire.synctools.storage.calendar.CalendarBatchOperation
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -56,11 +57,15 @@ class LocalCalendar @AssistedInject constructor(
            androidCalendar.writeSyncState(state.toString())
        }


    override fun findDeleted() =
        androidCalendar
            .findEvents("${Events.DELETED} AND ${Events.ORIGINAL_ID} IS NULL", null)
            .map { LocalEvent(it) }
    override fun findDeleted(): List<LocalEvent> {
        val result = LinkedList<LocalEvent>()
        androidCalendar.iterateEventRows(null, "${Events.DELETED} AND ${Events.ORIGINAL_ID} IS NULL", null) { values ->
            // create legacy AndroidEvent from AndroidEvent2's content values
            val legacyEvent = AndroidEvent(androidCalendar, values)
            result += LocalEvent(legacyEvent)
        }
        return result
    }

    override fun findDirty(): List<LocalEvent> {
        val dirty = LinkedList<LocalEvent>()
@@ -70,10 +75,11 @@ class LocalCalendar @AssistedInject constructor(
         * When a calendar component is created, its sequence number is 0. It is monotonically incremented by the "Organizer's"
         * CUA each time the "Organizer" makes a significant revision to the calendar component.
         */
        for (androidEvent in androidCalendar.findEvents("${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL", null)) {
            val localEvent = LocalEvent(androidEvent)
        androidCalendar.iterateEventRows(null, "${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL", null) { values ->
            val legacyEvent = AndroidEvent(androidCalendar, values)
            val localEvent = LocalEvent(legacyEvent)
            try {
                val event = requireNotNull(androidEvent.event)
                val event = localEvent.event

                val nonGroupScheduled = event.attendees.isEmpty()
                val weAreOrganizer = localEvent.weAreOrganizer
@@ -95,12 +101,14 @@ class LocalCalendar @AssistedInject constructor(
    }

    override fun findByName(name: String) =
        androidCalendar.findEvents("${Events._SYNC_ID}=?", arrayOf(name)).firstOrNull()?.let { LocalEvent(it) }

        androidCalendar.findEventRow(null, "${Events._SYNC_ID}=?", arrayOf(name))?.let { values ->
            val legacyEvent = AndroidEvent(androidCalendar, values)
            LocalEvent(legacyEvent)
        }

    override fun markNotDirty(flags: Int) =
        androidCalendar.updateEvents(
            contentValuesOf(AndroidEvent.COLUMN_FLAGS to flags),
        androidCalendar.updateEventRows(
            contentValuesOf(AndroidEvent2.COLUMN_FLAGS to flags),
            "${Events.CALENDAR_ID}=? AND NOT ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL",
            arrayOf(androidCalendar.id.toString())
        )
@@ -108,9 +116,9 @@ class LocalCalendar @AssistedInject constructor(
    override fun removeNotDirtyMarked(flags: Int): Int {
        // list all non-dirty events with the given flags and delete every row + its exceptions
        val batch = CalendarBatchOperation(androidCalendar.client)
        androidCalendar.iterateEvents(
        androidCalendar.iterateEventRows(
            arrayOf(Events._ID),
            "${Events.CALENDAR_ID}=? AND NOT ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL AND ${AndroidEvent.COLUMN_FLAGS}=?",
            "${Events.CALENDAR_ID}=? AND NOT ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL AND ${AndroidEvent2.COLUMN_FLAGS}=?",
            arrayOf(androidCalendar.id.toString(), flags.toString())
        ) { values ->
            val id = values.getAsInteger(Events._ID)
@@ -124,8 +132,8 @@ class LocalCalendar @AssistedInject constructor(
    }

    override fun forgetETags() {
        androidCalendar.updateEvents(
            contentValuesOf(AndroidEvent.COLUMN_ETAG to null),
        androidCalendar.updateEventRows(
            contentValuesOf(AndroidEvent2.COLUMN_ETAG to null),
            "${Events.CALENDAR_ID}=?", arrayOf(androidCalendar.id.toString())
        )
    }
@@ -135,8 +143,8 @@ class LocalCalendar @AssistedInject constructor(
        // process deleted exceptions
        logger.info("Processing deleted exceptions")

        androidCalendar.iterateEvents(
            arrayOf(Events._ID, Events.ORIGINAL_ID, AndroidEvent.COLUMN_SEQUENCE),
        androidCalendar.iterateEventRows(
            arrayOf(Events._ID, Events.ORIGINAL_ID, AndroidEvent2.COLUMN_SEQUENCE),
            "${Events.CALENDAR_ID}=? AND ${Events.DELETED} AND ${Events.ORIGINAL_ID} IS NOT NULL",
            arrayOf(androidCalendar.id.toString())
        ) { values ->
@@ -148,12 +156,12 @@ class LocalCalendar @AssistedInject constructor(
            val batch = CalendarBatchOperation(androidCalendar.client)

            // enqueue: increase sequence of main event
            val originalEventValues = androidCalendar.getEventValues(originalID, arrayOf(AndroidEvent.COLUMN_SEQUENCE))
            val originalSequence = originalEventValues?.getAsInteger(AndroidEvent.COLUMN_SEQUENCE) ?: 0
            val originalEventValues = androidCalendar.getEventRow(originalID, arrayOf(AndroidEvent2.COLUMN_SEQUENCE))
            val originalSequence = originalEventValues?.getAsInteger(AndroidEvent2.COLUMN_SEQUENCE) ?: 0

            batch += BatchOperation.CpoBuilder
                .newUpdate(ContentUris.withAppendedId(Events.CONTENT_URI, originalID).asSyncAdapter(androidCalendar.account))
                .withValue(AndroidEvent.COLUMN_SEQUENCE, originalSequence + 1)
                .withValue(AndroidEvent2.COLUMN_SEQUENCE, originalSequence + 1)
                .withValue(Events.DIRTY, 1)

            // completely remove deleted exception
@@ -163,8 +171,8 @@ class LocalCalendar @AssistedInject constructor(

        // process dirty exceptions
        logger.info("Processing dirty exceptions")
        androidCalendar.iterateEvents(
            arrayOf(Events._ID, Events.ORIGINAL_ID, AndroidEvent.COLUMN_SEQUENCE),
        androidCalendar.iterateEventRows(
            arrayOf(Events._ID, Events.ORIGINAL_ID, AndroidEvent2.COLUMN_SEQUENCE),
            "${Events.CALENDAR_ID}=? AND ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NOT NULL",
            arrayOf(androidCalendar.id.toString())
        ) { values ->
@@ -172,7 +180,7 @@ class LocalCalendar @AssistedInject constructor(

            val id = values.getAsLong(Events._ID)                   // can't be null (by definition)
            val originalID = values.getAsLong(Events.ORIGINAL_ID)   // can't be null (by query)
            val sequence = values.getAsInteger(AndroidEvent.COLUMN_SEQUENCE) ?: 0
            val sequence = values.getAsInteger(AndroidEvent2.COLUMN_SEQUENCE) ?: 0

            val batch = CalendarBatchOperation(androidCalendar.client)

@@ -184,7 +192,7 @@ class LocalCalendar @AssistedInject constructor(
            // enqueue: increase exception SEQUENCE and set DIRTY to 0
            batch += BatchOperation.CpoBuilder
                .newUpdate(androidCalendar.eventUri(id))
                .withValue(AndroidEvent.COLUMN_SEQUENCE, sequence + 1)
                .withValue(AndroidEvent2.COLUMN_SEQUENCE, sequence + 1)
                .withValue(Events.DIRTY, 0)

            batch.commit()
@@ -198,7 +206,7 @@ class LocalCalendar @AssistedInject constructor(
     */
    fun deleteDirtyEventsWithoutInstances() {
        // Iterate dirty main events without exceptions
        androidCalendar.iterateEvents(
        androidCalendar.iterateEventRows(
            arrayOf(Events._ID),
            "${Events.DIRTY} AND NOT ${Events.DELETED} AND ${Events.ORIGINAL_ID} IS NULL",
            null
@@ -211,7 +219,7 @@ class LocalCalendar @AssistedInject constructor(
            // delete event if there are no instances
            if (numEventInstances == 0) {
                logger.fine("Marking event #$eventId without instances as deleted")
                androidCalendar.updateEvent(eventId, contentValuesOf(Events.DELETED to 1))
                androidCalendar.updateEventRow(eventId, contentValuesOf(Events.DELETED to 1))
            }
        }
    }
+18 −11
Original line number Diff line number Diff line
@@ -9,6 +9,9 @@ import android.provider.CalendarContract.Events
import androidx.core.content.contentValuesOf
import at.bitfire.ical4android.AndroidEvent
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.LegacyAndroidCalendar
import at.bitfire.synctools.storage.LocalStorageException
import at.bitfire.synctools.storage.calendar.AndroidEvent2
import java.util.UUID

class LocalEvent(
@@ -37,8 +40,6 @@ class LocalEvent(
    override val flags: Int
        get() = androidEvent.flags

    override fun add() = androidEvent.add()

    override fun update(data: Event) = androidEvent.update(data)

    override fun delete() = androidEvent.delete()
@@ -46,8 +47,12 @@ class LocalEvent(

    // other methods

    val weAreOrganizer
        get() = androidEvent.event!!.isOrganizer == true
    val event: Event by lazy {
        val legacyCalendar = LegacyAndroidCalendar(androidEvent.calendar)
        legacyCalendar.getEvent(androidEvent.id) ?: throw LocalStorageException("Event ${androidEvent.id} not found")
    }

    val weAreOrganizer: Boolean = event.isOrganizer == true


    /**
@@ -58,7 +63,7 @@ class LocalEvent(
     */
    override fun prepareForUpload(): String {
        // make sure that UID is set
        val uid: String = androidEvent.event!!.uid ?: run {
        val uid: String = event.uid ?: run {
            // generate new UID
            val newUid = UUID.randomUUID().toString()

@@ -66,8 +71,8 @@ class LocalEvent(
            val values = contentValuesOf(Events.UID_2445 to newUid)
            androidEvent.update(values)

            // update this event
            androidEvent.event?.uid = newUid
            // update in event data object (does not write to calendar store!)
            event.uid = newUid

            newUid
        }
@@ -85,14 +90,16 @@ class LocalEvent(
            "${UUID.randomUUID()}.ics"      // UID would be dangerous as file name, use random UUID instead
    }

    @Deprecated("Use add...() of specific collection implementation", level = DeprecationLevel.ERROR)
    override fun add() = throw NotImplementedError()

    override fun clearDirty(fileName: String?, eTag: String?, scheduleTag: String?) {
        val values = ContentValues(5)
        if (fileName != null)
            values.put(Events._SYNC_ID, fileName)
        values.put(AndroidEvent.COLUMN_ETAG, eTag)
        values.put(AndroidEvent.COLUMN_SCHEDULE_TAG, scheduleTag)
        values.put(AndroidEvent.COLUMN_SEQUENCE, androidEvent.event!!.sequence)
        values.put(AndroidEvent2.COLUMN_ETAG, eTag)
        values.put(AndroidEvent2.COLUMN_SCHEDULE_TAG, scheduleTag)
        values.put(AndroidEvent2.COLUMN_SEQUENCE, event.sequence)
        values.put(Events.DIRTY, 0)
        androidEvent.update(values)

@@ -103,7 +110,7 @@ class LocalEvent(
    }

    override fun updateFlags(flags: Int) {
        val values = contentValuesOf(AndroidEvent.COLUMN_FLAGS to flags)
        val values = contentValuesOf(AndroidEvent2.COLUMN_FLAGS to flags)
        androidEvent.update(values)

        androidEvent.flags = flags
+5 −0
Original line number Diff line number Diff line
@@ -5,7 +5,11 @@
package at.bitfire.davdroid.resource

import android.net.Uri
import at.bitfire.davdroid.resource.LocalResource.Companion.FLAG_REMOTELY_PRESENT

/**
 * Defines operations that are used by SyncManager for all sync data types.
 */
interface LocalResource<in TData: Any> {

    companion object {
@@ -74,6 +78,7 @@ interface LocalResource<in TData: Any> {
     *
     * @return content URI of the created row (e.g. event URI)
     */
    @Deprecated("Use add...() of specific collection implementation")
    fun add(): Uri

    /**
Loading