diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/DmfsTaskProcessor.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/DmfsTaskProcessor.kt index 67a4c220..5218c7c9 100644 --- a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/DmfsTaskProcessor.kt +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/DmfsTaskProcessor.kt @@ -11,11 +11,17 @@ import at.bitfire.ical4android.Task import at.bitfire.ical4android.UnknownProperty import at.bitfire.ical4android.util.TimeApiExtensions.toLocalDate import at.bitfire.synctools.mapping.tasks.handler.AlarmsHandler +import at.bitfire.synctools.mapping.tasks.handler.ColorHandler +import at.bitfire.synctools.mapping.tasks.handler.DescriptionHandler import at.bitfire.synctools.mapping.tasks.handler.DmfsTaskFieldHandler import at.bitfire.synctools.mapping.tasks.handler.DmfsTaskPropertyHandler +import at.bitfire.synctools.mapping.tasks.handler.GeoHandler +import at.bitfire.synctools.mapping.tasks.handler.LocationHandler +import at.bitfire.synctools.mapping.tasks.handler.OrganizerHandler import at.bitfire.synctools.mapping.tasks.handler.SequenceHandler import at.bitfire.synctools.mapping.tasks.handler.TitleHandler import at.bitfire.synctools.mapping.tasks.handler.UidHandler +import at.bitfire.synctools.mapping.tasks.handler.UrlHandler import at.bitfire.synctools.storage.tasks.DmfsTask.Companion.UNKNOWN_PROPERTY_DATA import at.bitfire.synctools.storage.tasks.DmfsTaskList import at.bitfire.synctools.util.AndroidTimeUtils @@ -27,8 +33,6 @@ import net.fortuna.ical4j.model.property.DtStart import net.fortuna.ical4j.model.property.Due import net.fortuna.ical4j.model.property.Duration import net.fortuna.ical4j.model.property.ExDate -import net.fortuna.ical4j.model.property.Geo -import net.fortuna.ical4j.model.property.Organizer import net.fortuna.ical4j.model.property.RDate import net.fortuna.ical4j.model.property.RRule import net.fortuna.ical4j.model.property.RelatedTo @@ -39,7 +43,6 @@ import org.dmfs.tasks.contract.TaskContract.Property.Category import org.dmfs.tasks.contract.TaskContract.Property.Comment import org.dmfs.tasks.contract.TaskContract.Property.Relation import org.dmfs.tasks.contract.TaskContract.Tasks -import java.net.URISyntaxException import java.time.Instant import java.time.temporal.Temporal import java.util.logging.Level @@ -57,6 +60,12 @@ class DmfsTaskProcessor( UidHandler(), TitleHandler(), SequenceHandler(), + DescriptionHandler(), + LocationHandler(), + GeoHandler(), + ColorHandler(), + UrlHandler(), + OrganizerHandler(), ) private val propertyHandlers: Map = mapOf( @@ -70,30 +79,8 @@ class DmfsTaskProcessor( for (handler in fieldHandlers) handler.process(values, to) - to.location = values.getAsString(Tasks.LOCATION) to.userAgents += taskList.providerName.packageName - values.getAsString(Tasks.GEO)?.takeIf { it.contains(",") }?.let { geo -> - val (lng, lat) = geo.split(',') - try { - to.geoPosition = Geo(lat.toBigDecimal(), lng.toBigDecimal()) - } catch (e: NumberFormatException) { - logger.log(Level.WARNING, "Invalid GEO value: $geo", e) - } - } - - to.description = values.getAsString(Tasks.DESCRIPTION) - to.color = values.getAsInteger(Tasks.TASK_COLOR) - to.url = values.getAsString(Tasks.URL) - - values.getAsString(Tasks.ORGANIZER)?.let { - try { - to.organizer = Organizer("mailto:$it") - } catch(e: URISyntaxException) { - logger.log(Level.WARNING, "Invalid ORGANIZER email", e) - } - } - values.getAsInteger(Tasks.PRIORITY)?.let { to.priority = it } // Note: big method – maybe split? Depends on how we want to proceed with refactoring. diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/ColorHandler.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/ColorHandler.kt new file mode 100644 index 00000000..f8f8e8af --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/ColorHandler.kt @@ -0,0 +1,19 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks + +class ColorHandler : DmfsTaskFieldHandler { + + override fun process(from: ContentValues, to: Task) { + to.color = from.getAsInteger(Tasks.TASK_COLOR) + } + +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/DescriptionHandler.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/DescriptionHandler.kt new file mode 100644 index 00000000..c0e227aa --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/DescriptionHandler.kt @@ -0,0 +1,19 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks + +class DescriptionHandler : DmfsTaskFieldHandler { + + override fun process(from: ContentValues, to: Task) { + to.description = from.getAsString(Tasks.DESCRIPTION) + } + +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/GeoHandler.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/GeoHandler.kt new file mode 100644 index 00000000..3483cffd --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/GeoHandler.kt @@ -0,0 +1,34 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import at.bitfire.ical4android.Task +import net.fortuna.ical4j.model.property.Geo +import org.dmfs.tasks.contract.TaskContract.Tasks +import java.util.logging.Level +import java.util.logging.Logger + +class GeoHandler : DmfsTaskFieldHandler { + + private val logger + get() = Logger.getLogger(javaClass.name) + + override fun process(from: ContentValues, to: Task) { + val geo = from.getAsString(Tasks.GEO) ?: return + val commaIdx = geo.indexOf(',') + if (commaIdx < 0) return + val lng = geo.substring(0, commaIdx) + val lat = geo.substring(commaIdx + 1) + try { + to.geoPosition = Geo(lat.toBigDecimal(), lng.toBigDecimal()) + } catch (e: NumberFormatException) { + logger.log(Level.WARNING, "Invalid GEO value: $geo", e) + } + } + +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/LocationHandler.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/LocationHandler.kt new file mode 100644 index 00000000..d0459e95 --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/LocationHandler.kt @@ -0,0 +1,19 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks + +class LocationHandler : DmfsTaskFieldHandler { + + override fun process(from: ContentValues, to: Task) { + to.location = from.getAsString(Tasks.LOCATION) + } + +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/OrganizerHandler.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/OrganizerHandler.kt new file mode 100644 index 00000000..fcee2b3e --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/OrganizerHandler.kt @@ -0,0 +1,31 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import at.bitfire.ical4android.Task +import net.fortuna.ical4j.model.property.Organizer +import org.dmfs.tasks.contract.TaskContract.Tasks +import java.net.URISyntaxException +import java.util.logging.Level +import java.util.logging.Logger + +class OrganizerHandler : DmfsTaskFieldHandler { + + private val logger + get() = Logger.getLogger(javaClass.name) + + override fun process(from: ContentValues, to: Task) { + val email = from.getAsString(Tasks.ORGANIZER) ?: return + try { + to.organizer = Organizer("mailto:$email") + } catch (e: URISyntaxException) { + logger.log(Level.WARNING, "Invalid ORGANIZER email", e) + } + } + +} diff --git a/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/UrlHandler.kt b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/UrlHandler.kt new file mode 100644 index 00000000..65f0fb21 --- /dev/null +++ b/lib/src/main/kotlin/at/bitfire/synctools/mapping/tasks/handler/UrlHandler.kt @@ -0,0 +1,19 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks + +class UrlHandler : DmfsTaskFieldHandler { + + override fun process(from: ContentValues, to: Task) { + to.url = from.getAsString(Tasks.URL) + } + +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/ColorHandlerTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/ColorHandlerTest.kt new file mode 100644 index 00000000..77b88bdf --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/ColorHandlerTest.kt @@ -0,0 +1,38 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class ColorHandlerTest { + + private val handler = ColorHandler() + + @Test + fun `No COLOR`() { + val task = Task() + handler.process(ContentValues(), task) + assertNull(task.color) + } + + @Test + fun `COLOR set`() { + val task = Task() + handler.process(contentValuesOf(Tasks.TASK_COLOR to 0xFF112233.toInt()), task) + assertEquals(0xFF112233.toInt(), task.color) + } + +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/DescriptionHandlerTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/DescriptionHandlerTest.kt new file mode 100644 index 00000000..5259b804 --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/DescriptionHandlerTest.kt @@ -0,0 +1,38 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DescriptionHandlerTest { + + private val handler = DescriptionHandler() + + @Test + fun `No DESCRIPTION`() { + val task = Task() + handler.process(ContentValues(), task) + assertNull(task.description) + } + + @Test + fun `DESCRIPTION set`() { + val task = Task() + handler.process(contentValuesOf(Tasks.DESCRIPTION to "Task details"), task) + assertEquals("Task details", task.description) + } + +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/GeoHandlerTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/GeoHandlerTest.kt new file mode 100644 index 00000000..0658e032 --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/GeoHandlerTest.kt @@ -0,0 +1,60 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import at.bitfire.ical4android.Task +import net.fortuna.ical4j.model.property.Geo +import org.dmfs.tasks.contract.TaskContract.Tasks +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class GeoHandlerTest { + + private val handler = GeoHandler() + + @Test + fun `No GEO`() { + val task = Task() + handler.process(ContentValues(), task) + assertNull(task.geoPosition) + } + + @Test + fun `GEO is set`() { + val task = Task() + handler.process(contentValuesOf(Tasks.GEO to "16.3,48.2"), task) + assertEquals(Geo(48.2.toBigDecimal(), 16.3.toBigDecimal()), task.geoPosition) + } + + @Test + fun `GEO with trailing comma is ignored`() { + val task = Task() + handler.process(contentValuesOf(Tasks.GEO to "16.3,"), task) + assertNull(task.geoPosition) + } + + @Test + fun `GEO without comma is ignored`() { + val task = Task() + handler.process(contentValuesOf(Tasks.GEO to "invalid"), task) + assertNull(task.geoPosition) + } + + @Test + fun `GEO with invalid number is ignored`() { + val task = Task() + handler.process(contentValuesOf(Tasks.GEO to "not,a-number"), task) + assertNull(task.geoPosition) + } + +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/LocationHandlerTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/LocationHandlerTest.kt new file mode 100644 index 00000000..7c5eb194 --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/LocationHandlerTest.kt @@ -0,0 +1,38 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class LocationHandlerTest { + + private val handler = LocationHandler() + + @Test + fun `No LOCATION`() { + val task = Task() + handler.process(ContentValues(), task) + assertNull(task.location) + } + + @Test + fun `LOCATION set`() { + val task = Task() + handler.process(contentValuesOf(Tasks.LOCATION to "Vienna, Austria"), task) + assertEquals("Vienna, Austria", task.location) + } + +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/OrganizerHandlerTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/OrganizerHandlerTest.kt new file mode 100644 index 00000000..d5bfe96d --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/OrganizerHandlerTest.kt @@ -0,0 +1,39 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import at.bitfire.ical4android.Task +import net.fortuna.ical4j.model.property.Organizer +import org.dmfs.tasks.contract.TaskContract.Tasks +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class OrganizerHandlerTest { + + private val handler = OrganizerHandler() + + @Test + fun `No ORGANIZER`() { + val task = Task() + handler.process(ContentValues(), task) + assertNull(task.organizer) + } + + @Test + fun `ORGANIZER is email address`() { + val task = Task() + handler.process(contentValuesOf(Tasks.ORGANIZER to "organizer@example.com"), task) + assertEquals(Organizer("mailto:organizer@example.com"), task.organizer) + } + +} diff --git a/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/UrlHandlerTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/UrlHandlerTest.kt new file mode 100644 index 00000000..a6160c07 --- /dev/null +++ b/lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/handler/UrlHandlerTest.kt @@ -0,0 +1,38 @@ +/* + * This file is part of bitfireAT/synctools which is released under GPLv3. + * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package at.bitfire.synctools.mapping.tasks.handler + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import at.bitfire.ical4android.Task +import org.dmfs.tasks.contract.TaskContract.Tasks +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class UrlHandlerTest { + + private val handler = UrlHandler() + + @Test + fun `No URL`() { + val task = Task() + handler.process(ContentValues(), task) + assertNull(task.url) + } + + @Test + fun `URL set`() { + val task = Task() + handler.process(contentValuesOf(Tasks.URL to "https://example.com"), task) + assertEquals("https://example.com", task.url) + } + +}