Convert filters to data classes (#2569)

pull/2578/head
Alex Baker 12 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 package com.todoroo.astrid.subtasks
import androidx.test.InstrumentationRegistry 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.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
@ -14,7 +14,7 @@ import javax.inject.Inject
abstract class SubtasksTestCase : InjectingTestCase() { abstract class SubtasksTestCase : InjectingTestCase() {
lateinit var updater: SubtasksFilterUpdater lateinit var updater: SubtasksFilterUpdater
lateinit var filter: Filter lateinit var filter: AstridOrderingFilter
@Inject lateinit var taskListMetadataDao: TaskListMetadataDao @Inject lateinit var taskListMetadataDao: TaskListMetadataDao
@Inject lateinit var taskDao: TaskDao @Inject lateinit var taskDao: TaskDao
@Inject lateinit var preferences: Preferences @Inject lateinit var preferences: Preferences

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

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

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

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

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

@ -1,29 +1,45 @@
package com.todoroo.astrid.api 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.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.left import com.todoroo.andlib.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate 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 com.todoroo.astrid.data.Task
import org.tasks.R import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.CaldavTask
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
class CaldavFilter( @Parcelize
data class CaldavFilter(
val calendar: CaldavCalendar, val calendar: CaldavCalendar,
val principals: Int = 0, 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 { override val order: Int
id = calendar.id get() = calendar.order
tint = calendar.color
icon = calendar.getIcon()!! override val tint: Int
order = calendar.order get() = calendar.color
} override val icon: Int
get() = calendar.getIcon()!!
val uuid: String val uuid: String
get() = calendar.uuid!! get() = calendar.uuid!!
@ -33,50 +49,9 @@ class CaldavFilter(
override val isReadOnly: Boolean override val isReadOnly: Boolean
get() = calendar.access == CaldavCalendar.ACCESS_READ_ONLY get() = calendar.access == CaldavCalendar.ACCESS_READ_ONLY
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(calendar, 0)
}
override fun supportsManualSort() = true override fun supportsManualSort() = true
override val menu = R.menu.menu_caldav_list_fragment override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is CaldavFilter && calendar.id == other.calendar.id
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
}
} }
} }

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

@ -1,173 +1,57 @@
package com.todoroo.astrid.api package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import androidx.annotation.MenuRes import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.andlib.sql.QueryTemplate import kotlinx.parcelize.Parcelize
open class Filter : FilterListItem, Parcelable { interface Filter : FilterListItem, Parcelable {
var valuesForNewTasks: Map<String, Any> = HashMap() val valuesForNewTasks: String?
var sql: String? = null get() = null
@Deprecated("for astrid manual order") var filterOverride: String? = null val sql: String?
var id = 0L val icon: Int
var icon = -1 get() = -1
var title: String? = null val title: String?
var tint = 0 val tint: Int
var count = -1 get() = 0
var order = NO_ORDER @Deprecated("Remove this")
var count: Int
/** val order: Int
* Utility constructor for creating a Filter object get() = NO_ORDER
* override val itemType: FilterListItem.Type
* @param title Title of this item as displayed on the lists page, e.g. Inbox get() = FilterListItem.Type.ITEM
* @param sql SQL query for this list (see [.sqlQuery] for examples). val isReadOnly: Boolean
*/ get() = false
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
val isWritable: Boolean val isWritable: Boolean
get() = !isReadOnly get() = !isReadOnly
fun hasBeginningMenu(): Boolean { fun supportsManualSort(): Boolean = false
return beginningMenu != 0 fun supportsHiddenTasks(): Boolean = true
} fun supportsSubtasks(): Boolean = true
fun supportsSorting(): Boolean = true
@get:MenuRes
open val beginningMenu: Int
get() = 0
fun hasMenu(): Boolean { companion object {
return menu != 0 const val NO_ORDER = -1
const val NO_COUNT = -1
} }
}
@get:MenuRes @Deprecated("Use manual ordering")
open val menu: Int interface AstridOrderingFilter : Filter {
get() = 0 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 { override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is Filter && id == other.id && sql == other.sql return other is Filter && 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)
}
}
} }
} }

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

