@@ -9,56 +9,48 @@ package at.bitfire.synctools.mapping.tasks
99import android.content.ContentValues
1010import android.content.Entity
1111import at.bitfire.ical4android.Task
12- import at.bitfire.ical4android.UnknownProperty
12+ import at.bitfire.synctools.mapping.tasks.builder.AlarmsBuilder
1313import at.bitfire.synctools.mapping.tasks.builder.AllDayBuilder
14+ import at.bitfire.synctools.mapping.tasks.builder.CategoriesBuilder
1415import at.bitfire.synctools.mapping.tasks.builder.ClassificationBuilder
1516import at.bitfire.synctools.mapping.tasks.builder.ColorBuilder
17+ import at.bitfire.synctools.mapping.tasks.builder.CommentsBuilder
1618import at.bitfire.synctools.mapping.tasks.builder.CompletedBuilder
19+ import at.bitfire.synctools.mapping.tasks.builder.CreatedBuilder
1720import at.bitfire.synctools.mapping.tasks.builder.DescriptionBuilder
1821import at.bitfire.synctools.mapping.tasks.builder.DirtyBuilder
1922import at.bitfire.synctools.mapping.tasks.builder.DmfsTaskFieldBuilder
2023import at.bitfire.synctools.mapping.tasks.builder.DueBuilder
2124import at.bitfire.synctools.mapping.tasks.builder.DurationBuilder
2225import at.bitfire.synctools.mapping.tasks.builder.ETagBuilder
2326import at.bitfire.synctools.mapping.tasks.builder.GeoBuilder
27+ import at.bitfire.synctools.mapping.tasks.builder.LastModifiedBuilder
28+ import at.bitfire.synctools.mapping.tasks.builder.ListIdBuilder
2429import at.bitfire.synctools.mapping.tasks.builder.LocationBuilder
2530import at.bitfire.synctools.mapping.tasks.builder.OrganizerBuilder
2631import at.bitfire.synctools.mapping.tasks.builder.PercentCompleteBuilder
2732import at.bitfire.synctools.mapping.tasks.builder.PriorityBuilder
2833import at.bitfire.synctools.mapping.tasks.builder.RecurrenceFieldsBuilder
34+ import at.bitfire.synctools.mapping.tasks.builder.RelationsBuilder
2935import at.bitfire.synctools.mapping.tasks.builder.SequenceBuilder
3036import at.bitfire.synctools.mapping.tasks.builder.StartTimeBuilder
3137import at.bitfire.synctools.mapping.tasks.builder.StatusBuilder
3238import at.bitfire.synctools.mapping.tasks.builder.SyncFlagsBuilder
3339import at.bitfire.synctools.mapping.tasks.builder.SyncIdBuilder
3440import at.bitfire.synctools.mapping.tasks.builder.TitleBuilder
3541import at.bitfire.synctools.mapping.tasks.builder.UidBuilder
42+ import at.bitfire.synctools.mapping.tasks.builder.UnknownPropertiesBuilder
3643import at.bitfire.synctools.mapping.tasks.builder.UrlBuilder
3744import at.bitfire.synctools.storage.BatchOperation.CpoBuilder
38- import at.bitfire.synctools.storage.tasks.DmfsTask.Companion.UNKNOWN_PROPERTY_DATA
3945import at.bitfire.synctools.storage.tasks.DmfsTaskList
4046import at.bitfire.synctools.storage.tasks.TasksBatchOperation
41- import at.bitfire.synctools.util.AlarmTriggerCalculator
42- import net.fortuna.ical4j.model.Parameter
43- import net.fortuna.ical4j.model.Property
44- import net.fortuna.ical4j.model.parameter.RelType
45- import net.fortuna.ical4j.model.parameter.Related
46- import net.fortuna.ical4j.model.property.Action
47- import net.fortuna.ical4j.model.property.immutable.ImmutableAction
4847import org.dmfs.tasks.contract.TaskContract.Properties
49- import org.dmfs.tasks.contract.TaskContract.Property.Alarm
50- import org.dmfs.tasks.contract.TaskContract.Property.Category
51- import org.dmfs.tasks.contract.TaskContract.Property.Comment
52- import org.dmfs.tasks.contract.TaskContract.Property.Relation
5348import org.dmfs.tasks.contract.TaskContract.Tasks
54- import java.util.Locale
5549import java.util.logging.Level
5650import java.util.logging.Logger
57- import kotlin.jvm.optionals.getOrNull
5851
5952/* *
60- * Writes [at.bitfire.ical4android.Task] to dmfs task provider data rows
61- * (former DmfsTask "build..." methods).
53+ * Writes [at.bitfire.ical4android.Task] to dmfs task provider data rows.
6254 */
6355class DmfsTaskBuilder (
6456 private val taskList : DmfsTaskList ,
@@ -71,20 +63,20 @@ class DmfsTaskBuilder(
7163 private val flags : Int ,
7264) {
7365
66+ private val logger
67+ get() = Logger .getLogger(javaClass.name)
68+
7469 private val fieldBuilders: Array <DmfsTaskFieldBuilder > = arrayOf(
7570 // main task row fields
7671 UidBuilder (),
7772 SyncIdBuilder (syncId),
7873 ETagBuilder (eTag),
7974 SyncFlagsBuilder (flags),
8075 SequenceBuilder (),
76+ ListIdBuilder (taskList.id),
8177 DirtyBuilder (),
82- // status fields
83- PriorityBuilder (),
84- ClassificationBuilder (),
85- StatusBuilder (),
86- CompletedBuilder (),
87- PercentCompleteBuilder (),
78+ CreatedBuilder (),
79+ LastModifiedBuilder (),
8880 // content fields
8981 TitleBuilder (),
9082 DescriptionBuilder (),
@@ -93,162 +85,73 @@ class DmfsTaskBuilder(
9385 ColorBuilder (),
9486 UrlBuilder (),
9587 OrganizerBuilder (),
96- // time fields and recurrence
88+ // status fields
89+ PriorityBuilder (),
90+ ClassificationBuilder (),
91+ StatusBuilder (),
92+ CompletedBuilder (),
93+ PercentCompleteBuilder (),
94+ // time fields
9795 AllDayBuilder (),
9896 StartTimeBuilder (),
9997 DueBuilder (),
10098 DurationBuilder (),
99+ // recurrence
101100 RecurrenceFieldsBuilder (),
102- // property sub-rows (still inline below via insertProperties)
101+ // property sub-rows
102+ AlarmsBuilder (taskList),
103+ CategoriesBuilder (taskList),
104+ CommentsBuilder (taskList),
105+ RelationsBuilder (taskList),
106+ UnknownPropertiesBuilder (taskList),
103107 )
104108
105- private val logger
106- get() = Logger .getLogger(javaClass.name)
107-
108- fun addRows (batch : TasksBatchOperation ): Int {
109- val builder = CpoBuilder .newInsert(taskList.tasksUri())
110- buildTask(builder, false )
111- val idxTask = batch.nextBackrefIdx() // Get nextBackrefIdx BEFORE adding builder to batch
112- batch + = builder
113- return idxTask
114- }
115-
116- fun updateRows (batch : TasksBatchOperation ) {
117- val id = requireNotNull(id)
118- val builder = CpoBuilder .newUpdate(taskList.taskUri(id))
119- buildTask(builder, true )
120- batch + = builder
121- }
122-
123- private fun buildTask (builder : CpoBuilder , update : Boolean ) {
124- if (! update)
125- builder .withValue(Tasks .LIST_ID , taskList.id)
126109
127- // new builders
110+ fun addRows (batch : TasksBatchOperation ) {
111+ val entity = buildTask()
128112
129- val entity = Entity (ContentValues ())
130- for (fieldBuilder in fieldBuilders)
131- fieldBuilder.build(task, entity)
132- builder.withValues(entity.entityValues)
133-
134- // old builders
135-
136- // parent_id will be re-calculated when the relation row is inserted (if there is any)
137- .withValue(Tasks .PARENT_ID , null )
113+ val mainBuilder = CpoBuilder .newInsert(taskList.tasksUri())
114+ .withValues(entity.entityValues)
115+ val idxTask = batch.nextBackrefIdx() // Get nextBackrefIdx BEFORE adding builder to batch
116+ batch + = mainBuilder
138117
139- builder
140- .withValue(Tasks .CREATED , task.createdAt)
141- .withValue(Tasks .LAST_MODIFIED , task.lastModified)
118+ for (subValue in entity.subValues)
119+ batch + = CpoBuilder .newInsert(subValue.uri)
120+ .withValues(subValue.values)
121+ .withValueBackReference(Properties .TASK_ID , idxTask)
142122
143- logger.log(Level .FINE , " Built task object " , builder .build())
123+ logger.log(Level .FINE , " Added task" , mainBuilder .build())
144124 }
145125
146- fun insertProperties (batch : TasksBatchOperation , idxTask : Int? ) {
147- insertAlarms(batch, idxTask)
148- insertCategories(batch, idxTask)
149- insertComment(batch, idxTask)
150- insertRelatedTo(batch, idxTask)
151- insertUnknownProperties(batch, idxTask)
152- }
126+ fun updateRows (batch : TasksBatchOperation ) {
127+ val existingId = requireNotNull(id)
128+ val entity = buildTask()
153129
154- private fun insertAlarms (batch : TasksBatchOperation , idxTask : Int? ) {
155- for (alarm in task.alarms) {
156- val (alarmRef, minutes) = AlarmTriggerCalculator .alarmTriggerToMinutes(
157- alarm = alarm,
158- refStart = task.dtStart,
159- refEnd = task.end,
160- allowRelEnd = true
161- ) ? : continue
162- val ref = when (alarmRef) {
163- Related .END ->
164- Alarm .ALARM_REFERENCE_DUE_DATE
165- else /* Related.START is the default value */ ->
166- Alarm .ALARM_REFERENCE_START_DATE
167- }
168-
169- val alarmType = when (
170- alarm.getProperty<Action >(Property .ACTION ).getOrNull()?.value?.uppercase(Locale .ROOT )
171- ) {
172- ImmutableAction .VALUE_AUDIO -> Alarm .ALARM_TYPE_SOUND
173- ImmutableAction .VALUE_DISPLAY -> Alarm .ALARM_TYPE_MESSAGE
174- ImmutableAction .VALUE_EMAIL -> Alarm .ALARM_TYPE_EMAIL
175- else -> Alarm .ALARM_TYPE_NOTHING
176- }
177-
178- val builder = CpoBuilder
179- .newInsert(taskList.tasksPropertiesUri())
180- .withTaskId(Alarm .TASK_ID , idxTask)
181- .withValue(Alarm .MIMETYPE , Alarm .CONTENT_ITEM_TYPE )
182- .withValue(Alarm .MINUTES_BEFORE , minutes)
183- .withValue(Alarm .REFERENCE , ref)
184- .withValue(Alarm .MESSAGE , alarm.description?.value ? : alarm.summary)
185- .withValue(Alarm .ALARM_TYPE , alarmType)
186-
187- logger.log(Level .FINE , " Inserting alarm" , builder.build())
188- batch + = builder
130+ val mainValues = ContentValues (entity.entityValues).apply {
131+ // LIST_ID must not be updated (it doesn't change for updates, and setting it would cause issues)
132+ remove(Tasks .LIST_ID )
189133 }
190- }
134+ val mainBuilder = CpoBuilder .newUpdate(taskList.taskUri(existingId))
135+ .withValues(mainValues)
136+ batch + = mainBuilder
191137
192- private fun insertCategories (batch : TasksBatchOperation , idxTask : Int? ) {
193- for (category in task.categories) {
194- val builder = CpoBuilder .newInsert(taskList.tasksPropertiesUri())
195- .withTaskId(Category .TASK_ID , idxTask)
196- .withValue(Category .MIMETYPE , Category .CONTENT_ITEM_TYPE )
197- .withValue(Category .CATEGORY_NAME , category)
198- logger.log(Level .FINE , " Inserting category" , builder.build())
199- batch + = builder
200- }
201- }
138+ for (subValue in entity.subValues)
139+ batch + = CpoBuilder .newInsert(subValue.uri)
140+ .withValues(ContentValues (subValue.values).apply {
141+ put(Properties .TASK_ID , existingId)
142+ })
202143
203- private fun insertComment (batch : TasksBatchOperation , idxTask : Int? ) {
204- val comment = task.comment ? : return
205- val builder = CpoBuilder .newInsert(taskList.tasksPropertiesUri())
206- .withTaskId(Comment .TASK_ID , idxTask)
207- .withValue(Comment .MIMETYPE , Comment .CONTENT_ITEM_TYPE )
208- .withValue(Comment .COMMENT , comment)
209- logger.log(Level .FINE , " Inserting comment" , builder.build())
210- batch + = builder
144+ logger.log(Level .FINE , " Updated task" , mainBuilder.build())
211145 }
212146
213- private fun insertRelatedTo (batch : TasksBatchOperation , idxTask : Int? ) {
214- for (relatedTo in task.relatedTo) {
215- val relType = when ((relatedTo.getParameter<RelType >(Parameter .RELTYPE )).getOrNull()) {
216- RelType .CHILD -> Relation .RELTYPE_CHILD
217- RelType .SIBLING -> Relation .RELTYPE_SIBLING
218- else /* RelType.PARENT, default value */ -> Relation .RELTYPE_PARENT
219- }
220- val builder = CpoBuilder .newInsert(taskList.tasksPropertiesUri())
221- .withTaskId(Relation .TASK_ID , idxTask)
222- .withValue(Relation .MIMETYPE , Relation .CONTENT_ITEM_TYPE )
223- .withValue(Relation .RELATED_UID , relatedTo.value)
224- .withValue(Relation .RELATED_TYPE , relType)
225- logger.log(Level .FINE , " Inserting relation" , builder.build())
226- batch + = builder
227- }
228- }
147+ private fun buildTask (): Entity {
148+ val entity = Entity (ContentValues ())
229149
230- private fun insertUnknownProperties (batch : TasksBatchOperation , idxTask : Int? ) {
231- for (property in task.unknownProperties) {
232- if (property.value.length > UnknownProperty .MAX_UNKNOWN_PROPERTY_SIZE ) {
233- logger.warning(" Ignoring unknown property with ${property.value.length} octets (too long)" )
234- return
235- }
236-
237- val builder = CpoBuilder .newInsert(taskList.tasksPropertiesUri())
238- .withTaskId(Properties .TASK_ID , idxTask)
239- .withValue(Properties .MIMETYPE , UnknownProperty .CONTENT_ITEM_TYPE )
240- .withValue(UNKNOWN_PROPERTY_DATA , UnknownProperty .toJsonString(property))
241- logger.log(Level .FINE , " Inserting unknown property" , builder.build())
242- batch + = builder
243- }
244- }
150+ for (fieldBuilder in fieldBuilders)
151+ fieldBuilder.build(task, entity)
245152
246- private fun CpoBuilder.withTaskId (column : String , idxTask : Int? ): CpoBuilder {
247- if (idxTask != null )
248- withValueBackReference(column, idxTask)
249- else
250- withValue(column, requireNotNull(id))
251- return this
153+ logger.log(Level .FINE , " Built task" , entity.entityValues)
154+ return entity
252155 }
253156
254157}
0 commit comments