Convert filters to data classes (#2569)

pull/2578/head
Alex Baker 8 months ago committed by GitHub
parent 68fd36b14d
commit ee500c24b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,7 @@
package com.todoroo.astrid.subtasks
import androidx.test.InstrumentationRegistry
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
@ -14,7 +14,7 @@ import javax.inject.Inject
abstract class SubtasksTestCase : InjectingTestCase() {
lateinit var updater: SubtasksFilterUpdater
lateinit var filter: Filter
lateinit var filter: AstridOrderingFilter
@Inject lateinit var taskListMetadataDao: TaskListMetadataDao
@Inject lateinit var taskDao: TaskDao
@Inject lateinit var preferences: Preferences

@ -16,6 +16,7 @@ import android.widget.TextView;
import org.tasks.BuildConfig;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
@ -80,12 +81,12 @@ public class AndroidUtilities {
result.append(SERIALIZATION_SEPARATOR);
}
public static Map<String, Object> mapFromSerializedString(String string) {
public static Map<String, Serializable> mapFromSerializedString(String string) {
if (string == null) {
return new HashMap<>();
}
Map<String, Object> result = new HashMap<>();
Map<String, Serializable> result = new HashMap<>();
fromSerialized(
string,
result,
@ -191,11 +192,6 @@ public class AndroidUtilities {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
/** Capitalize the first character */
public static String capitalize(String string) {
return string.substring(0, 1).toUpperCase() + string.substring(1);
}
interface SerializedPut<T> {
void put(T object, String key, char type, String value) throws NumberFormatException;

@ -288,10 +288,7 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler {
return
}
val newFilter = taskListFragment.getFilter()
if (filter != null
&& !force
&& filter!!.areItemsTheSame(newFilter)
&& filter!!.areContentsTheSame(newFilter)) {
if (!force && filter == newFilter) {
return
}
filter = newFilter

@ -47,15 +47,19 @@ import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.composethemeadapter.MdcTheme
import com.google.android.material.snackbar.Snackbar
import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.adapter.TaskAdapter
import com.todoroo.astrid.adapter.TaskAdapterProvider
import com.todoroo.astrid.api.AstridApiConstants.EXTRAS_OLD_DUE_DATE
import com.todoroo.astrid.api.AstridApiConstants.EXTRAS_TASK_ID
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.CustomFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.api.IdListFilter
import com.todoroo.astrid.api.TagFilter
import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao
@ -86,6 +90,7 @@ import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.SubscriptionNagBanner
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.data.CaldavDao
import org.tasks.data.Tag
import org.tasks.data.TagDataDao
import org.tasks.data.TaskContainer
import org.tasks.databinding.FragmentTaskListBinding
@ -175,12 +180,11 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
result.data?.let { data ->
if (data.getBooleanExtra(SortSettingsActivity.EXTRA_FORCE_RELOAD, false)) {
activity?.recreate()
} else {
listViewModel.invalidate()
}
if (data.getBooleanExtra(SortSettingsActivity.EXTRA_CHANGED_GROUP, false)) {
taskAdapter.clearCollapsed()
}
listViewModel.invalidate()
}
}
}
@ -265,7 +269,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
fab.isVisible = filter.isWritable
}
themeColor = if (filter.tint != 0) colorProvider.getThemeColor(filter.tint, true) else defaultThemeColor
filter.filterOverride = null
(filter as? AstridOrderingFilter)?.filterOverride = null
// set up list adapters
taskAdapter = taskAdapterProvider.createTaskAdapter(filter)
@ -360,12 +364,19 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private fun setupMenu(appBar: Toolbar) {
val menu = appBar.menu
menu.clear()
if (filter.hasBeginningMenu()) {
appBar.inflateMenu(filter.beginningMenu)
if (filter is PlaceFilter) {
appBar.inflateMenu(R.menu.menu_location_actions)
}
appBar.inflateMenu(R.menu.menu_task_list_fragment_bottom)
if (filter.hasMenu()) {
appBar.inflateMenu(filter.menu)
when (filter) {
is CaldavFilter -> R.menu.menu_caldav_list_fragment
is CustomFilter -> R.menu.menu_custom_filter
is GtasksFilter -> R.menu.menu_gtasks_list_fragment
is TagFilter -> R.menu.menu_tag_view_fragment
is PlaceFilter -> R.menu.menu_location_list_fragment
else -> null
}?.let {
appBar.inflateMenu(it)
}
if (appBar is BottomAppBar) {
menu.removeItem(R.id.menu_search)
@ -430,7 +441,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
SortSettingsActivity.getIntent(
requireActivity(),
filter.supportsManualSort(),
filter.supportsAstridSorting() && preferences.isAstridSortEnabled,
filter is AstridOrderingFilter && preferences.isAstridSortEnabled,
)
)
true
@ -745,8 +756,19 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
}
R.id.menu_share -> {
lifecycleScope.launch {
selected.chunkedMap { taskDao.fetchTasks(preferences, IdListFilter(it)) }
.apply { send(this) }
selected
.chunkedMap {
taskDao.fetchTasks(
preferences,
FilterImpl(
sql = QueryTemplate()
.join(Join.left(Tag.TABLE, Tag.TASK.eq(Task.ID)))
.where(Task.ID.`in`(it))
.toString()
)
)
}
.let { send(it) }
}
true
}

@ -1,7 +1,7 @@
package com.todoroo.astrid.adapter
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskMover
@ -13,19 +13,19 @@ import org.tasks.data.GoogleTaskDao
import org.tasks.data.TaskContainer
import org.tasks.data.TaskListMetadata
import timber.log.Timber
import java.util.*
import java.util.Collections
import kotlin.math.abs
@Deprecated("legacy astrid manual sorting")
class AstridTaskAdapter internal constructor(
private val list: TaskListMetadata,
private val filter: Filter,
private val updater: SubtasksFilterUpdater,
googleTaskDao: GoogleTaskDao,
caldavDao: CaldavDao,
private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager,
taskMover: TaskMover,
private val list: TaskListMetadata,
private val filter: AstridOrderingFilter,
private val updater: SubtasksFilterUpdater,
googleTaskDao: GoogleTaskDao,
caldavDao: CaldavDao,
private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager,
taskMover: TaskMover,
) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) {
private val chainedCompletions = Collections.synchronizedMap(HashMap<String, ArrayList<String>>())

@ -108,7 +108,7 @@ class NavigationDrawerAdapter @Inject constructor(
old[oldPosition].areItemsTheSame(new[newPosition])
override fun areContentsTheSame(oldPosition: Int, newPosition: Int) =
old[oldPosition].areContentsTheSame(new[newPosition])
old[oldPosition] == new[newPosition]
}
override fun onChanged(position: Int, count: Int, payload: Any?) =

@ -1,6 +1,7 @@
package com.todoroo.astrid.adapter
import android.content.Context
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.GtasksFilter
@ -33,7 +34,7 @@ class TaskAdapterProvider @Inject constructor(
private val taskMover: TaskMover,
) {
fun createTaskAdapter(filter: Filter): TaskAdapter {
if (filter.supportsAstridSorting() && preferences.isAstridSort) {
if (filter is AstridOrderingFilter && preferences.isAstridSort) {
when (filter) {
is TagFilter -> return createManualTagTaskAdapter(filter)
else -> {
@ -67,7 +68,7 @@ class TaskAdapterProvider @Inject constructor(
AstridTaskAdapter(list!!, filter, updater, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover)
}
private fun createManualFilterTaskAdapter(filter: Filter): TaskAdapter? = runBlocking {
private fun createManualFilterTaskAdapter(filter: AstridOrderingFilter): TaskAdapter? = runBlocking {
var filterId: String? = null
var prefId: String? = null
if (BuiltInFilterExposer.isInbox(context, filter)) {

@ -1,29 +1,45 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import androidx.core.os.ParcelCompat
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task
import org.tasks.R
import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
class CaldavFilter(
@Parcelize
data class CaldavFilter(
val calendar: CaldavCalendar,
val principals: Int = 0,
) : Filter(calendar.name, queryTemplate(calendar), getValuesForNewTask(calendar)) {
override var count: Int = NO_COUNT,
) : Filter {
override val title: String?
get() = calendar.name
override val sql: String
get() = QueryTemplate()
.join(left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK)))
.where(
and(
activeAndVisible(),
CaldavTask.DELETED.eq(0),
CaldavTask.CALENDAR.eq(calendar.uuid)
)
)
.toString()
override val valuesForNewTasks: String
get() = AndroidUtilities.mapToSerializedString(mapOf(CaldavTask.KEY to calendar.uuid!!))
init {
id = calendar.id
tint = calendar.color
icon = calendar.getIcon()!!
order = calendar.order
}
override val order: Int
get() = calendar.order
override val tint: Int
get() = calendar.color
override val icon: Int
get() = calendar.getIcon()!!
val uuid: String
get() = calendar.uuid!!
@ -33,50 +49,9 @@ class CaldavFilter(
override val isReadOnly: Boolean
get() = calendar.access == CaldavCalendar.ACCESS_READ_ONLY
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(calendar, 0)
}
override fun supportsManualSort() = true
override val menu = R.menu.menu_caldav_list_fragment
override fun areContentsTheSame(other: FilterListItem): Boolean {
return super.areContentsTheSame(other) && calendar == (other as CaldavFilter).calendar && principals == other.principals
}
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<CaldavFilter> {
override fun createFromParcel(source: Parcel): CaldavFilter {
return CaldavFilter(
ParcelCompat.readParcelable(source, javaClass.classLoader, CaldavCalendar::class.java)!!
)
}
override fun newArray(size: Int): Array<CaldavFilter?> {
return arrayOfNulls(size)
}
}
private fun queryTemplate(caldavCalendar: CaldavCalendar): QueryTemplate {
return QueryTemplate()
.join(left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK)))
.where(getCriterion(caldavCalendar))
}
private fun getCriterion(caldavCalendar: CaldavCalendar): Criterion {
return and(
activeAndVisible(),
CaldavTask.DELETED.eq(0),
CaldavTask.CALENDAR.eq(caldavCalendar.uuid)
)
}
private fun getValuesForNewTask(caldavCalendar: CaldavCalendar): Map<String, Any> {
val result: MutableMap<String, Any> = HashMap()
result[CaldavTask.KEY] = caldavCalendar.uuid!!
return result
}
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is CaldavFilter && calendar.id == other.calendar.id
}
}

@ -1,59 +1,36 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.utility.AndroidUtilities
import org.tasks.R
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import kotlinx.parcelize.Parcelize
import org.tasks.themes.CustomIcons
class CustomFilter : Filter {
var criterion: String? = null
private set
constructor(filter: org.tasks.data.Filter) : super(
filter.title,
filter.sql!!,
AndroidUtilities.mapFromSerializedString(filter.values)
) {
id = filter.id
criterion = filter.criterion
tint = filter.color ?: 0
icon = filter.icon ?: CustomIcons.FILTER
order = filter.order
}
private constructor(parcel: Parcel) {
readFromParcel(parcel)
}
/** {@inheritDoc} */
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
dest.writeString(criterion)
}
override fun readFromParcel(source: Parcel) {
super.readFromParcel(source)
criterion = source.readString()
}
override val menu: Int
get() = if (id > 0) R.menu.menu_custom_filter else 0
override fun areContentsTheSame(other: FilterListItem): Boolean {
return super.areContentsTheSame(other) && criterion == (other as CustomFilter).criterion
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<CustomFilter> = object : Parcelable.Creator<CustomFilter> {
override fun createFromParcel(source: Parcel): CustomFilter? {
return CustomFilter(source)
}
override fun newArray(size: Int): Array<CustomFilter?> {
return arrayOfNulls(size)
}
}
@Parcelize
data class CustomFilter(
val filter: org.tasks.data.Filter,
override var count: Int = NO_COUNT,
) : Filter {
override val title: String?
get() = filter.title
override val sql: String
get() = filter.sql!!
override val valuesForNewTasks: String?
get() = filter.values
val criterion: String?
get() = filter.criterion
override val order: Int
get() = filter.order
val id: Long
get() = filter.id
override val icon: Int
get() = filter.icon ?: CustomIcons.FILTER
override val tint: Int
get() = filter.color ?: 0
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is CustomFilter && id == other.id
}
}

@ -1,173 +1,57 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import androidx.annotation.MenuRes
import com.todoroo.andlib.sql.QueryTemplate
open class Filter : FilterListItem, Parcelable {
var valuesForNewTasks: Map<String, Any> = HashMap()
var sql: String? = null
@Deprecated("for astrid manual order") var filterOverride: String? = null
var id = 0L
var icon = -1
var title: String? = null
var tint = 0
var count = -1
var order = NO_ORDER
/**
* Utility constructor for creating a Filter object
*
* @param title Title of this item as displayed on the lists page, e.g. Inbox
* @param sql SQL query for this list (see [.sqlQuery] for examples).
*/
constructor(
title: String?,
sql: QueryTemplate,
valuesForNewTasks: Map<String, Any> = emptyMap(),
) : this(title, sql.toString(), valuesForNewTasks)
/**
* Utility constructor for creating a Filter object
*
* @param title Title of this item as displayed on the lists page, e.g. Inbox
* @param sql SQL query for this list (see [.sqlQuery] for examples).
*/
internal constructor(
title: String?,
sql: String,
valuesForNewTasks: Map<String, Any>
) {
this.title = title
this.sql = sql
this.valuesForNewTasks = valuesForNewTasks
}
protected constructor()
fun getSqlQuery(): String {
return filterOverride ?: sql!!
}
override val itemType = FilterListItem.Type.ITEM
/** {@inheritDoc} */
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeLong(id)
dest.writeInt(icon)
dest.writeString(title)
dest.writeInt(tint)
dest.writeInt(count)
dest.writeInt(order)
dest.writeString(sql)
dest.writeMap(valuesForNewTasks)
}
open fun readFromParcel(source: Parcel) {
id = source.readLong()
icon = source.readInt()
title = source.readString()
tint = source.readInt()
count = source.readInt()
order = source.readInt()
sql = source.readString()
source.readMap(valuesForNewTasks, javaClass.classLoader)
}
open fun supportsAstridSorting() = false
open fun supportsManualSort() = false
open fun supportsHiddenTasks() = true
open fun supportsSubtasks() = true
open fun supportsSorting() = true
open val isReadOnly: Boolean = false
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import kotlinx.parcelize.Parcelize
interface Filter : FilterListItem, Parcelable {
val valuesForNewTasks: String?
get() = null
val sql: String?
val icon: Int
get() = -1
val title: String?
val tint: Int
get() = 0
@Deprecated("Remove this")
var count: Int
val order: Int
get() = NO_ORDER
override val itemType: FilterListItem.Type
get() = FilterListItem.Type.ITEM
val isReadOnly: Boolean
get() = false
val isWritable: Boolean
get() = !isReadOnly
fun hasBeginningMenu(): Boolean {
return beginningMenu != 0
}
@get:MenuRes
open val beginningMenu: Int
get() = 0
fun supportsManualSort(): Boolean = false
fun supportsHiddenTasks(): Boolean = true
fun supportsSubtasks(): Boolean = true
fun supportsSorting(): Boolean = true
fun hasMenu(): Boolean {
return menu != 0
companion object {
const val NO_ORDER = -1
const val NO_COUNT = -1
}
}
@get:MenuRes
open val menu: Int
get() = 0
@Deprecated("Use manual ordering")
interface AstridOrderingFilter : Filter {
var filterOverride: String?
override fun describeContents() = 0
fun getSqlQuery(): String = filterOverride ?: sql!!
}
@Parcelize
data class FilterImpl(
override val title: String? = null,
override val sql: String? = null,
override val valuesForNewTasks: String? = null,
override val icon: Int = -1,
override val tint: Int = 0,
override var count: Int = NO_COUNT,
) : Filter {
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is Filter && id == other.id && sql == other.sql
}
override fun areContentsTheSame(other: FilterListItem): Boolean {
return this == other
}
override fun toString(): String {
return "Filter(valuesForNewTasks=$valuesForNewTasks, sql=$sql, filterOverride=$filterOverride, id=$id, icon=$icon, title=$title, tint=$tint, count=$count, order=$order)"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Filter
if (valuesForNewTasks != other.valuesForNewTasks) return false
if (sql != other.sql) return false
if (filterOverride != other.filterOverride) return false
if (id != other.id) return false
if (icon != other.icon) return false
if (title != other.title) return false
if (tint != other.tint) return false
if (count != other.count) return false
if (order != other.order) return false
return isReadOnly == other.isReadOnly
}
override fun hashCode(): Int {
var result = valuesForNewTasks.hashCode()
result = 31 * result + (sql?.hashCode() ?: 0)
result = 31 * result + (filterOverride?.hashCode() ?: 0)
result = 31 * result + id.hashCode()
result = 31 * result + icon
result = 31 * result + (title?.hashCode() ?: 0)
result = 31 * result + tint
result = 31 * result + count
result = 31 * result + order
result = 31 * result + isReadOnly.hashCode()
return result
}
companion object {
const val NO_ORDER = -1
@JvmField
val CREATOR: Parcelable.Creator<Filter> = object : Parcelable.Creator<Filter> {
/** {@inheritDoc} */
override fun createFromParcel(source: Parcel): Filter {
val item = Filter()
item.readFromParcel(source)
return item
}
/** {@inheritDoc} */
override fun newArray(size: Int): Array<Filter?> {
return arrayOfNulls(size)
}
}
return other is Filter && sql == other.sql
}
}

@ -7,7 +7,6 @@ interface FilterListItem {
val itemType: Type
fun areItemsTheSame(other: FilterListItem): Boolean
fun areContentsTheSame(other: FilterListItem): Boolean
enum class Type(@param:LayoutRes val layout: Int) {
ITEM(R.layout.filter_adapter_row),

@ -1,77 +1,58 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import androidx.core.os.ParcelCompat
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task
import org.tasks.R
import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask
import org.tasks.data.GoogleTask
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
class GtasksFilter(
val list: CaldavCalendar
) : Filter(list.name, getQueryTemplate(list), getValuesForNewTasks(list)) {
@Parcelize
data class GtasksFilter(
val list: CaldavCalendar,
override var count: Int = NO_COUNT,
) : Filter {
override val title: String?
get() = list.name
override val sql: String
get() = QueryTemplate()
.join(left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK)))
.where(
and(
activeAndVisible(),
CaldavTask.DELETED.eq(0),
CaldavTask.CALENDAR.eq(list.uuid)
)
)
.toString()
init {
id = list.id
tint = list.color
icon = list.getIcon()!!
order = list.order
}
override val valuesForNewTasks: String
get() = AndroidUtilities.mapToSerializedString(mapOf(GoogleTask.KEY to list.uuid!!))
override val order: Int
get() = list.order
override val icon: Int
get() = list.getIcon()!!
override val tint: Int
get() = list.color
val account: String
get() = list.account!!
override fun supportsManualSort() = true
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(list, 0)
}
val remoteId: String
get() = list.uuid!!
override val menu = R.menu.menu_gtasks_list_fragment
override fun areContentsTheSame(other: FilterListItem): Boolean {
return super.areContentsTheSame(other) && list == (other as GtasksFilter).list
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<GtasksFilter> = object : Parcelable.Creator<GtasksFilter> {
override fun createFromParcel(source: Parcel): GtasksFilter {
return GtasksFilter(
ParcelCompat.readParcelable(source, javaClass.classLoader, CaldavCalendar::class.java)!!
)
}
override fun newArray(size: Int): Array<GtasksFilter?> {
return arrayOfNulls(size)
}
}
private fun getQueryTemplate(list: CaldavCalendar): QueryTemplate {
return QueryTemplate()
.join(left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK)))
.where(
and(
activeAndVisible(),
CaldavTask.DELETED.eq(0),
CaldavTask.CALENDAR.eq(list.uuid)
)
)
}
private fun getValuesForNewTasks(list: CaldavCalendar): Map<String, Any> {
val values: MutableMap<String, Any> = HashMap()
values[GoogleTask.KEY] = list.uuid!!
return values
}
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is GtasksFilter && list.id == other.list.id
}
}

@ -1,48 +0,0 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import com.google.common.primitives.Longs
import com.todoroo.andlib.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.data.Task
import org.tasks.data.Tag
class IdListFilter : Filter {
private var ids: List<Long?>? = null
constructor(ids: List<Long?>) : super("", getQueryTemplate(ids)) {
this.ids = ids
}
private constructor(source: Parcel) {
readFromParcel(source)
}
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
dest.writeLongArray(Longs.toArray(ids!!))
}
override fun readFromParcel(source: Parcel) {
super.readFromParcel(source)
val ids = LongArray(source.readInt())
source.setDataPosition(source.dataPosition() - 1)
source.readLongArray(ids)
this.ids = Longs.asList(*ids)
}
companion object {
@JvmField val CREATOR: Parcelable.Creator<IdListFilter> = object : Parcelable.Creator<IdListFilter> {
override fun createFromParcel(source: Parcel) = IdListFilter(source)
override fun newArray(size: Int): Array<IdListFilter?> = arrayOfNulls(size)
}
private fun getQueryTemplate(ids: List<Long?>): QueryTemplate {
return QueryTemplate()
.join(left(Tag.TABLE, Tag.TASK.eq(Task.ID)))
.where(Task.ID.`in`(ids))
}
}
}

@ -1,12 +1,12 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.Query
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask
import org.tasks.data.Geofence
@ -14,59 +14,63 @@ import org.tasks.data.Place
import org.tasks.data.Tag
import org.tasks.data.UserActivity
class SearchFilter : Filter {
private constructor()
constructor(title: String?, query: String) : super(title, getQueryTemplate(query))
override fun supportsHiddenTasks() = false
companion object {
/** Parcelable Creator Object */
@JvmField
val CREATOR: Parcelable.Creator<SearchFilter> = object : Parcelable.Creator<SearchFilter> {
/** {@inheritDoc} */
override fun createFromParcel(source: Parcel): SearchFilter {
val item = SearchFilter()
item.readFromParcel(source)
return item
}
/** {@inheritDoc} */
override fun newArray(size: Int): Array<SearchFilter?> {
return arrayOfNulls(size)
}
}
private fun getQueryTemplate(query: String): QueryTemplate {
@Parcelize
data class SearchFilter(
override val title: String,
val query: String,
override var count: Int = NO_COUNT,
) : Filter {
override val sql: String
get() {
val matcher = "%$query%"
return QueryTemplate()
.where(
Criterion.and(
Task.DELETION_DATE.eq(0),
Criterion.or(
Task.NOTES.like(matcher),
Task.TITLE.like(matcher),
Task.ID.`in`(
Query.select(Tag.TASK)
.from(Tag.TABLE)
.where(Tag.NAME.like(matcher))),
Task.UUID.`in`(
Query.select(UserActivity.TASK)
.from(UserActivity.TABLE)
.where(UserActivity.MESSAGE.like(matcher))),
Task.ID.`in`(
Query.select(Geofence.TASK)
.from(Geofence.TABLE)
.join(Join.inner(Place.TABLE, Place.UID.eq(Geofence.PLACE)))
.where(Criterion.or(Place.NAME.like(matcher),
Place.ADDRESS.like(matcher)))),
Task.ID.`in`(
Query.select(CaldavTask.TASK)
.from(CaldavTask.TABLE)
.join(Join.inner(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR)))
.where(CaldavCalendar.NAME.like(matcher))),
)))
.where(
Criterion.and(
Task.DELETION_DATE.eq(0),
Criterion.or(
Task.NOTES.like(matcher),
Task.TITLE.like(matcher),
Task.ID.`in`(
Query.select(Tag.TASK)
.from(Tag.TABLE)
.where(Tag.NAME.like(matcher))
),
Task.UUID.`in`(
Query.select(UserActivity.TASK)
.from(UserActivity.TABLE)
.where(UserActivity.MESSAGE.like(matcher))
),
Task.ID.`in`(
Query.select(Geofence.TASK)
.from(Geofence.TABLE)
.join(Join.inner(Place.TABLE, Place.UID.eq(Geofence.PLACE)))
.where(
Criterion.or(
Place.NAME.like(matcher),
Place.ADDRESS.like(matcher)
)
)
),
Task.ID.`in`(
Query.select(CaldavTask.TASK)
.from(CaldavTask.TABLE)
.join(
Join.inner(
CaldavCalendar.TABLE,
CaldavCalendar.UUID.eq(CaldavTask.CALENDAR)
)
)
.where(CaldavCalendar.NAME.like(matcher))
),
)
)
)
.toString()
}
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is SearchFilter
}
override fun supportsHiddenTasks() = false
}

@ -1,67 +1,46 @@
package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import androidx.core.os.ParcelCompat
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task
import org.tasks.R
import kotlinx.parcelize.Parcelize
import org.tasks.data.Tag
import org.tasks.data.TagData
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
class TagFilter(
val tagData: TagData
) : Filter(tagData.name, queryTemplate(tagData.remoteId), getValuesForNewTask(tagData)) {
@Parcelize
data class TagFilter(
val tagData: TagData,
override var count: Int = NO_COUNT,
override var filterOverride: String? = null,
) : AstridOrderingFilter {
override val title: String?
get() = tagData.name
override val sql: String
get() = QueryTemplate()
.join(inner(Tag.TABLE, Task.ID.eq(Tag.TASK)))
.where(and(Tag.TAG_UID.eq(uuid), activeAndVisible()))
.toString()
init {
id = tagData.id!!
tint = tagData.getColor()!!
icon = tagData.getIcon()!!
order = tagData.order
}
val uuid: String
get() = tagData.remoteId!!
override val order: Int
get() = tagData.order
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(tagData, 0)
}
override val valuesForNewTasks: String
get() = AndroidUtilities.mapToSerializedString(mapOf(Tag.KEY to tagData.name!!))
override fun supportsAstridSorting() = true
override val icon: Int
get() = tagData.getIcon()!!
override val menu = R.menu.menu_tag_view_fragment
override val tint: Int
get() = tagData.getColor()!!
override fun areContentsTheSame(other: FilterListItem): Boolean {
return tagData == (other as TagFilter).tagData
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<TagFilter> = object : Parcelable.Creator<TagFilter> {
override fun createFromParcel(source: Parcel): TagFilter {
return TagFilter(
ParcelCompat.readParcelable(source, javaClass.classLoader, TagData::class.java)!!
)
}
override fun newArray(size: Int): Array<TagFilter?> {
return arrayOfNulls(size)
}
}
private fun queryTemplate(uuid: String?): QueryTemplate {
return QueryTemplate()
.join(inner(Tag.TABLE, Task.ID.eq(Tag.TASK)))
.where(and(Tag.TAG_UID.eq(uuid), activeAndVisible()))
}
val uuid: String
get() = tagData.remoteId!!
private fun getValuesForNewTask(tagData: TagData): Map<String, Any> {
val values: MutableMap<String, Any> = HashMap()
values[Tag.KEY] = tagData.name!!
return values
}
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is TagFilter && tagData.id!! == other.tagData.id
}
}

@ -11,9 +11,9 @@ import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Criterion.Companion.or
import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.timers.TimerPlugin
import dagger.hilt.android.qualifiers.ApplicationContext
@ -22,13 +22,12 @@ import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask
import org.tasks.data.TaskDao
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
import org.tasks.filters.MyTasksFilter
import org.tasks.filters.NotificationsFilter
import org.tasks.filters.RecentlyModifiedFilter
import org.tasks.filters.SnoozedFilter
import org.tasks.filters.SortableFilter
import org.tasks.filters.TodayFilter
import org.tasks.preferences.Preferences
import org.tasks.themes.CustomIcons
import javax.inject.Inject
/**
@ -43,122 +42,117 @@ class BuiltInFilterExposer @Inject constructor(
val myTasksFilter: Filter
get() {
val myTasksFilter = getMyTasksFilter(context.resources)
myTasksFilter.icon = CustomIcons.ALL_INBOX
return myTasksFilter
return getMyTasksFilter(context.resources)
}
suspend fun filters(): List<Filter> {
val r = context.resources
val filters: MutableList<Filter> = ArrayList()
if (preferences.getBoolean(R.string.p_show_today_filter, true)) {
val todayFilter = getTodayFilter(r)
todayFilter.icon = CustomIcons.TODAY
filters.add(todayFilter)
filters.add(getTodayFilter(r))
}
if (preferences.getBoolean(R.string.p_show_recently_modified_filter, true)) {
val recentlyModifiedFilter = getRecentlyModifiedFilter(r)
recentlyModifiedFilter.icon = CustomIcons.HISTORY
filters.add(recentlyModifiedFilter)
filters.add(getRecentlyModifiedFilter(r))
}
if (taskDao.snoozedReminders() > 0) {
val snoozedFilter = getSnoozedFilter(r)
snoozedFilter.icon = R.drawable.ic_snooze_white_24dp
filters.add(snoozedFilter)
filters.add(getSnoozedFilter(r))
}
if (taskDao.activeTimers() > 0) {
filters.add(TimerPlugin.createFilter(context))
}
if (taskDao.hasNotifications() > 0) {
val element = NotificationsFilter(context)
element.icon = R.drawable.ic_outline_notifications_24px
filters.add(element)
filters.add(getNotificationsFilter(context))
}
return filters
}
companion object {
/** Build inbox filter */
fun getMyTasksFilter(r: Resources): Filter {
return SortableFilter(
r.getString(R.string.BFE_Active),
QueryTemplate().where(and(activeAndVisible(), Task.PARENT.eq(0))))
fun getMyTasksFilter(r: Resources): AstridOrderingFilter {
return MyTasksFilter(r.getString(R.string.BFE_Active))
}
fun getTodayFilter(r: Resources): Filter {
val todayTitle = AndroidUtilities.capitalize(r.getString(R.string.today))
val todayValues: MutableMap<String, Any> = HashMap()
todayValues[Task.DUE_DATE.name] = PermaSql.VALUE_NOON
return SortableFilter(
todayTitle,
QueryTemplate()
.where(
and(
activeAndVisible(),
Task.DUE_DATE.gt(0),
Task.DUE_DATE.lte(PermaSql.VALUE_EOD))),
todayValues)
return TodayFilter(r.getString(R.string.today))
}
fun getNoListFilter() =
Filter(
"No list",
QueryTemplate()
.join(Join.left(CaldavTask.TABLE, CaldavTask.TASK.eq(Task.ID)))
.where(CaldavTask.ID.eq(null))
).apply {
icon = R.drawable.ic_outline_cloud_off_24px
}
FilterImpl(
title = "No list",
sql = QueryTemplate()
.join(Join.left(CaldavTask.TABLE, CaldavTask.TASK.eq(Task.ID)))
.where(CaldavTask.ID.eq(null))
.toString(),
icon = R.drawable.ic_outline_cloud_off_24px,
)
fun getDeleted() =
Filter("Deleted", QueryTemplate().where(Task.DELETION_DATE.gt(0)))
.apply { icon = R.drawable.ic_outline_delete_24px }
FilterImpl(
title = "Deleted",
sql = QueryTemplate().where(Task.DELETION_DATE.gt(0)).toString(),
icon = R.drawable.ic_outline_delete_24px,
)
fun getMissingListFilter() =
Filter(
"Missing list",
QueryTemplate()
.join(Join.left(CaldavTask.TABLE, CaldavTask.TASK.eq(Task.ID)))
.join(Join.left(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR)))
.where(and(CaldavTask.ID.gt(0), CaldavCalendar.UUID.eq(null)))
).apply {
icon = R.drawable.ic_outline_cloud_off_24px
}
FilterImpl(
title = "Missing list",
sql = QueryTemplate()
.join(Join.left(CaldavTask.TABLE, CaldavTask.TASK.eq(Task.ID)))
.join(
Join.left(
CaldavCalendar.TABLE,
CaldavCalendar.UUID.eq(CaldavTask.CALENDAR)
)
)
.where(and(CaldavTask.ID.gt(0), CaldavCalendar.UUID.eq(null)))
.toString(),
icon = R.drawable.ic_outline_cloud_off_24px,
)
fun getMissingAccountFilter() =
Filter(
"Missing account",
QueryTemplate()
.join(Join.left(CaldavTask.TABLE, and(CaldavTask.TASK.eq(Task.ID))))
.join(Join.left(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR)))
.join(Join.left(CaldavAccount.TABLE, CaldavAccount.UUID.eq(CaldavCalendar.ACCOUNT)))
.where(and(CaldavTask.ID.gt(0), CaldavAccount.UUID.eq(null)))
).apply {
icon = R.drawable.ic_outline_cloud_off_24px
}
FilterImpl(
title = "Missing account",
sql = QueryTemplate()
.join(
Join.left(CaldavTask.TABLE, and(CaldavTask.TASK.eq(Task.ID)))
).join(
Join.left(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR))
).join(
Join.left(
CaldavAccount.TABLE, CaldavAccount.UUID.eq(CaldavCalendar.ACCOUNT)
)
)
.where(and(CaldavTask.ID.gt(0), CaldavAccount.UUID.eq(null)))
.toString(),
icon = R.drawable.ic_outline_cloud_off_24px,
)
fun getNoTitleFilter() =
Filter(
"No title",
QueryTemplate().where(or(Task.TITLE.eq(null), Task.TITLE.eq("")))
).apply {
icon = R.drawable.ic_outline_clear_24px
}
FilterImpl(
title = "No title",
sql = QueryTemplate().where(or(Task.TITLE.eq(null), Task.TITLE.eq(""))).toString(),
icon = R.drawable.ic_outline_clear_24px,
)
fun getNoCreateDateFilter() =
Filter("No create time", QueryTemplate().where(Task.CREATION_DATE.eq(0)))
.apply { icon = R.drawable.ic_outline_add_24px }
FilterImpl(
title = "No create time",
sql = QueryTemplate().where(Task.CREATION_DATE.eq(0)).toString(),
icon = R.drawable.ic_outline_add_24px,
)
fun getNoModificationDateFilter() =
Filter("No modify time", QueryTemplate().where(Task.MODIFICATION_DATE.eq(0)))
.apply { icon = R.drawable.ic_outline_edit_24px }
FilterImpl(
title = "No modify time",
sql = QueryTemplate().where(Task.MODIFICATION_DATE.eq(0)).toString(),
icon = R.drawable.ic_outline_edit_24px,
)
fun getRecentlyModifiedFilter(r: Resources) =
RecentlyModifiedFilter(r.getString(R.string.BFE_Recent))
fun getSnoozedFilter(r: Resources) = SnoozedFilter(r.getString(R.string.filter_snoozed))
fun getNotificationsFilter(context: Context) = NotificationsFilter(context)
fun getNotificationsFilter(context: Context) = NotificationsFilter(context.getString(R.string.notifications))
@JvmStatic
fun isInbox(context: Context, filter: Filter?) =

@ -1,5 +1,6 @@
package com.todoroo.astrid.service
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter
@ -123,7 +124,10 @@ class TaskCreator @Inject constructor(
}
suspend fun createWithValues(filter: Filter?, title: String?): Task {
return create(filter?.valuesForNewTasks, title)
return create(
AndroidUtilities.mapFromSerializedString(filter?.valuesForNewTasks),
title
)
}
/**

@ -1,6 +1,7 @@
package com.todoroo.astrid.service
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.data.Task
import org.tasks.LocalBroadcastManager
import org.tasks.caldav.VtodoCache
@ -43,10 +44,8 @@ class TaskDeleter @Inject constructor(
}
suspend fun clearCompleted(filter: Filter): Int {
val deleteFilter = Filter(
null,
QueryUtils.removeOrder(QueryUtils.showHiddenAndCompleted(filter.sql!!)),
emptyMap()
val deleteFilter = FilterImpl(
sql = QueryUtils.removeOrder(QueryUtils.showHiddenAndCompleted(filter.sql!!)),
)
val completed = taskDao.fetchTasks(preferences, deleteFilter)
.filter(TaskContainer::isCompleted)

@ -1,6 +1,6 @@
package com.todoroo.astrid.subtasks
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.isValidUuid
@ -16,7 +16,8 @@ import javax.inject.Inject
class SubtasksFilterUpdater @Inject constructor(
private val taskListMetadataDao: TaskListMetadataDao,
private val taskDao: TaskDao) {
private val taskDao: TaskDao
) {
private val idToNode = HashMap<String, Node?>()
private var treeRoot: Node? = null
private fun getSerializedTree(list: TaskListMetadata?): String? {
@ -38,12 +39,12 @@ class SubtasksFilterUpdater @Inject constructor(
}
}
suspend fun initialize(list: TaskListMetadata?, filter: Filter) {
suspend fun initialize(list: TaskListMetadata?, filter: AstridOrderingFilter) {
initializeFromSerializedTree(list, filter, getSerializedTree(list))
applyToFilter(filter)
}
private fun applyToFilter(filter: Filter) {
private fun applyToFilter(filter: AstridOrderingFilter) {
var query = filter.getSqlQuery()
query = query.replace("ORDER BY .*".toRegex(), "")
query += "ORDER BY $orderString"
@ -56,13 +57,13 @@ class SubtasksFilterUpdater @Inject constructor(
return n.indent
}
suspend fun initializeFromSerializedTree(list: TaskListMetadata?, filter: Filter, serializedTree: String?) {
suspend fun initializeFromSerializedTree(list: TaskListMetadata?, filter: AstridOrderingFilter, serializedTree: String?) {
idToNode.clear()
treeRoot = buildTreeModel(serializedTree) { node -> node?.let { idToNode[it.uuid] = it } }
verifyTreeModel(list, filter)
}
private suspend fun verifyTreeModel(list: TaskListMetadata?, filter: Filter) {
private suspend fun verifyTreeModel(list: TaskListMetadata?, filter: AstridOrderingFilter) {
var changedThings = false
val keySet: Set<String> = idToNode.keys
val currentIds: MutableSet<String> = HashSet(keySet)
@ -146,12 +147,12 @@ class SubtasksFilterUpdater @Inject constructor(
}
}
suspend fun indent(list: TaskListMetadata, filter: Filter, targetTaskId: String?, delta: Int) {
suspend fun indent(list: TaskListMetadata, filter: AstridOrderingFilter, targetTaskId: String?, delta: Int) {
val node = idToNode[targetTaskId]
indentHelper(list, filter, node, delta)
}
private suspend fun indentHelper(list: TaskListMetadata, filter: Filter, node: Node?, delta: Int) {
private suspend fun indentHelper(list: TaskListMetadata, filter: AstridOrderingFilter, node: Node?, delta: Int) {
if (node == null) {
return
}
@ -205,7 +206,7 @@ class SubtasksFilterUpdater @Inject constructor(
}
}
suspend fun moveTo(list: TaskListMetadata, filter: Filter, targetTaskId: String?, beforeTaskId: String) {
suspend fun moveTo(list: TaskListMetadata, filter: AstridOrderingFilter, targetTaskId: String?, beforeTaskId: String) {
val target = idToNode[targetTaskId] ?: return
if ("-1" == beforeTaskId) { // $NON-NLS-1$
moveToEndOfList(list, filter, target)
@ -229,7 +230,7 @@ class SubtasksFilterUpdater @Inject constructor(
setNodeIndent(toMove, toMove.parent!!.indent + 1)
}
private suspend fun moveHelper(list: TaskListMetadata, filter: Filter, moveThis: Node, beforeThis: Node) {
private suspend fun moveHelper(list: TaskListMetadata, filter: AstridOrderingFilter, moveThis: Node, beforeThis: Node) {
val oldParent = moveThis.parent
val oldSiblings = oldParent!!.children
val newParent = beforeThis.parent
@ -269,7 +270,7 @@ class SubtasksFilterUpdater @Inject constructor(
return false
}
private suspend fun moveToEndOfList(list: TaskListMetadata, filter: Filter, moveThis: Node) {
private suspend fun moveToEndOfList(list: TaskListMetadata, filter: AstridOrderingFilter, moveThis: Node) {
val parent = moveThis.parent
parent!!.children.remove(moveThis)
treeRoot!!.children.add(moveThis)
@ -279,7 +280,7 @@ class SubtasksFilterUpdater @Inject constructor(
applyToFilter(filter)
}
suspend fun onCreateTask(list: TaskListMetadata?, filter: Filter, uuid: String) {
suspend fun onCreateTask(list: TaskListMetadata?, filter: AstridOrderingFilter, uuid: String) {
if (idToNode.containsKey(uuid) || !isValidUuid(uuid)) {
return
}
@ -290,7 +291,7 @@ class SubtasksFilterUpdater @Inject constructor(
applyToFilter(filter)
}
suspend fun onDeleteTask(list: TaskListMetadata?, filter: Filter, taskId: String?) {
suspend fun onDeleteTask(list: TaskListMetadata?, filter: AstridOrderingFilter, taskId: String?) {
val task = idToNode[taskId] ?: return
val parent = task.parent
val siblings = parent!!.children

@ -1,6 +1,7 @@
package com.todoroo.astrid.subtasks
import android.content.Context
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.core.BuiltInFilterExposer.Companion.isInbox
import com.todoroo.astrid.core.BuiltInFilterExposer.Companion.isTodayFilter
@ -31,7 +32,7 @@ class SubtasksHelper @Inject constructor(
originalQuery: String
): String {
var query = originalQuery
if (filter.supportsAstridSorting() && preferences.isAstridSort) {
if (filter is AstridOrderingFilter && preferences.isAstridSort) {
val tagData = tagDataDao.getTagByName(filter.title!!)
val tlm = when {
tagData != null ->

@ -12,6 +12,7 @@ import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.utility.Constants
import dagger.hilt.android.qualifiers.ApplicationContext
@ -94,12 +95,17 @@ class TimerPlugin @Inject constructor(
companion object {
fun createFilter(context: Context): Filter {
val filter = Filter(
context.getString(R.string.TFE_workingOn),
QueryTemplate()
.where(and(Task.TIMER_START.gt(0), Task.DELETION_DATE.eq(0))))
filter.icon = R.drawable.ic_outline_timer_24px
return filter
return FilterImpl(
title = context.getString(R.string.TFE_workingOn),
sql = QueryTemplate()
.where(
and(
Task.TIMER_START.gt(0),
Task.DELETION_DATE.eq(0)
)
).toString(),
icon = R.drawable.ic_outline_timer_24px,
)
}
}
}

@ -243,7 +243,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
title = newName,
color = selectedColor,
icon = selectedIcon,
values = AndroidUtilities.mapToSerializedString(criteria.values),
values = criteria.values,
criterion = CriterionInstance.serialize(criteria),
sql = criteria.sql,
order = filter?.order ?: NO_ORDER,
@ -384,7 +384,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
return sql.toString()
}
private val List<CriterionInstance>.values: Map<String, Any>
private val List<CriterionInstance>.values: String
get() {
val values: MutableMap<String, Any> = HashMap()
for (instance in this) {
@ -396,7 +396,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
}
}
}
return values
return AndroidUtilities.mapToSerializedString(values)
}
}
}

@ -210,7 +210,6 @@ class NavigationDrawerCustomization : ThemedInjectingAppCompatActivity(), Toolba
(viewHolder as FilterViewHolder).setMoving(false)
if (from != to) {
viewHolder.filter.order = to
lifecycleScope.launch {
adapter.items
.apply {
@ -218,9 +217,9 @@ class NavigationDrawerCustomization : ThemedInjectingAppCompatActivity(), Toolba
add(to, viewHolder.filter)
}
.filter(getPredicate(viewHolder.filter))
// TODO: use transaction, or shift positions with a single query
.forEachIndexed { order, filter ->
if (filter is Filter) {
filter.order = order
setOrder(order, filter)
}
}

@ -8,8 +8,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import org.tasks.R
import org.tasks.compose.ChipGroup
import org.tasks.compose.FilterChip
@ -48,7 +48,7 @@ fun ListRow(
fun ListPreview() {
MdcTheme {
ListRow(
list = Filter("Default list", QueryTemplate()),
list = FilterImpl("Default list", ""),
colorProvider = { -769226 },
onClick = {},
)

@ -222,7 +222,7 @@ FROM recursive_tasks
return count
}
suspend fun fetchFiltered(filter: Filter): List<Task> = fetchFiltered(filter.getSqlQuery())
suspend fun fetchFiltered(filter: Filter): List<Task> = fetchFiltered(filter.sql!!)
suspend fun fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS)

@ -3,6 +3,7 @@ package org.tasks.data
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Field.Companion.field
import com.todoroo.andlib.sql.Join
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.data.Task
import org.tasks.data.TaskListQueryNonRecursive.getNonRecursiveQuery
@ -35,7 +36,7 @@ object TaskListQuery {
): MutableList<String> = when {
filter.supportsManualSort() && preferences.isManualSort ->
getRecursiveQuery(filter, preferences)
filter.supportsAstridSorting() && preferences.isAstridSort ->
filter is AstridOrderingFilter && preferences.isAstridSort ->
getNonRecursiveQuery(filter, preferences)
filter.supportsSorting() ->
getRecursiveQuery(filter, preferences)

@ -3,6 +3,7 @@ package org.tasks.data
import com.todoroo.andlib.sql.Field.Companion.field
import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.Query
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.core.SortHelper
@ -28,7 +29,7 @@ internal object TaskListQueryNonRecursive {
)).toTypedArray()
fun getNonRecursiveQuery(filter: Filter, preferences: QueryPreferences): MutableList<String> {
val joinedQuery = JOINS + filter.getSqlQuery()
val joinedQuery = JOINS + if (filter is AstridOrderingFilter) filter.getSqlQuery() else filter.sql!!
val sortMode = preferences.sortMode
val groupMode = preferences.groupMode
val sortGroup = field(SortHelper.getSortGroup(groupMode) ?: "NULL").`as`("sortGroup")

@ -45,7 +45,7 @@ internal object TaskListQueryRecursive {
val parentQuery = when (filter) {
is CaldavFilter -> newCaldavQuery(filter.uuid)
is GtasksFilter -> newCaldavQuery(filter.list.uuid!!)
else -> PermaSql.replacePlaceholdersForQuery(filter.getSqlQuery())
else -> PermaSql.replacePlaceholdersForQuery(filter.sql!!)
}
val manualSort = preferences.isManualSort
val groupPreference = preferences.groupMode

@ -7,7 +7,6 @@ import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.LifecycleOwner
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
@ -28,11 +27,7 @@ class FilterPicker : DialogFragment() {
onSelected = { filter ->
val data = Bundle()
data.putParcelable(EXTRA_FILTER, filter)
if (filter.valuesForNewTasks != null) {
data.putString(
EXTRA_FILTER_VALUES,
AndroidUtilities.mapToSerializedString(filter.valuesForNewTasks))
}
filter.valuesForNewTasks?.let { data.putString(EXTRA_FILTER_VALUES, it) }
setFragmentResult(SELECT_FILTER, data)
dismiss()
}

@ -287,7 +287,8 @@ class FilterProvider @Inject constructor(
companion object {
private val COMPARATOR = Comparator<Filter> { f1, f2 ->
when {
f1.order == NO_ORDER && f2.order == NO_ORDER -> f1.id.compareTo(f2.id)
f1.order == NO_ORDER && f2.order == NO_ORDER ->
AlphanumComparator.FILTER.compare(f1, f2)
f1.order == NO_ORDER -> 1
f2.order == NO_ORDER -> -1
f1.order < f2.order -> -1

@ -0,0 +1,33 @@
package org.tasks.filters
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.data.TaskDao
import org.tasks.themes.CustomIcons
@Parcelize
data class MyTasksFilter(
override val title: String,
override var filterOverride: String? = null,
override var count: Int = NO_COUNT,
) : AstridOrderingFilter {
override val icon: Int
get() = CustomIcons.ALL_INBOX
override val sql: String
get() = QueryTemplate()
.where(
Criterion.and(
TaskDao.TaskCriteria.activeAndVisible(),
Task.PARENT.eq(0)
)
).toString()
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is MyTasksFilter
}
}

@ -12,6 +12,4 @@ data class NavigationDrawerAction(
override val itemType = FilterListItem.Type.ACTION
override fun areItemsTheSame(other: FilterListItem) = this == other
override fun areContentsTheSame(other: FilterListItem) = true
}

@ -8,8 +8,4 @@ class NavigationDrawerSeparator : FilterListItem {
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is NavigationDrawerSeparator
}
override fun areContentsTheSame(other: FilterListItem): Boolean {
return true
}
}

@ -16,10 +16,6 @@ data class NavigationDrawerSubheader(
return other is NavigationDrawerSubheader && subheaderType == other.subheaderType && id == other.id
}
override fun areContentsTheSame(other: FilterListItem): Boolean {
return this == other
}
override val itemType = FilterListItem.Type.SUBHEADER
enum class SubheaderType {

@ -1,36 +1,30 @@
package org.tasks.filters
import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.R
import org.tasks.notifications.Notification
class NotificationsFilter : Filter {
constructor(context: Context) : super(context.getString(R.string.notifications), queryTemplate)
private constructor()
@Parcelize
data class NotificationsFilter(
override val title: String,
override var count: Int = NO_COUNT,
) : Filter {
override val icon: Int
get() = R.drawable.ic_outline_notifications_24px
override val sql: String
get() = QueryTemplate()
.join(Join.inner(Notification.TABLE, Task.ID.eq(Notification.TASK)))
.toString()
override fun supportsHiddenTasks(): Boolean = false
companion object {
@JvmField val CREATOR: Parcelable.Creator<NotificationsFilter> = object : Parcelable.Creator<NotificationsFilter> {
/** {@inheritDoc} */
override fun createFromParcel(source: Parcel): NotificationsFilter =
NotificationsFilter().apply {
readFromParcel(source)
}
/** {@inheritDoc} */
override fun newArray(size: Int): Array<NotificationsFilter?> = arrayOfNulls(size)
}
private val queryTemplate: QueryTemplate
get() = QueryTemplate()
.join(Join.inner(Notification.TABLE, Task.ID.eq(Notification.TASK)))
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is NotificationsFilter
}
}

@ -1,49 +1,50 @@
package org.tasks.filters
import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import androidx.core.os.ParcelCompat
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Field.Companion.field
import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.data.Task
import org.tasks.R
import kotlinx.parcelize.Parcelize
import org.tasks.data.Geofence
import org.tasks.data.Place
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
import org.tasks.themes.CustomIcons
class PlaceFilter(
val place: Place
) : Filter(place.displayName, queryTemplate(place), getValuesForNewTask(place)) {
init {
id = place.id
tint = place.color
icon = place.icon
if (icon == -1) {
icon = CustomIcons.PLACE
}
order = place.order
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(place, 0)
}
@Parcelize
data class PlaceFilter(
val place: Place,
override var count: Int = NO_COUNT,
) : Filter {
override val valuesForNewTasks: String
get() = AndroidUtilities.mapToSerializedString(mapOf(Place.KEY to place.uid!!))
override val sql: String
get() = QueryTemplate()
.join(inner(G2, Task.ID.eq(G2_TASK)))
.join(inner(P2, P2_UID.eq(G2_PLACE)))
.where(and(activeAndVisible(), G2_PLACE.eq(place.uid)))
.toString()
override val order: Int
get() = place.order
override val icon: Int
get() = place.icon.takeIf { it != -1 } ?: CustomIcons.PLACE
override val title: String
get() = place.displayName
override val tint: Int
get() = place.color
val uid: String
get() = place.uid!!
override val beginningMenu = R.menu.menu_location_actions
override val menu = R.menu.menu_location_list_fragment
override fun areContentsTheSame(other: FilterListItem): Boolean {
return place == (other as PlaceFilter).place && count == other.count
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is PlaceFilter && place.id == other.place.id
}
fun openMap(context: Context?) {
@ -51,35 +52,10 @@ class PlaceFilter(
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<PlaceFilter> = object : Parcelable.Creator<PlaceFilter> {
override fun createFromParcel(source: Parcel): PlaceFilter {
return PlaceFilter(
ParcelCompat.readParcelable(source, javaClass.classLoader, Place::class.java)!!
)
}
/** {@inheritDoc} */
override fun newArray(size: Int): Array<PlaceFilter?> {
return arrayOfNulls(size)
}
}
private val G2 = Geofence.TABLE.`as`("G2")
private val G2_PLACE = field("G2.place")
private val G2_TASK = field("G2.task")
private val P2 = Place.TABLE.`as`("P2")
private val P2_UID = field("P2.uid")
private fun queryTemplate(place: Place): QueryTemplate {
return QueryTemplate()
.join(inner(G2, Task.ID.eq(G2_TASK)))
.join(inner(P2, P2_UID.eq(G2_PLACE)))
.where(and(activeAndVisible(), G2_PLACE.eq(place.uid)))
}
private fun getValuesForNewTask(place: Place): Map<String, Any> {
val result: MutableMap<String, Any> = HashMap()
result[Place.KEY] = place.uid!!
return result
}
}
}

@ -1,17 +1,36 @@
package org.tasks.filters
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Order.Companion.desc
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.themes.CustomIcons
import org.tasks.time.DateTime
class RecentlyModifiedFilter : Filter {
constructor(title: String?) : super(title, queryTemplate)
private constructor()
@Parcelize
data class RecentlyModifiedFilter(
override val title: String,
override var count: Int = NO_COUNT,
) : Filter {
override val icon: Int
get() = CustomIcons.HISTORY
override val sql: String
get() = QueryTemplate()
.where(
and(
Task.DELETION_DATE.lte(0),
Task.MODIFICATION_DATE.gt(
DateTime().minusDays(1).startOfMinute().millis
)
)
)
.orderBy(desc(Task.MODIFICATION_DATE))
.toString()
override fun supportsHiddenTasks() = false
@ -19,30 +38,7 @@ class RecentlyModifiedFilter : Filter {
override fun supportsSorting() = false
companion object {
@JvmField
val CREATOR: Parcelable.Creator<RecentlyModifiedFilter> =
object : Parcelable.Creator<RecentlyModifiedFilter> {
override fun createFromParcel(source: Parcel): RecentlyModifiedFilter? {
val item = RecentlyModifiedFilter()
item.readFromParcel(source)
return item
}
override fun newArray(size: Int): Array<RecentlyModifiedFilter?> {
return arrayOfNulls(size)
}
}
private val queryTemplate: QueryTemplate
get() = QueryTemplate()
.where(
and(
Task.DELETION_DATE.lte(0),
Task.MODIFICATION_DATE.gt(
DateTime().minusDays(1).startOfMinute().millis
)
)
)
.orderBy(desc(Task.MODIFICATION_DATE))
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is RecentlyModifiedFilter
}
}

@ -1,39 +1,38 @@
package org.tasks.filters
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.R
import org.tasks.data.Alarm
class SnoozedFilter : Filter {
constructor(title: String?) : super(title, queryTemplate)
private constructor()
@Parcelize
data class SnoozedFilter(
override val title: String,
override var count: Int = NO_COUNT,
) : Filter {
override val icon: Int
get() = R.drawable.ic_snooze_white_24dp
override fun supportsHiddenTasks(): Boolean {
return false
}
override val sql: String
get() = QueryTemplate()
.join(inner(Alarm.TABLE, Task.ID.eq(Alarm.TASK)))
.where(
and(
Task.DELETION_DATE.lte(0),
Alarm.TYPE.eq(Alarm.TYPE_SNOOZE)
)
)
.toString()
companion object {
@JvmField
val CREATOR: Parcelable.Creator<SnoozedFilter> =
object : Parcelable.Creator<SnoozedFilter> {
override fun createFromParcel(source: Parcel): SnoozedFilter {
val item = SnoozedFilter()
item.readFromParcel(source)
return item
}
override fun supportsHiddenTasks() = false
override fun newArray(size: Int): Array<SnoozedFilter?> {
return arrayOfNulls(size)
}
}
private val queryTemplate: QueryTemplate
get() = QueryTemplate()
.join(inner(Alarm.TABLE, Task.ID.eq(Alarm.TASK)))
.where(and(Task.DELETION_DATE.lte(0), Alarm.TYPE.eq(Alarm.TYPE_SNOOZE)))
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is SnoozedFilter
}
}

@ -1,37 +0,0 @@
package org.tasks.filters
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter
class SortableFilter : Filter {
constructor(title: String?, sql: QueryTemplate) : super(title, sql)
constructor(
title: String?, sql: QueryTemplate, valuesForNewTasks: Map<String, Any>
) : super(title, sql, valuesForNewTasks)
private constructor()
override fun supportsAstridSorting(): Boolean {
return true
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<SortableFilter> =
object : Parcelable.Creator<SortableFilter> {
/** {@inheritDoc} */
override fun createFromParcel(source: Parcel): SortableFilter {
val item = SortableFilter()
item.readFromParcel(source)
return item
}
/** {@inheritDoc} */
override fun newArray(size: Int): Array<SortableFilter?> {
return arrayOfNulls(size)
}
}
}
}

@ -0,0 +1,40 @@
package org.tasks.filters
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.data.TaskDao
import org.tasks.themes.CustomIcons
@Parcelize
data class TodayFilter(
override val title: String,
override var filterOverride: String? = null,
override var count: Int = NO_COUNT,
) : AstridOrderingFilter {
override val sql: String
get() = QueryTemplate()
.where(
Criterion.and(
TaskDao.TaskCriteria.activeAndVisible(),
Task.DUE_DATE.gt(0),
Task.DUE_DATE.lte(PermaSql.VALUE_EOD)
)
)
.toString()
override val icon: Int
get() = CustomIcons.TODAY
override val valuesForNewTasks: String
get() = AndroidUtilities.mapToSerializedString(mapOf(Task.DUE_DATE.name to PermaSql.VALUE_NOON))
override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is TodayFilter
}
}

@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.core.BuiltInFilterExposer
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.LocalBroadcastManager
import org.tasks.R
@ -13,7 +14,6 @@ import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.Alarm
import org.tasks.data.LocationDao
import org.tasks.data.TaskDao
import org.tasks.filters.NotificationsFilter
import org.tasks.intents.TaskIntents
import org.tasks.markdown.MarkdownProvider
import org.tasks.preferences.PermissionChecker
@ -253,7 +253,7 @@ class NotificationManager @Inject constructor(
PendingIntent.getActivity(
context,
0,
TaskIntents.getTaskListIntent(context, NotificationsFilter(context)),
TaskIntents.getTaskListIntent(context, BuiltInFilterExposer.getNotificationsFilter(context)),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)

@ -10,6 +10,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.core.SortHelper.SORT_ALPHA
import com.todoroo.astrid.core.SortHelper.SORT_CREATED
@ -135,7 +136,7 @@ class ScrollableWidget : InjectingPreferenceFragment() {
SortSettingsActivity.getIntent(
requireActivity(),
filter.supportsManualSort(),
filter.supportsAstridSorting(),
filter is AstridOrderingFilter,
appWidgetId,
),
REQUEST_SORT
@ -221,7 +222,7 @@ class ScrollableWidget : InjectingPreferenceFragment() {
findPreference(R.string.p_widget_sort).setSummary(
if (filter.supportsManualSort() && widgetPreferences.isManualSort) {
R.string.SSD_sort_my_order
} else if (filter.supportsAstridSorting() && widgetPreferences.isAstridSort) {
} else if (filter is AstridOrderingFilter && widgetPreferences.isAstridSort) {
R.string.astrid_sort_order
} else {
when (widgetPreferences.sortMode) {

@ -14,6 +14,7 @@ import androidx.recyclerview.widget.ItemTouchHelper.UP
import androidx.recyclerview.widget.RecyclerView
import com.todoroo.astrid.activity.TaskListFragment
import com.todoroo.astrid.adapter.TaskAdapter
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.utility.Flags
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -39,7 +40,7 @@ class DragAndDropRecyclerAdapter(
private val disableHeaders = taskList.getFilter().let {
!it.supportsSorting()
|| (it.supportsManualSort() && preferences.isManualSort)
|| (it.supportsAstridSorting() && preferences.isAstridSort)
|| (it is AstridOrderingFilter && preferences.isAstridSort)
}
private val itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback()).apply {
attachToRecyclerView(recyclerView)

@ -6,6 +6,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.todoroo.astrid.activity.TaskListFragment
import com.todoroo.astrid.adapter.TaskAdapter
import com.todoroo.astrid.adapter.TaskAdapterDataSource
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.core.SortHelper
import org.tasks.data.TaskContainer
import org.tasks.preferences.Preferences
@ -24,7 +25,7 @@ abstract class TaskListRecyclerAdapter internal constructor(
val filter = taskList.getFilter()
val groupsEnabled = filter.supportsSorting()
&& !(filter.supportsManualSort() && preferences.isManualSort)
&& !(filter.supportsAstridSorting() && preferences.isAstridSort)
&& !(filter is AstridOrderingFilter && preferences.isAstridSort)
val task = getItem(position)
if (task != null) {
(holder as TaskViewHolder)

@ -6,6 +6,7 @@ import android.content.Intent
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.api.FilterListItem
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@ -51,7 +52,7 @@ class NavigationDrawerViewModel @Inject constructor(
filterProvider
.navDrawerItems()
.onEach {
if (it is Filter && it.count == -1) {
if (it is Filter && it.count == NO_COUNT) {
it.count = try {
taskDao.count(it)
} catch (e: Exception) {

@ -11,7 +11,7 @@ import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskCompleter
@ -44,7 +44,7 @@ class SubtaskControlSet : TaskEditControlFragment() {
override fun createView(savedInstanceState: Bundle?) {
viewModel.task.takeIf { it.id > 0 }?.let {
listViewModel.setFilter(Filter("subtasks", getQueryTemplate(it)))
listViewModel.setFilter(FilterImpl("subtasks", getQueryTemplate(it)))
}
}
@ -105,12 +105,13 @@ class SubtaskControlSet : TaskEditControlFragment() {
companion object {
val TAG = R.string.TEA_ctrl_subtask_pref
private fun getQueryTemplate(task: Task): QueryTemplate = QueryTemplate()
private fun getQueryTemplate(task: Task): String = QueryTemplate()
.where(
Criterion.and(
activeAndVisible(),
Task.PARENT.eq(task.id)
)
)
.toString()
}
}

@ -9,6 +9,7 @@ import android.widget.RemoteViews
import android.widget.RemoteViewsService.RemoteViewsFactory
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.core.SortHelper
import com.todoroo.astrid.data.Task
@ -367,7 +368,7 @@ internal class ScrollableViewsFactory(
disableGroups = filter?.let {
!it.supportsSorting()
|| (it.supportsManualSort() && widgetPreferences.isManualSort)
|| (it.supportsAstridSorting() && widgetPreferences.isAstridSort)
|| (it is AstridOrderingFilter && widgetPreferences.isAstridSort)
} == true
showPlaces = widgetPreferences.showPlaces()
showSubtasks = widgetPreferences.showSubtasks()

@ -150,7 +150,7 @@ unstable class MainActivity {
unstable var firebase: Firebase
stable var currentNightMode: Int
stable var currentPro: Boolean
unstable var filter: Filter?
runtime var filter: Filter?
unstable var actionMode: ActionMode?
unstable var binding: TaskListActivityBinding
<runtime stability> = Unstable
@ -211,7 +211,7 @@ unstable class TaskListFragment {
unstable val listViewModel$delegate: Lazy<TaskListViewModel>
unstable var taskAdapter: TaskAdapter
unstable var recyclerAdapter: DragAndDropRecyclerAdapter?
unstable var filter: Filter
runtime var filter: Filter
unstable var search: MenuItem
unstable var mode: ActionMode?
unstable var themeColor: ThemeColor
@ -232,11 +232,11 @@ unstable class ActionViewHolder {
}
unstable class AstridTaskAdapter {
unstable val list: TaskListMetadata
unstable val filter: Filter
runtime val filter: AstridOrderingFilter
unstable val updater: SubtasksFilterUpdater
unstable val taskDao: TaskDao
unstable val localBroadcastManager: LocalBroadcastManager
unstable val chainedCompletions: @[FlexibleNullability] @[FlexibleMutability] MutableMap<@[FlexibleNullability] String?, @[FlexibleNullability] ArrayList<String>?>?
unstable val chainedCompletions: @[FlexibleNullability] @[FlexibleMutability] MutableMap<@[FlexibleNullability] String?, @[FlexibleNullability] ArrayList<String>?{ kotlin.collections.TypeAliasesKt.ArrayList<String>? }>?
<runtime stability> = Unstable
}
stable class CaldavManualSortTaskAdapter {
@ -254,7 +254,7 @@ unstable class FilterViewHolder {
unstable val icon: ImageView
unstable val size: TextView
unstable val shareIndicator: ImageView
unstable var filter: Filter
runtime var filter: Filter
<runtime stability> = Unstable
}
stable class GoogleTaskManualSortAdapter {
@ -267,7 +267,7 @@ unstable class NavigationDrawerAdapter {
unstable val colorProvider: ColorProvider
unstable val subheaderClickHandler: SubheaderClickHandler
stable var onClick: Function1<FilterListItem?, Unit>
unstable var selected: Filter?
runtime var selected: Filter?
unstable val channel: Channel<List<FilterListItem>>
unstable val updates: Queue<Pair<MutableList<FilterListItem>, DiffResult?>>
unstable val scope: CoroutineScope
@ -329,42 +329,38 @@ stable class BooleanCriterion {
unstable class CaldavFilter {
unstable val calendar: CaldavCalendar
stable val principals: Int
stable val menu: Int
stable var count: Int
<runtime stability> = Unstable
}
unstable class CustomFilter {
stable var criterion: String?
stable val filter: Filter
stable var count: Int
<runtime stability> = Unstable
}
unstable class Filter {
unstable val valuesForNewTasks: MutableMap<String, Any>
stable var originalSqlQuery: String?
stable var filterOverride: String?
stable var id: Long
stable var icon: Int
stable var listingTitle: String?
stable var tint: Int
unstable class FilterImpl {
stable val title: String?
stable val sql: String?
stable val valuesForNewTasks: String?
stable val icon: Int
stable val tint: Int
stable var count: Int
stable var order: Int
stable val itemType: Type
stable val isReadOnly: Boolean
<runtime stability> = Unstable
}
unstable class GtasksFilter {
unstable val list: CaldavCalendar
stable val menu: Int
stable var count: Int
<runtime stability> = Unstable
}
unstable class IdListFilter {
unstable var ids: List<Long?>?
unstable class SearchFilter {
stable val title: String
stable val query: String
stable var count: Int
<runtime stability> = Unstable
}
stable class SearchFilter {
<runtime stability> = Stable
}
unstable class TagFilter {
unstable val tagData: TagData
stable val menu: Int
stable var count: Int
stable var filterOverride: String?
<runtime stability> = Unstable
}
unstable class BuiltInFilterExposer {
@ -1366,16 +1362,16 @@ runtime class ContentProviderDaoBlocking {
stable class DeletionDao {
<runtime stability> = Stable
}
unstable class Filter {
stable var id: Long
stable var title: String?
stable var sql: String?
stable var values: String?
stable var criterion: String?
stable var color: Int?
stable var icon: Int?
stable var order: Int
<runtime stability> = Unstable
stable class Filter {
stable val id: Long
stable val title: String?
stable val sql: String?
stable val values: String?
stable val criterion: String?
stable val color: Int?
stable val icon: Int?
stable val order: Int
<runtime stability> = Stable
}
unstable class Geofence {
stable val id: Long
@ -1931,8 +1927,14 @@ unstable class LocationFilters {
stable var count: Int
<runtime stability> = Unstable
}
unstable class MyTasksFilter {
stable val title: String
stable var filterOverride: String?
stable var count: Int
<runtime stability> = Unstable
}
unstable class NavigationDrawerAction {
stable val listingTitle: String
stable val title: String
stable val icon: Int
stable val requestCode: Int
unstable val intent: Intent?
@ -1944,7 +1946,7 @@ stable class NavigationDrawerSeparator {
<runtime stability> = Stable
}
unstable class NavigationDrawerSubheader {
stable val listingTitle: String?
stable val title: String?
stable val error: Boolean
stable val isCollapsed: Boolean
stable val subheaderType: SubheaderType
@ -1954,29 +1956,37 @@ unstable class NavigationDrawerSubheader {
stable val itemType: Type
<runtime stability> = Unstable
}
stable class NotificationsFilter {
<runtime stability> = Stable
unstable class NotificationsFilter {
stable val title: String
stable var count: Int
<runtime stability> = Unstable
}
stable class PlaceFilter {
unstable class PlaceFilter {
stable val place: Place
stable val beginningMenu: Int
stable val menu: Int
<runtime stability> = Stable
}
stable class RecentlyModifiedFilter {
<runtime stability> = Stable
stable var count: Int
<runtime stability> = Unstable
}
stable class SnoozedFilter {
<runtime stability> = Stable
unstable class RecentlyModifiedFilter {
stable val title: String
stable var count: Int
<runtime stability> = Unstable
}
stable class SortableFilter {
<runtime stability> = Stable
unstable class SnoozedFilter {
stable val title: String
stable var count: Int
<runtime stability> = Unstable
}
unstable class TagFilters {
unstable var tagData: TagData
stable var count: Int
<runtime stability> = Unstable
}
unstable class TodayFilter {
stable val title: String
stable var filterOverride: String?
stable var count: Int
<runtime stability> = Unstable
}
unstable class CommentBarFragment {
unstable var activity: Activity
unstable var dialogBuilder: DialogBuilder
@ -2575,7 +2585,7 @@ unstable class TaskListPreferences {
unstable class TaskerListNotification {
unstable var defaultFilterProvider: DefaultFilterProvider
unstable var inventory: Inventory
unstable var filter: Filter
runtime var filter: Filter
stable var cancelled: Boolean
<runtime stability> = Unstable
}
@ -3064,7 +3074,7 @@ unstable class NavigationDrawerFragment {
<runtime stability> = Unstable
}
unstable class ViewState {
unstable val selected: Filter?
runtime val selected: Filter?
unstable val filters: List<FilterListItem>
<runtime stability> = Unstable
}
@ -3144,7 +3154,7 @@ unstable class TaskEditViewModel {
unstable val startDate: MutableStateFlow<Long>
stable var originalCalendar: String?
unstable var selectedCalendar: MutableStateFlow<String?>
unstable val originalList: Filter
runtime val originalList: Filter
unstable var selectedList: MutableStateFlow<Filter>
unstable var originalLocation: Location?
unstable var selectedLocation: MutableStateFlow<Location?>
@ -3170,7 +3180,7 @@ stable class CalendarEventCreated {
<runtime stability> = Stable
}
unstable class State {
unstable val filter: Filter?
runtime val filter: Filter?
stable val now: Long
stable val searchQuery: String?
unstable val tasks: List<TaskContainer>
@ -3215,7 +3225,7 @@ unstable class ShortcutConfigActivity {
unstable var shortcutName: TextInputEditText
unstable var colorIcon: TextView
unstable var clear: View
unstable var selectedFilter: Filter?
runtime var selectedFilter: Filter?
stable var selectedTheme: Int
<runtime stability> = Unstable
}

@ -193,8 +193,8 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisabledT
stable text: String
stable modifier: Modifier? = @static Companion
)
restartable scheme("[androidx.compose.ui.UiComposable]") fun FilterChip(
unstable filter: Filter
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun FilterChip(
filter: Filter
stable defaultIcon: Int
stable showText: Boolean
stable showIcon: Boolean
@ -482,8 +482,8 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun InfoRow(
unstable locale: Locale? = @dynamic getDefault()
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun InfoPreview()
restartable scheme("[androidx.compose.ui.UiComposable]") fun ListRow(
unstable list: Filter?
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ListRow(
list: Filter?
stable colorProvider: Function1<Int, Int>
stable onClick: Function0<Unit>
)
@ -567,8 +567,8 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun NoStartDa
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun FutureStartDate()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun PastStartDate()
restartable scheme("[androidx.compose.ui.UiComposable]") fun SubtaskRow(
unstable originalFilter: Filter?
unstable filter: Filter?
originalFilter: Filter?
filter: Filter?
stable hasParent: Boolean
stable desaturate: Boolean
unstable existingSubtasks: List<TaskContainer>
@ -701,9 +701,9 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DatePicke
stable dismiss: Function0<Unit>
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DatePickerPreview()
restartable scheme("[androidx.compose.ui.UiComposable]") fun FilterPicker(
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun FilterPicker(
unstable viewModel: FilterPickerViewModel? = @dynamic viewModel(null, null, null, null, $composer, 0, 0b1111)
unstable selected: Filter?
selected: Filter?
stable onSelected: Function1<Filter, Unit>
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SortSheetContent(
@ -774,7 +774,7 @@ restartable scheme("[androidx.compose.ui.UiComposable]") fun StartDateChip(
unstable <this>: ChipProvider
)
restartable scheme("[androidx.compose.ui.UiComposable]") fun Chips(
unstable filter: Filter?
filter: Filter?
stable id: Long
stable children: Int
stable collapsed: Boolean

@ -1,21 +1,21 @@
{
"skippableComposables": 361,
"skippableComposables": 364,
"restartableComposables": 490,
"readonlyComposables": 0,
"totalComposables": 496,
"restartGroups": 490,
"totalGroups": 598,
"totalGroups": 599,
"staticArguments": 762,
"certainArguments": 328,
"knownStableArguments": 4879,
"knownUnstableArguments": 146,
"unknownStableArguments": 2,
"knownStableArguments": 4878,
"knownUnstableArguments": 139,
"unknownStableArguments": 10,
"totalArguments": 5027,
"markedStableClasses": 0,
"inferredStableClasses": 105,
"inferredUnstableClasses": 340,
"inferredStableClasses": 100,
"inferredUnstableClasses": 345,
"inferredUncertainClasses": 1,
"effectivelyStableClasses": 105,
"effectivelyStableClasses": 100,
"totalClasses": 446,
"memoizedLambdas": 524,
"singletonLambdas": 182,

Loading…
Cancel
Save