@ -1,77 +1,58 @@
package com.todoroo.astrid.api 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.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.left import com.todoroo.andlib.sql.Join.Companion.left
import com.todoroo.andlib.sql.QueryTemplate 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 com.todoroo.astrid.data.Task
import org.tasks.R import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.CaldavTask
import org.tasks.data.GoogleTask import org.tasks.data.GoogleTask
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
class GtasksFilter( @Parcelize
val list: CaldavCalendar data class GtasksFilter(
) : Filter(list.name, getQueryTemplate(list), getValuesForNewTasks(list)) { 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 { override val valuesForNewTasks: String
id = list.id get() = AndroidUtilities.mapToSerializedString(mapOf(GoogleTask.KEY to list.uuid!!))
tint = list.color
icon = list.getIcon()!! override val order: Int
order = list.order get() = list.order
}
override val icon: Int
get() = list.getIcon()!!
override val tint: Int
get() = list.color
val account: String val account: String
get() = list.account!! get() = list.account!!
override fun supportsManualSort() = true override fun supportsManualSort() = true
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(list, 0)
}
val remoteId: String val remoteId: String
get() = list.uuid!! get() = list.uuid!!
override val menu = R.menu.menu_gtasks_list_fragment override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is GtasksFilter && list.id == other.list.id
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
}
} }
} }

