Migrate google task filters

pull/2294/head
Alex Baker 1 year ago
parent ad3c35bc5d
commit 067317b5a0

@ -20,7 +20,7 @@ public class MultipleSelectCriterion extends CustomFilterCriterion implements Pa
/** Parcelable Creator Object */
public static final Parcelable.Creator<MultipleSelectCriterion> CREATOR =
new Parcelable.Creator<MultipleSelectCriterion>() {
new Parcelable.Creator<>() {
/** {@inheritDoc} */
@Override

@ -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<CriterionInstance> {
if (isNullOrEmpty(criterion)) {
return emptyList()
}
val entries: MutableList<CriterionInstance> = 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<CriterionInstance>): String {
return criterion
.joinToString("\n") { it.serialize() }

@ -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
}
}

@ -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<Upgrade_11_3>,
private val upgrade_11_12_3: Lazy<Upgrade_11_12_3>,
private val upgrade_12_4: Lazy<Upgrade_12_4>,
private val upgrade_13_2: Lazy<Upgrade_13_2>,
) {
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 {

@ -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<CriterionInstance>.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<String, Any>
get() {
val values: MutableMap<String, Any> = HashMap()
for (instance in criteria) {
val value = getValue(instance)
if (instance.criterion.valuesForNewTasks != null
private val List<CriterionInstance>.values: Map<String, Any>
get() {
val values: MutableMap<String, Any> = 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"
}
}

@ -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<CriterionInstance> {
if (criterion.isNullOrBlank()) {
return emptyList()
}
val entries: MutableList<CriterionInstance> = 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)
}
}
}
Loading…
Cancel
Save