From 067317b5a0be42d0c352ec2cc5d15eea7b2cca52 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 27 Apr 2023 10:00:55 -0500 Subject: [PATCH] Migrate google task filters --- .../astrid/api/MultipleSelectCriterion.java | 2 +- .../todoroo/astrid/core/CriterionInstance.kt | 46 ------- .../todoroo/astrid/service/Upgrade_13_2.kt | 30 +++++ .../com/todoroo/astrid/service/Upgrader.kt | 20 ++- .../activities/FilterSettingsActivity.kt | 114 +++++++++--------- .../tasks/filters/FilterCriteriaProvider.kt | 61 +++++++++- 6 files changed, 162 insertions(+), 111 deletions(-) create mode 100644 app/src/main/java/com/todoroo/astrid/service/Upgrade_13_2.kt diff --git a/app/src/main/java/com/todoroo/astrid/api/MultipleSelectCriterion.java b/app/src/main/java/com/todoroo/astrid/api/MultipleSelectCriterion.java index 5924d8bfc..76250aa10 100644 --- a/app/src/main/java/com/todoroo/astrid/api/MultipleSelectCriterion.java +++ b/app/src/main/java/com/todoroo/astrid/api/MultipleSelectCriterion.java @@ -20,7 +20,7 @@ public class MultipleSelectCriterion extends CustomFilterCriterion implements Pa /** Parcelable Creator Object */ public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + new Parcelable.Creator<>() { /** {@inheritDoc} */ @Override diff --git a/app/src/main/java/com/todoroo/astrid/core/CriterionInstance.kt b/app/src/main/java/com/todoroo/astrid/core/CriterionInstance.kt index 921cb3f5f..d20cbbe6a 100644 --- a/app/src/main/java/com/todoroo/astrid/core/CriterionInstance.kt +++ b/app/src/main/java/com/todoroo/astrid/core/CriterionInstance.kt @@ -6,10 +6,6 @@ import com.todoroo.astrid.api.CustomFilterCriterion import com.todoroo.astrid.api.MultipleSelectCriterion import com.todoroo.astrid.api.TextInputCriterion import com.todoroo.astrid.helper.UUIDHelper -import org.tasks.Strings.isNullOrEmpty -import org.tasks.filters.FilterCriteriaProvider -import timber.log.Timber -import java.util.* class CriterionInstance { lateinit var criterion: CustomFilterCriterion @@ -121,54 +117,12 @@ class CriterionInstance { const val TYPE_INTERSECT = 2 const val TYPE_UNIVERSE = 3 - suspend fun fromString( - provider: FilterCriteriaProvider, criterion: String): List { - if (isNullOrEmpty(criterion)) { - return emptyList() - } - val entries: MutableList = ArrayList() - for (row in criterion.trim().split("\n")) { - val split = row - .split(AndroidUtilities.SERIALIZATION_SEPARATOR) - .map { unescape(it) } - if (split.size != 4 && split.size != 5) { - Timber.e("invalid row: %s", row) - return emptyList() - } - val entry = CriterionInstance() - entry.criterion = provider.getFilterCriteria(split[0]) - val value = split[1] - if (entry.criterion is TextInputCriterion) { - entry.selectedText = value - } else if (entry.criterion is MultipleSelectCriterion) { - val multipleSelectCriterion = entry.criterion as MultipleSelectCriterion? - if (multipleSelectCriterion!!.entryValues != null) { - entry.selectedIndex = multipleSelectCriterion.entryValues.indexOf(value) - } - } else { - Timber.d("Ignored value %s for %s", value, entry.criterion) - } - entry.type = split[3].toInt() - entry.criterion.sql = split[4] - Timber.d("%s -> %s", row, entry) - entries.add(entry) - } - return entries - } - private fun escape(item: String?): String { return item?.replace( AndroidUtilities.SERIALIZATION_SEPARATOR, AndroidUtilities.SEPARATOR_ESCAPE) ?: "" // $NON-NLS-1$ } - private fun unescape(item: String?): String { - return if (isNullOrEmpty(item)) { - "" - } else item!!.replace( - AndroidUtilities.SEPARATOR_ESCAPE, AndroidUtilities.SERIALIZATION_SEPARATOR) - } - fun serialize(criterion: List): String { return criterion .joinToString("\n") { it.serialize() } diff --git a/app/src/main/java/com/todoroo/astrid/service/Upgrade_13_2.kt b/app/src/main/java/com/todoroo/astrid/service/Upgrade_13_2.kt new file mode 100644 index 000000000..43b549c3b --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/service/Upgrade_13_2.kt @@ -0,0 +1,30 @@ +@file:Suppress("ClassName") + +package com.todoroo.astrid.service + +import com.todoroo.astrid.core.CriterionInstance +import org.tasks.activities.FilterSettingsActivity.Companion.sql +import org.tasks.data.Filter +import org.tasks.data.FilterDao +import org.tasks.filters.FilterCriteriaProvider +import javax.inject.Inject + +class Upgrade_13_2 @Inject constructor( + private val filterDao: FilterDao, + private val filterCriteriaProvider: FilterCriteriaProvider, +) { + internal suspend fun rebuildFilters() = + filterDao.getFilters().forEach { rebuildFilter(it) } + + private suspend fun rebuildFilter(filter: Filter) { + val serialized = filter.criterion?.takeIf { it.isNotBlank() } + val criterion = filterCriteriaProvider.fromString(serialized) + filter.setSql(criterion.sql) + filter.criterion = CriterionInstance.serialize(criterion) + filterDao.update(filter) + } + + companion object { + const val VERSION = 130200 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt b/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt index 86c187a16..bb6d36d0e 100644 --- a/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt +++ b/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt @@ -19,7 +19,20 @@ import org.tasks.caldav.iCalendar import org.tasks.caldav.iCalendar.Companion.fromVtodo import org.tasks.caldav.iCalendar.Companion.order import org.tasks.caldav.iCalendar.Companion.parent -import org.tasks.data.* +import org.tasks.data.CaldavDao +import org.tasks.data.CaldavTask +import org.tasks.data.CaldavTaskContainer +import org.tasks.data.FilterDao +import org.tasks.data.GoogleTaskListDao +import org.tasks.data.Location +import org.tasks.data.LocationDao +import org.tasks.data.Tag +import org.tasks.data.TagDao +import org.tasks.data.TagData +import org.tasks.data.TagDataDao +import org.tasks.data.TaskAttachmentDao +import org.tasks.data.UpgraderDao +import org.tasks.data.UserActivityDao import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.Preferences import org.tasks.widget.AppWidgetManager @@ -48,6 +61,7 @@ class Upgrader @Inject constructor( private val upgrade_11_3: Lazy, private val upgrade_11_12_3: Lazy, private val upgrade_12_4: Lazy, + private val upgrade_13_2: Lazy, ) { fun upgrade(from: Int, to: Int) { @@ -94,8 +108,9 @@ class Upgrader @Inject constructor( run(from, V12_6) { setInstallDetails(from) } - run(from, V13_2) { + run(from, Upgrade_13_2.VERSION) { caldavDao.updateParents() + upgrade_13_2.get().rebuildFilters() } preferences.setBoolean(R.string.p_just_updated, true) } else { @@ -340,7 +355,6 @@ class Upgrader @Inject constructor( const val V12_4 = 120400 const val V12_6 = 120601 const val V12_8 = 120800 - const val V13_2 = 130200 @JvmStatic fun getAndroidColor(context: Context, index: Int): Int { diff --git a/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt b/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt index 900b91dde..904819a4f 100644 --- a/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt +++ b/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt @@ -24,7 +24,12 @@ import com.todoroo.andlib.sql.UnaryCriterion import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.astrid.activity.MainActivity import com.todoroo.astrid.activity.TaskListFragment -import com.todoroo.astrid.api.* +import com.todoroo.astrid.api.BooleanCriterion +import com.todoroo.astrid.api.CustomFilter +import com.todoroo.astrid.api.CustomFilterCriterion +import com.todoroo.astrid.api.MultipleSelectCriterion +import com.todoroo.astrid.api.PermaSql +import com.todoroo.astrid.api.TextInputCriterion import com.todoroo.astrid.core.CriterionInstance import com.todoroo.astrid.core.CustomFilterAdapter import com.todoroo.astrid.core.CustomFilterItemTouchHelper @@ -42,7 +47,7 @@ import org.tasks.db.QueryUtils import org.tasks.extensions.Context.openUri import org.tasks.filters.FilterCriteriaProvider import org.tasks.themes.CustomIcons -import java.util.* +import java.util.Locale import javax.inject.Inject import kotlin.math.max @@ -73,17 +78,20 @@ class FilterSettingsActivity : BaseListSettingsActivity() { } when { savedInstanceState != null -> lifecycleScope.launch { - setCriteria(CriterionInstance.fromString( - filterCriteriaProvider, savedInstanceState.getString(EXTRA_CRITERIA)!!)) + setCriteria( + filterCriteriaProvider.fromString( + savedInstanceState.getString(EXTRA_CRITERIA)!! + ) + ) } filter != null -> lifecycleScope.launch { - setCriteria(CriterionInstance.fromString( - filterCriteriaProvider, filter!!.criterion)) + setCriteria(filterCriteriaProvider.fromString(filter!!.criterion)) } intent.hasExtra(EXTRA_CRITERIA) -> lifecycleScope.launch { name.setText(intent.getStringExtra(EXTRA_TITLE)) - setCriteria(CriterionInstance.fromString( - filterCriteriaProvider, intent.getStringExtra(EXTRA_CRITERIA)!!)) + setCriteria( + filterCriteriaProvider.fromString(intent.getStringExtra(EXTRA_CRITERIA)!!) + ) } else -> setCriteria(universe()) } @@ -233,12 +241,12 @@ class FilterSettingsActivity : BaseListSettingsActivity() { f.title = newName f.setColor(selectedColor) f.setIcon(selectedIcon) - f.values = AndroidUtilities.mapToSerializedString(values) + f.values = AndroidUtilities.mapToSerializedString(criteria.values) f.criterion = CriterionInstance.serialize(criteria) if (f.criterion.isNullOrBlank()) { throw RuntimeException("Criterion cannot be empty") } - f.setSql(sql) + f.setSql(criteria.sql) if (isNew) { f.id = filterDao.insert(f) } else { @@ -267,8 +275,8 @@ class FilterSettingsActivity : BaseListSettingsActivity() { || selectedColor != filter!!.tint || selectedIcon != filter!!.icon || CriterionInstance.serialize(criteria) != filter!!.criterion.trim() - || values != filter!!.valuesForNewTasks - || sql != filter!!.originalSqlQuery + || criteria.values != filter!!.valuesForNewTasks + || criteria.sql != filter!!.originalSqlQuery } override fun finish() { @@ -314,10 +322,6 @@ class FilterSettingsActivity : BaseListSettingsActivity() { val sql = StringBuilder(Query.select(Field.COUNT).from(Task.TABLE).toString()) .append(" WHERE ") for (instance in criteria) { - var value = instance.valueFromCriterion - if (value == null && instance.criterion.sql != null && instance.criterion.sql.contains("?")) { - value = "" - } when (instance.type) { CriterionInstance.TYPE_ADD -> sql.append("OR ") CriterionInstance.TYPE_SUBTRACT -> sql.append("AND NOT ") @@ -328,7 +332,10 @@ class FilterSettingsActivity : BaseListSettingsActivity() { if (instance.type == CriterionInstance.TYPE_UNIVERSE || instance.criterion.sql == null) { sql.append(activeAndVisible()).append(' ') } else { - var subSql: String? = instance.criterion.sql.replace("?", UnaryCriterion.sanitize(value!!)) + var subSql: String? = instance.criterion.sql.replace( + "?", + UnaryCriterion.sanitize(instance.valueFromCriterion!!) + ) subSql = PermaSql.replacePlaceholdersForQuery(subSql) sql.append(Task.ID).append(" IN (").append(subSql).append(")") } @@ -347,57 +354,48 @@ class FilterSettingsActivity : BaseListSettingsActivity() { adapter.submitList(criteria) } - private fun getValue(instance: CriterionInstance): String? { - var value = instance.valueFromCriterion - if (value == null && instance.criterion.sql != null && instance.criterion.sql.contains("?")) { - value = "" - } - return value - } + companion object { + const val TOKEN_FILTER = "token_filter" + const val EXTRA_TITLE = "extra_title" + const val EXTRA_CRITERIA = "extra_criteria" - // special code for all tasks universe - private val sql: String - get() { - val sql = StringBuilder(" WHERE ") - for (instance in criteria) { - val value = getValue(instance) - when (instance.type) { - CriterionInstance.TYPE_ADD -> sql.append(" OR ") - CriterionInstance.TYPE_SUBTRACT -> sql.append(" AND NOT ") - CriterionInstance.TYPE_INTERSECT -> sql.append(" AND ") - } + val List.sql: String + get() { + val sql = StringBuilder(" WHERE ") + for (instance in this) { + val value = instance.valueFromCriterion + when (instance.type) { + CriterionInstance.TYPE_ADD -> sql.append(" OR ") + CriterionInstance.TYPE_SUBTRACT -> sql.append(" AND NOT ") + CriterionInstance.TYPE_INTERSECT -> sql.append(" AND ") + } - // special code for all tasks universe - if (instance.type == CriterionInstance.TYPE_UNIVERSE || instance.criterion.sql == null) { - sql.append(activeAndVisible()) - } else { - val subSql = instance.criterion.sql + // special code for all tasks universe + if (instance.type == CriterionInstance.TYPE_UNIVERSE || instance.criterion.sql == null) { + sql.append(activeAndVisible()) + } else { + val subSql = instance.criterion.sql .replace("?", UnaryCriterion.sanitize(value!!)) .trim() - sql.append(Task.ID).append(" IN (").append(subSql).append(")") + sql.append(Task.ID).append(" IN (").append(subSql).append(")") + } } + return sql.toString() } - return sql.toString() - } - private val values: Map - get() { - val values: MutableMap = HashMap() - for (instance in criteria) { - val value = getValue(instance) - if (instance.criterion.valuesForNewTasks != null + private val List.values: Map + get() { + val values: MutableMap = HashMap() + for (instance in this) { + val value = instance.valueFromCriterion + if (instance.criterion.valuesForNewTasks != null && instance.type == CriterionInstance.TYPE_INTERSECT) { - for ((key, value1) in instance.criterion.valuesForNewTasks) { - values[key.replace("?", value!!)] = value1.toString().replace("?", value) + for ((key, value1) in instance.criterion.valuesForNewTasks) { + values[key.replace("?", value!!)] = value1.toString().replace("?", value) + } } } + return values } - return values - } - - companion object { - const val TOKEN_FILTER = "token_filter" - const val EXTRA_TITLE = "extra_title" - const val EXTRA_CRITERIA = "extra_criteria" } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt b/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt index fe7156efd..aeafdb835 100644 --- a/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt +++ b/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt @@ -8,12 +8,27 @@ import com.todoroo.andlib.sql.Field.Companion.field import com.todoroo.andlib.sql.Join.Companion.inner import com.todoroo.andlib.sql.Query.Companion.select import com.todoroo.andlib.sql.UnaryCriterion.Companion.isNotNull -import com.todoroo.astrid.api.* +import com.todoroo.andlib.utility.AndroidUtilities +import com.todoroo.astrid.api.BooleanCriterion +import com.todoroo.astrid.api.CustomFilterCriterion +import com.todoroo.astrid.api.MultipleSelectCriterion +import com.todoroo.astrid.api.PermaSql +import com.todoroo.astrid.api.TextInputCriterion +import com.todoroo.astrid.core.CriterionInstance import com.todoroo.astrid.data.Task import dagger.hilt.android.qualifiers.ApplicationContext import org.tasks.R -import org.tasks.data.* +import org.tasks.Strings +import org.tasks.data.Alarm +import org.tasks.data.CaldavDao +import org.tasks.data.CaldavTask +import org.tasks.data.GoogleTask +import org.tasks.data.GoogleTaskListDao +import org.tasks.data.Tag +import org.tasks.data.TagData +import org.tasks.data.TagDataDao import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible +import timber.log.Timber import javax.inject.Inject class FilterCriteriaProvider @Inject constructor( @@ -23,7 +38,40 @@ class FilterCriteriaProvider @Inject constructor( private val caldavDao: CaldavDao) { private val r = context.resources - suspend fun getFilterCriteria(identifier: String): CustomFilterCriterion = when (identifier) { + suspend fun fromString(criterion: String?): List { + if (criterion.isNullOrBlank()) { + return emptyList() + } + val entries: MutableList = java.util.ArrayList() + for (row in criterion.trim().split("\n")) { + val split = row + .split(AndroidUtilities.SERIALIZATION_SEPARATOR) + .map { unescape(it) } + if (split.size != 4 && split.size != 5) { + Timber.e("invalid row: %s", row) + return emptyList() + } + val entry = CriterionInstance() + entry.criterion = getFilterCriteria(split[0]) + val value = split[1] + if (entry.criterion is TextInputCriterion) { + entry.selectedText = value + } else if (entry.criterion is MultipleSelectCriterion) { + val multipleSelectCriterion = entry.criterion as MultipleSelectCriterion? + if (multipleSelectCriterion!!.entryValues != null) { + entry.selectedIndex = multipleSelectCriterion.entryValues.indexOf(value) + } + } else { + Timber.d("Ignored value %s for %s", value, entry.criterion) + } + entry.type = split[3].toInt() + Timber.d("%s -> %s", row, entry) + entries.add(entry) + } + return entries + } + + private suspend fun getFilterCriteria(identifier: String): CustomFilterCriterion = when (identifier) { IDENTIFIER_UNIVERSE -> startingUniverse IDENTIFIER_TITLE -> taskTitleContainsFilter IDENTIFIER_IMPORTANCE -> priorityFilter @@ -350,5 +398,12 @@ class FilterCriteriaProvider @Inject constructor( private const val IDENTIFIER_REMINDERS = "reminders" private val ONE = field("1") + + private fun unescape(item: String?): String { + return if (Strings.isNullOrEmpty(item)) { + "" + } else item!!.replace( + AndroidUtilities.SEPARATOR_ESCAPE, AndroidUtilities.SERIALIZATION_SEPARATOR) + } } } \ No newline at end of file