@ -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 package com.todoroo.astrid.api
import android.os.Parcel
import android.os.Parcelable
import com.todoroo.andlib.sql.Criterion import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Join import com.todoroo.andlib.sql.Join
import com.todoroo.andlib.sql.Query import com.todoroo.andlib.sql.Query
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter.Companion.NO_COUNT
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.data.CaldavCalendar import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask import org.tasks.data.CaldavTask
import org.tasks.data.Geofence import org.tasks.data.Geofence
@ -14,59 +14,63 @@ import org.tasks.data.Place
import org.tasks.data.Tag import org.tasks.data.Tag
import org.tasks.data.UserActivity import org.tasks.data.UserActivity
class SearchFilter : Filter { @Parcelize
private constructor() data class SearchFilter(
override val title: String,
constructor(title: String?, query: String) : super(title, getQueryTemplate(query)) val query: String,
override var count: Int = NO_COUNT,
override fun supportsHiddenTasks() = false ) : Filter {
override val sql: String
companion object { get() {
/** 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 {
val matcher = "%$query%" val matcher = "%$query%"
return QueryTemplate() return QueryTemplate()
.where( .where(
Criterion.and( Criterion.and(
Task.DELETION_DATE.eq(0), Task.DELETION_DATE.eq(0),
Criterion.or( Criterion.or(
Task.NOTES.like(matcher), Task.NOTES.like(matcher),
Task.TITLE.like(matcher), Task.TITLE.like(matcher),
Task.ID.`in`( Task.ID.`in`(
Query.select(Tag.TASK) Query.select(Tag.TASK)
.from(Tag.TABLE) .from(Tag.TABLE)
.where(Tag.NAME.like(matcher))), .where(Tag.NAME.like(matcher))
Task.UUID.`in`( ),
Query.select(UserActivity.TASK) Task.UUID.`in`(
.from(UserActivity.TABLE) Query.select(UserActivity.TASK)
.where(UserActivity.MESSAGE.like(matcher))), .from(UserActivity.TABLE)
Task.ID.`in`( .where(UserActivity.MESSAGE.like(matcher))
Query.select(Geofence.TASK) ),
.from(Geofence.TABLE) Task.ID.`in`(
.join(Join.inner(Place.TABLE, Place.UID.eq(Geofence.PLACE))) Query.select(Geofence.TASK)
.where(Criterion.or(Place.NAME.like(matcher), .from(Geofence.TABLE)
Place.ADDRESS.like(matcher)))), .join(Join.inner(Place.TABLE, Place.UID.eq(Geofence.PLACE)))
Task.ID.`in`( .where(
Query.select(CaldavTask.TASK) Criterion.or(
.from(CaldavTask.TABLE) Place.NAME.like(matcher),
.join(Join.inner(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR))) Place.ADDRESS.like(matcher)
.where(CaldavCalendar.NAME.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 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.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.inner import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate 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 com.todoroo.astrid.data.Task
import org.tasks.R import kotlinx.parcelize.Parcelize
import org.tasks.data.Tag import org.tasks.data.Tag
import org.tasks.data.TagData import org.tasks.data.TagData
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
class TagFilter( @Parcelize
val tagData: TagData data class TagFilter(
) : Filter(tagData.name, queryTemplate(tagData.remoteId), getValuesForNewTask(tagData)) { 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 { override val order: Int
id = tagData.id!! get() = tagData.order
tint = tagData.getColor()!!
icon = tagData.getIcon()!!
order = tagData.order
}
val uuid: String
get() = tagData.remoteId!!
override fun writeToParcel(dest: Parcel, flags: Int) { override val valuesForNewTasks: String
dest.writeParcelable(tagData, 0) 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 { val uuid: String
return tagData == (other as TagFilter).tagData get() = tagData.remoteId!!
}
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()))
}
private fun getValuesForNewTask(tagData: TagData): Map<String, Any> { override fun areItemsTheSame(other: FilterListItem): Boolean {
val values: MutableMap<String, Any> = HashMap() return other is TagFilter && tagData.id!! == other.tagData.id
values[Tag.KEY] = tagData.name!!
return values
}
} }
} }

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

@ -1,5 +1,6 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.CaldavFilter import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
@ -123,7 +124,10 @@ class TaskCreator @Inject constructor(
} }
suspend fun createWithValues(filter: Filter?, title: String?): Task { 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 package com.todoroo.astrid.service
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.caldav.VtodoCache import org.tasks.caldav.VtodoCache
@ -43,10 +44,8 @@ class TaskDeleter @Inject constructor(
} }
suspend fun clearCompleted(filter: Filter): Int { suspend fun clearCompleted(filter: Filter): Int {
val deleteFilter = Filter( val deleteFilter = FilterImpl(
null, sql = QueryUtils.removeOrder(QueryUtils.showHiddenAndCompleted(filter.sql!!)),
QueryUtils.removeOrder(QueryUtils.showHiddenAndCompleted(filter.sql!!)),
emptyMap()
) )
val completed = taskDao.fetchTasks(preferences, deleteFilter) val completed = taskDao.fetchTasks(preferences, deleteFilter)
.filter(TaskContainer::isCompleted) .filter(TaskContainer::isCompleted)

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

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

@ -12,6 +12,7 @@ import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterImpl
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import com.todoroo.astrid.utility.Constants import com.todoroo.astrid.utility.Constants
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
@ -94,12 +95,17 @@ class TimerPlugin @Inject constructor(
companion object { companion object {
fun createFilter(context: Context): Filter { fun createFilter(context: Context): Filter {
val filter = Filter( return FilterImpl(
context.getString(R.string.TFE_workingOn), title = context.getString(R.string.TFE_workingOn),
QueryTemplate() sql = QueryTemplate()
.where(and(Task.TIMER_START.gt(0), Task.DELETION_DATE.eq(0)))) .where(
filter.icon = R.drawable.ic_outline_timer_24px and(
return filter 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, title = newName,
color = selectedColor, color = selectedColor,
icon = selectedIcon, icon = selectedIcon,
values = AndroidUtilities.mapToSerializedString(criteria.values), values = criteria.values,
criterion = CriterionInstance.serialize(criteria), criterion = CriterionInstance.serialize(criteria),
sql = criteria.sql, sql = criteria.sql,
order = filter?.order ?: NO_ORDER, order = filter?.order ?: NO_ORDER,
@ -384,7 +384,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
return sql.toString() return sql.toString()
} }
private val List<CriterionInstance>.values: Map<String, Any> private val List<CriterionInstance>.values: String
get() { get() {
val values: MutableMap<String, Any> = HashMap() val values: MutableMap<String, Any> = HashMap()
for (instance in this) { 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) (viewHolder as FilterViewHolder).setMoving(false)
if (from != to) { if (from != to) {
viewHolder.filter.order = to
lifecycleScope.launch { lifecycleScope.launch {
adapter.items adapter.items
.apply { .apply {
@ -218,9 +217,9 @@ class NavigationDrawerCustomization : ThemedInjectingAppCompatActivity(), Toolba
add(to, viewHolder.filter) add(to, viewHolder.filter)
} }
.filter(getPredicate(viewHolder.filter)) .filter(getPredicate(viewHolder.filter))
// TODO: use transaction, or shift positions with a single query
.forEachIndexed { order, filter -> .forEachIndexed { order, filter ->
if (filter is Filter) { if (filter is Filter) {
filter.order = order
setOrder(order, filter) setOrder(order, filter)
} }
} }

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

@ -222,7 +222,7 @@ FROM recursive_tasks
return count 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> { suspend fun fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS) 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.Criterion
import com.todoroo.andlib.sql.Field.Companion.field import com.todoroo.andlib.sql.Field.Companion.field
import com.todoroo.andlib.sql.Join import com.todoroo.andlib.sql.Join
import com.todoroo.astrid.api.AstridOrderingFilter
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import org.tasks.data.TaskListQueryNonRecursive.getNonRecursiveQuery import org.tasks.data.TaskListQueryNonRecursive.getNonRecursiveQuery
@ -35,7 +36,7 @@ object TaskListQuery {
): MutableList<String> = when { ): MutableList<String> = when {
filter.supportsManualSort() && preferences.isManualSort -> filter.supportsManualSort() && preferences.isManualSort ->
getRecursiveQuery(filter, preferences) getRecursiveQuery(filter, preferences)
filter.supportsAstridSorting() && preferences.isAstridSort -> filter is AstridOrderingFilter && preferences.isAstridSort ->
getNonRecursiveQuery(filter, preferences) getNonRecursiveQuery(filter, preferences)
filter.supportsSorting() -> filter.supportsSorting() ->
getRecursiveQuery(filter, preferences) getRecursiveQuery(filter, preferences)

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

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

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

@ -287,7 +287,8 @@ class FilterProvider @Inject constructor(
companion object { companion object {
private val COMPARATOR = Comparator<Filter> { f1, f2 -> private val COMPARATOR = Comparator<Filter> { f1, f2 ->
when { 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 f1.order == NO_ORDER -> 1
f2.order == NO_ORDER -> -1 f2.order == NO_ORDER -> -1
f1.order < f2.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 val itemType = FilterListItem.Type.ACTION
override fun areItemsTheSame(other: FilterListItem) = this == other 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 { override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is NavigationDrawerSeparator 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 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 override val itemType = FilterListItem.Type.SUBHEADER
enum class SubheaderType { enum class SubheaderType {

@ -1,36 +1,30 @@
package org.tasks.filters 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.Join
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter 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 com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.R import org.tasks.R
import org.tasks.notifications.Notification import org.tasks.notifications.Notification
class NotificationsFilter : Filter { @Parcelize
constructor(context: Context) : super(context.getString(R.string.notifications), queryTemplate) data class NotificationsFilter(
override val title: String,
private constructor() 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 override fun supportsHiddenTasks(): Boolean = false
companion object { override fun areItemsTheSame(other: FilterListItem): Boolean {
@JvmField val CREATOR: Parcelable.Creator<NotificationsFilter> = object : Parcelable.Creator<NotificationsFilter> { return other is 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)))
} }
} }

@ -1,49 +1,50 @@
package org.tasks.filters package org.tasks.filters
import android.content.Context 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.Criterion.Companion.and
import com.todoroo.andlib.sql.Field.Companion.field import com.todoroo.andlib.sql.Field.Companion.field
import com.todoroo.andlib.sql.Join.Companion.inner import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.Filter 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.api.FilterListItem
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import org.tasks.R import kotlinx.parcelize.Parcelize
import org.tasks.data.Geofence import org.tasks.data.Geofence
import org.tasks.data.Place import org.tasks.data.Place
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
import org.tasks.themes.CustomIcons import org.tasks.themes.CustomIcons
class PlaceFilter( @Parcelize
val place: Place data class PlaceFilter(
) : Filter(place.displayName, queryTemplate(place), getValuesForNewTask(place)) { val place: Place,
override var count: Int = NO_COUNT,
init { ) : Filter {
id = place.id override val valuesForNewTasks: String
tint = place.color get() = AndroidUtilities.mapToSerializedString(mapOf(Place.KEY to place.uid!!))
icon = place.icon override val sql: String
if (icon == -1) { get() = QueryTemplate()
icon = CustomIcons.PLACE .join(inner(G2, Task.ID.eq(G2_TASK)))
} .join(inner(P2, P2_UID.eq(G2_PLACE)))
order = place.order .where(and(activeAndVisible(), G2_PLACE.eq(place.uid)))
} .toString()
override fun writeToParcel(dest: Parcel, flags: Int) { override val order: Int
dest.writeParcelable(place, 0) 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 val uid: String
get() = place.uid!! get() = place.uid!!
override val beginningMenu = R.menu.menu_location_actions override fun areItemsTheSame(other: FilterListItem): Boolean {
return other is PlaceFilter && place.id == other.place.id
override val menu = R.menu.menu_location_list_fragment
override fun areContentsTheSame(other: FilterListItem): Boolean {
return place == (other as PlaceFilter).place && count == other.count
} }
fun openMap(context: Context?) { fun openMap(context: Context?) {
@ -51,35 +52,10 @@ class PlaceFilter(
} }
companion object { 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 = Geofence.TABLE.`as`("G2")
private val G2_PLACE = field("G2.place") private val G2_PLACE = field("G2.place")
private val G2_TASK = field("G2.task") private val G2_TASK = field("G2.task")
private val P2 = Place.TABLE.`as`("P2") private val P2 = Place.TABLE.`as`("P2")
private val P2_UID = field("P2.uid") 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 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.Criterion.Companion.and
import com.todoroo.andlib.sql.Order.Companion.desc import com.todoroo.andlib.sql.Order.Companion.desc
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter 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 com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.themes.CustomIcons
import org.tasks.time.DateTime import org.tasks.time.DateTime
class RecentlyModifiedFilter : Filter { @Parcelize
constructor(title: String?) : super(title, queryTemplate) data class RecentlyModifiedFilter(
private constructor() 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 override fun supportsHiddenTasks() = false
@ -19,30 +38,7 @@ class RecentlyModifiedFilter : Filter {
override fun supportsSorting() = false override fun supportsSorting() = false
companion object { override fun areItemsTheSame(other: FilterListItem): Boolean {
@JvmField return other is RecentlyModifiedFilter
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))
} }
} }

@ -1,39 +1,38 @@
package org.tasks.filters 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.Criterion.Companion.and
import com.todoroo.andlib.sql.Join.Companion.inner import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter 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 com.todoroo.astrid.data.Task
import kotlinx.parcelize.Parcelize
import org.tasks.R
import org.tasks.data.Alarm import org.tasks.data.Alarm
class SnoozedFilter : Filter { @Parcelize
constructor(title: String?) : super(title, queryTemplate) data class SnoozedFilter(
private constructor() 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 { override val sql: String
return false 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 { override fun supportsHiddenTasks() = false
@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 newArray(size: Int): Array<SnoozedFilter?> { override fun areItemsTheSame(other: FilterListItem): Boolean {
return arrayOfNulls(size) return other is SnoozedFilter
}
}
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)))
} }
} }

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

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

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

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

@ -6,6 +6,7 @@ import android.content.Intent
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.todoroo.astrid.api.Filter 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.api.FilterListItem
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -51,7 +52,7 @@ class NavigationDrawerViewModel @Inject constructor(
filterProvider filterProvider
.navDrawerItems() .navDrawerItems()
.onEach { .onEach {
if (it is Filter && it.count == -1) { if (it is Filter && it.count == NO_COUNT) {
it.count = try { it.count = try {
taskDao.count(it) taskDao.count(it)
} catch (e: Exception) { } 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.Criterion
import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities.now 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.dao.TaskDao
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import com.todoroo.astrid.service.TaskCompleter import com.todoroo.astrid.service.TaskCompleter
@ -44,7 +44,7 @@ class SubtaskControlSet : TaskEditControlFragment() {
override fun createView(savedInstanceState: Bundle?) { override fun createView(savedInstanceState: Bundle?) {
viewModel.task.takeIf { it.id > 0 }?.let { 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 { companion object {
val TAG = R.string.TEA_ctrl_subtask_pref val TAG = R.string.TEA_ctrl_subtask_pref
private fun getQueryTemplate(task: Task): QueryTemplate = QueryTemplate() private fun getQueryTemplate(task: Task): String = QueryTemplate()
.where( .where(
Criterion.and( Criterion.and(
activeAndVisible(), activeAndVisible(),
Task.PARENT.eq(task.id) Task.PARENT.eq(task.id)
) )
) )
.toString()
} }
} }

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

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

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

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

Loading…
Cancel
Save