Skip to content

Commit

Permalink
Merge branch 'main' into 171-add-_sync_id-property
Browse files Browse the repository at this point in the history
  • Loading branch information
rfc2822 committed Dec 15, 2024
2 parents 1ecb919 + 12df9bf commit f6fcfee
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 39 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
agp = "8.7.2"
agp = "8.7.3"
android-desugar = "2.1.3"
androidx-core = "1.15.0"
androidx-test-rules = "1.6.1"
Expand All @@ -10,7 +10,7 @@ dokka ="1.9.20"
# noinspection GradleDependency
ical4j = "3.2.19" # final version; update to 4.x will require much work
junit = "4.13.2"
kotlin = "2.0.21"
kotlin = "2.1.0"
mockk = "1.13.13"
slf4j = "2.0.16"

Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import java.util.logging.Logger

@RunWith(Parameterized::class)

abstract class AbstractTasksTest(
abstract class DmfsStyleProvidersTaskTest(
val providerName: TaskProvider.ProviderName
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import org.junit.Assert.assertTrue
import org.junit.Test

class DmfsTaskListTest(providerName: TaskProvider.ProviderName):
AbstractTasksTest(providerName) {
DmfsStyleProvidersTaskTest(providerName) {

private val testAccount = Account("AndroidTaskListTest", TaskContract.LOCAL_ACCOUNT_TYPE)

Expand Down
25 changes: 18 additions & 7 deletions lib/src/androidTest/kotlin/at/bitfire/ical4android/DmfsTaskTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import android.content.ContentUris
import android.content.ContentValues
import android.database.DatabaseUtils
import android.net.Uri
import androidx.test.filters.MediumTest
import at.bitfire.ical4android.impl.TestTask
import at.bitfire.ical4android.impl.TestTaskList
import at.bitfire.ical4android.util.DateUtils
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateList
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.component.VAlarm
import net.fortuna.ical4j.model.parameter.Email
import net.fortuna.ical4j.model.parameter.RelType
import net.fortuna.ical4j.model.parameter.TzId
Expand Down Expand Up @@ -52,8 +52,8 @@ import org.junit.Test
import java.time.ZoneId

class DmfsTaskTest(
providerName: TaskProvider.ProviderName
): AbstractTasksTest(providerName) {
providerName: TaskProvider.ProviderName
): DmfsStyleProvidersTaskTest(providerName) {

private val tzVienna = DateUtils.ical4jTimeZone("Europe/Vienna")!!
private val tzChicago = DateUtils.ical4jTimeZone("America/Chicago")!!
Expand Down Expand Up @@ -643,7 +643,6 @@ class DmfsTaskTest(
}


@MediumTest
@Test
fun testAddTask() {
// build and write event to calendar provider
Expand Down Expand Up @@ -693,7 +692,6 @@ class DmfsTaskTest(
}
}

@MediumTest
@Test(expected = CalendarStorageException::class)
fun testAddTaskWithInvalidDue() {
val task = Task()
Expand All @@ -705,7 +703,21 @@ class DmfsTaskTest(
TestTask(taskList!!, task).add()
}

@MediumTest
@Test
fun testAddTaskWithManyAlarms() {
val task = Task()
task.uid = "TaskWithManyAlarms"
task.summary = "Task with many alarms"
task.dtStart = DtStart(Date("20150102"))

for (i in 1..1050)
task.alarms += VAlarm(java.time.Duration.ofMinutes(i.toLong()))

val uri = TestTask(taskList!!, task).add()
val task2 = taskList!!.findById(ContentUris.parseId(uri))
assertEquals(1050, task2.task?.alarms?.size)
}

@Test
fun testUpdateTask() {
// add test event without reminder
Expand Down Expand Up @@ -739,7 +751,6 @@ class DmfsTaskTest(
}
}

@MediumTest
@Test
fun testBuildAllDayTask() {
// add all-day event to calendar provider
Expand Down
46 changes: 34 additions & 12 deletions lib/src/main/kotlin/at/bitfire/ical4android/BatchOperation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ import java.util.logging.Level
import java.util.logging.Logger

class BatchOperation(
private val providerClient: ContentProviderClient
private val providerClient: ContentProviderClient,
private val maxOperationsPerYieldPoint: Int? = null
) {


companion object {

/** Maximum number of operations per yield point in task providers that are based on SQLiteContentProvider. */
const val TASKS_OPERATIONS_PER_YIELD_POINT = 499

}

private val logger = Logger.getLogger(javaClass.name)

private val queue = LinkedList<CpoBuilder>()
Expand Down Expand Up @@ -96,7 +104,6 @@ class BatchOperation(

try {
val ops = toCPO(start, end)
logger.fine("Running ${ops.size} operations ($start .. ${end - 1})")
val partResults = providerClient.applyBatch(ops)

val n = end - start
Expand Down Expand Up @@ -133,6 +140,7 @@ class BatchOperation(
* 2. If a back reference points to a row outside of start/end,
* replace it by the actual result, which has already been calculated. */

var currentIdx = 0
for (cpoBuilder in queue.subList(start, end)) {
for ((backrefKey, backref) in cpoBuilder.valueBackrefs) {
val originalIdx = backref.originalIndex
Expand All @@ -148,17 +156,22 @@ class BatchOperation(
backref.setIndex(originalIdx - start)
}

// Set a possible yield point every MAX_OPERATIONS_PER_YIELD_POINT operations for SQLiteContentProvider
currentIdx += 1
if (maxOperationsPerYieldPoint != null && currentIdx.mod(maxOperationsPerYieldPoint) == 0)
cpoBuilder.withYieldAllowed()

cpo += cpoBuilder.build()
}
return cpo
}


class BackReference(
/** index of the referenced row in the original, nonsplitted transaction */
val originalIndex: Int
/** index of the referenced row in the original, non-splitted transaction */
val originalIndex: Int
) {
/** overriden index, i.e. index within the splitted transaction */
/** overridden index, i.e. index within the splitted transaction */
private var index: Int? = null

/**
Expand All @@ -182,8 +195,8 @@ class BatchOperation(
* value back references.
*/
class CpoBuilder private constructor(
val uri: Uri,
val type: Type
val uri: Uri,
val type: Type
) {

enum class Type { INSERT, UPDATE, DELETE }
Expand All @@ -197,11 +210,13 @@ class BatchOperation(
}


var selection: String? = null
var selectionArguments: Array<String>? = null
private var selection: String? = null
private var selectionArguments: Array<String>? = null

internal val values = mutableMapOf<String, Any?>()
internal val valueBackrefs = mutableMapOf<String, BackReference>()

val values = mutableMapOf<String, Any?>()
val valueBackrefs = mutableMapOf<String, BackReference>()
private var yieldAllowed = false


fun withSelection(select: String, args: Array<String>): CpoBuilder {
Expand All @@ -226,6 +241,10 @@ class BatchOperation(
return this
}

fun withYieldAllowed() {
yieldAllowed = true
}


fun build(): ContentProviderOperation {
val builder = when (type) {
Expand All @@ -242,6 +261,9 @@ class BatchOperation(
for ((key, backref) in valueBackrefs)
builder.withValueBackReference(key, backref.getIndex())

if (yieldAllowed)
builder.withYieldAllowed(true)

return builder.build()
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/main/kotlin/at/bitfire/ical4android/DmfsTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ abstract class DmfsTask(


fun add(): Uri {
val batch = BatchOperation(taskList.provider)
val batch = BatchOperation(taskList.provider, BatchOperation.TASKS_OPERATIONS_PER_YIELD_POINT)

val builder = CpoBuilder.newInsert(taskList.tasksSyncUri())
buildTask(builder, false)
Expand All @@ -350,7 +350,7 @@ abstract class DmfsTask(
this.task = task
val existingId = requireNotNull(id)

val batch = BatchOperation(taskList.provider)
val batch = BatchOperation(taskList.provider, BatchOperation.TASKS_OPERATIONS_PER_YIELD_POINT)

// remove associated rows which are added later again
batch.enqueue(CpoBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ abstract class DmfsTaskList<out T : DmfsTask>(
*/
fun touchRelations(): Int {
logger.fine("Touching relations to set parent_id")
val batchOperation = BatchOperation(provider)
val batchOperation = BatchOperation(provider, BatchOperation.TASKS_OPERATIONS_PER_YIELD_POINT)
provider.query(
tasksSyncUri(true), null,
"${Tasks.LIST_ID}=? AND ${Tasks.PARENT_ID} IS NULL AND ${Relation.MIMETYPE}=? AND ${Relation.RELATED_ID} IS NOT NULL",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package at.bitfire.ical4android.validation

import androidx.annotation.VisibleForTesting
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.Ical4Android
import at.bitfire.ical4android.util.DateUtils
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDate
import at.bitfire.ical4android.util.TimeApiExtensions.toLocalDate
Expand Down Expand Up @@ -47,7 +46,7 @@ object EventValidator {
val dtStart = correctStartAndEndTime(event)
sameTypeForDtStartAndRruleUntil(dtStart, event.rRules)
removeRRulesWithUntilBeforeDtStart(dtStart, event.rRules)
removeRRulesOfExceptions(event.exceptions)
removeRecurrenceOfExceptions(event.exceptions)
}


Expand Down Expand Up @@ -169,15 +168,20 @@ object EventValidator {


/**
* Removes RRULEs of exceptions of (potentially recurring) events
* Note: This repair step needs to be applied after all exceptions have been found
* Removes all recurrence information of exceptions of (potentially recurring) events. This is:
* `RRULE`, `RDATE` and `EXDATE`.
* Note: This repair step needs to be applied after all exceptions have been found.
*
* @param exceptions exceptions of an event
*/
@VisibleForTesting
internal fun removeRRulesOfExceptions(exceptions: List<Event>) {
for (exception in exceptions)
exception.rRules.clear() // Drop all RRULEs for the exception
internal fun removeRecurrenceOfExceptions(exceptions: List<Event>) {
for (exception in exceptions) {
// Drop all RRULEs, RDATEs, EXDATEs for the exception
exception.rRules.clear()
exception.rDates.clear()
exception.exDates.clear()
}
}


Expand Down
Loading

0 comments on commit f6fcfee

Please sign in to comment.