Refactor FilterProvider for reuse

pull/1012/head
Alex Baker 4 years ago
parent efa663f444
commit 864bc73a8c

@ -1,49 +0,0 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.timers;
import android.content.Context;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import javax.inject.Inject;
import org.jetbrains.annotations.Nullable;
import org.tasks.R;
import org.tasks.injection.ForApplication;
/**
* Exposes "working on" filter to the NavigationDrawerFragment
*
* @author Tim Su <tim@todoroo.com>
*/
public final class TimerFilterExposer {
private final TaskDao taskDao;
private final Context context;
@Inject
public TimerFilterExposer(@ForApplication Context context, TaskDao taskDao) {
this.context = context;
this.taskDao = taskDao;
}
static Filter createFilter(Context context) {
Filter filter =
new Filter(
context.getString(R.string.TFE_workingOn),
new QueryTemplate()
.where(Criterion.and(Task.TIMER_START.gt(0), Task.DELETION_DATE.eq(0))));
filter.icon = R.drawable.ic_outline_timer_24px;
return filter;
}
public @Nullable Filter getFilters() {
return taskDao.activeTimers() == 0 ? null : createFilter(context);
}
}

@ -13,6 +13,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import androidx.core.app.NotificationCompat;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao;
@ -86,7 +88,7 @@ public class TimerPlugin {
if (count == 0) {
notificationManager.cancel(Constants.NOTIFICATION_TIMER);
} else {
Filter filter = TimerFilterExposer.createFilter(context);
Filter filter = createFilter(context);
Intent notifyIntent = TaskIntents.getTaskListIntent(context, filter);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent =
@ -109,4 +111,14 @@ public class TimerPlugin {
notificationManager.notify(Constants.NOTIFICATION_TIMER, builder, false, false, false);
}
}
public static Filter createFilter(Context context) {
Filter filter =
new Filter(
context.getString(R.string.TFE_workingOn),
new QueryTemplate()
.where(Criterion.and(Task.TIMER_START.gt(0), Task.DELETION_DATE.eq(0))));
filter.icon = R.drawable.ic_outline_timer_24px;
return filter;
}
}

@ -120,7 +120,7 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity {
private void refresh() {
disposables.add(
Single.fromCallable(() -> filterProvider.getItems(false))
Single.fromCallable(() -> filterProvider.getFilterPickerItems())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> filterAdapter.setData(items, selected)));

@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
import androidx.room.*
import io.reactivex.Single
import org.tasks.filters.LocationFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis
@Dao
interface LocationDao {
@ -107,5 +108,5 @@ interface LocationDao {
+ " LEFT JOIN tasks ON geofences.task = tasks._id AND tasks.completed = 0 AND tasks.deleted = 0 AND tasks.hideUntil < :now"
+ " GROUP BY places.uid"
+ " ORDER BY name COLLATE NOCASE ASC")
fun getPlaceFilters(now: Long): List<LocationFilters>
fun getPlaceFilters(now: Long = currentTimeMillis()): List<LocationFilters>
}

@ -8,6 +8,7 @@ import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.db.DbUtils
import org.tasks.filters.AlphanumComparator
import org.tasks.filters.TagFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis
import java.util.*
import kotlin.collections.HashSet
@ -146,5 +147,5 @@ abstract class TagDataDao {
+ " LEFT JOIN tasks ON tags.task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now"
+ " WHERE tagdata.name IS NOT NULL AND tagdata.name != ''"
+ " GROUP BY tagdata.remoteId")
abstract fun getTagFilters(now: Long): List<TagFilters>
abstract fun getTagFilters(now: Long = currentTimeMillis()): List<TagFilters>
}

@ -3,15 +3,13 @@ package org.tasks.filters
import android.content.Context
import android.content.Intent
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.CustomFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.FilterListItem
import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.timers.TimerFilterExposer
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.timers.TimerPlugin
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.Strings
import org.tasks.activities.GoogleTaskListSettingsActivity
import org.tasks.activities.TagSettingsActivity
import org.tasks.billing.Inventory
@ -25,14 +23,13 @@ import org.tasks.preferences.HelpAndFeedback
import org.tasks.preferences.MainPreferences
import org.tasks.preferences.Preferences
import org.tasks.ui.NavigationDrawerFragment
import java.util.*
import javax.inject.Inject
class FilterProvider @Inject constructor(
@param:ForApplication private val context: Context,
private val inventory: Inventory,
private val builtInFilterExposer: BuiltInFilterExposer,
private val timerFilterExposer: TimerFilterExposer,
private val taskDao: TaskDao,
private val filterDao: FilterDao,
private val tagDataDao: TagDataDao,
private val googleTaskListDao: GoogleTaskListDao,
@ -43,255 +40,203 @@ class FilterProvider @Inject constructor(
val listPickerItems: List<FilterListItem>
get() {
AndroidUtilities.assertNotMainThread()
val items: MutableList<FilterListItem> = ArrayList()
for ((account, value) in googleTaskFilters) {
items.addAll(
getSubmenu(
account.account,
!Strings.isNullOrEmpty(account.error),
value,
true,
account.isCollapsed,
SubheaderType.GOOGLE_TASKS,
account.id))
}
for ((account, value) in caldavFilters) {
items.addAll(
getSubmenu(
account.name,
!Strings.isNullOrEmpty(account.error),
value,
true,
account.isCollapsed,
SubheaderType.CALDAV,
account.id))
}
return items
return googleTaskFilters(false).plus(caldavFilters(false))
}
private fun addFilters(items: MutableList<FilterListItem>, navigationDrawer: Boolean) {
if (!preferences.getBoolean(R.string.p_filters_enabled, true)) {
return
}
items.addAll(getSubmenu(R.string.filters, R.string.p_collapse_filters) { filters })
if (navigationDrawer && !preferences.getBoolean(R.string.p_collapse_filters, false)) {
items.add(
NavigationDrawerAction(
context.getString(R.string.add_filter),
R.drawable.ic_outline_add_24px,
NavigationDrawerFragment.REQUEST_NEW_FILTER))
val navDrawerItems: List<FilterListItem>
get() {
AndroidUtilities.assertNotMainThread()
return arrayListOf(builtInFilterExposer.myTasksFilter)
.plus(getAllFilters(true))
.plus(navDrawerFooter)
}
}
private fun addTags(items: MutableList<FilterListItem>, navigationDrawer: Boolean) {
if (!preferences.getBoolean(R.string.p_tags_enabled, true)) {
return
}
val collapsed = preferences.getBoolean(R.string.p_collapse_tags, false)
var filters = if (collapsed) emptyList() else tagDataDao.getTagFilters(DateUtilities.now())
if (preferences.getBoolean(R.string.p_tags_hide_unused, false)) {
filters = filters.filter { it.count > 0 }
}
val tags = filters.map(TagFilters::toTagFilter).sortedWith(AlphanumComparator.FILTER)
items.addAll(
getSubmenu(
context.getString(R.string.tags),
false,
tags,
false,
collapsed,
SubheaderType.PREFERENCE,
R.string.p_collapse_tags.toLong()))
if (navigationDrawer && !collapsed) {
items.add(
NavigationDrawerAction(
context.getString(R.string.new_tag),
R.drawable.ic_outline_add_24px,
Intent(context, TagSettingsActivity::class.java),
NavigationDrawerFragment.REQUEST_NEW_LIST))
}
}
private fun addPlaces(items: MutableList<FilterListItem>, navigationDrawer: Boolean) {
if (!preferences.getBoolean(R.string.p_places_enabled, true)) {
return
}
val collapsed = preferences.getBoolean(R.string.p_collapse_locations, false)
var filters = if (collapsed) emptyList() else locationDao.getPlaceFilters(DateUtilities.now())
if (preferences.getBoolean(R.string.p_places_hide_unused, false)) {
filters = filters.filter { it.count > 0 }
}
items.addAll(
getSubmenu(
context.getString(R.string.places),
false,
filters.map(LocationFilters::toLocationFilter),
false,
collapsed,
SubheaderType.PREFERENCE,
R.string.p_collapse_locations.toLong()))
if (navigationDrawer && !collapsed) {
items.add(
NavigationDrawerAction(
context.getString(R.string.add_place),
R.drawable.ic_outline_add_24px,
Intent(context, LocationPickerActivity::class.java),
NavigationDrawerFragment.REQUEST_NEW_PLACE))
val filterPickerItems: List<FilterListItem>
get() {
AndroidUtilities.assertNotMainThread()
return arrayListOf(builtInFilterExposer.myTasksFilter)
.plus(getAllFilters(false))
}
}
fun getItems(navigationDrawer: Boolean): List<FilterListItem> {
AndroidUtilities.assertNotMainThread()
val items: MutableList<FilterListItem> = ArrayList()
items.add(builtInFilterExposer.myTasksFilter)
addFilters(items, navigationDrawer)
addTags(items, navigationDrawer)
addPlaces(items, navigationDrawer)
for ((account, value) in googleTaskFilters) {
items.addAll(
getSubmenu(
account.account,
!Strings.isNullOrEmpty(account.error),
value,
!navigationDrawer,
account.isCollapsed,
SubheaderType.GOOGLE_TASKS,
account.id))
if (navigationDrawer && !account.isCollapsed) {
items.add(
NavigationDrawerAction(
context.getString(R.string.new_list),
R.drawable.ic_outline_add_24px,
Intent(context, GoogleTaskListSettingsActivity::class.java)
.putExtra(GoogleTaskListSettingsActivity.EXTRA_ACCOUNT, account),
NavigationDrawerFragment.REQUEST_NEW_LIST))
}
}
for ((account, value) in caldavFilters) {
if (account.accountType == TYPE_LOCAL && !preferences.getBoolean(R.string.p_lists_enabled, true)) {
continue
private fun addFilters(showCreate: Boolean): List<FilterListItem> =
if (!preferences.getBoolean(R.string.p_filters_enabled, true)) {
emptyList()
} else {
val collapsed = preferences.getBoolean(R.string.p_collapse_filters, false)
listOf(
NavigationDrawerSubheader(
context.getString(R.string.filters),
false,
collapsed,
SubheaderType.PREFERENCE,
R.string.p_collapse_filters.toLong()))
.apply { if (collapsed) return this }
.plus(builtInFilterExposer.filters)
.plusIf(taskDao.activeTimers() > 0) { TimerPlugin.createFilter(context) }
.plus(filterDao.getFilters().map(::CustomFilter).sortedWith(AlphanumComparator.FILTER))
.plusIf(showCreate) {
NavigationDrawerAction(
context.getString(R.string.add_filter),
R.drawable.ic_outline_add_24px,
NavigationDrawerFragment.REQUEST_NEW_FILTER)
}
}
items.addAll(
getSubmenu(
account.name,
!Strings.isNullOrEmpty(account.error),
value,
!navigationDrawer,
account.isCollapsed,
SubheaderType.CALDAV,
account.id))
if (navigationDrawer && !account.isCollapsed) {
items.add(
NavigationDrawerAction(
context.getString(R.string.new_list),
R.drawable.ic_outline_add_24px,
Intent(context, account.listSettingsClass())
.putExtra(BaseCaldavCalendarSettingsActivity.EXTRA_CALDAV_ACCOUNT, account),
NavigationDrawerFragment.REQUEST_NEW_LIST))
}
}
if (navigationDrawer) {
items.add(NavigationDrawerSeparator())
@Suppress("ConstantConditionIf")
if (BuildConfig.FLAVOR == "generic") {
items.add(
NavigationDrawerAction(
context.getString(R.string.TLA_menu_donate),
R.drawable.ic_outline_attach_money_24px,
NavigationDrawerFragment.REQUEST_DONATE))
} else if (!inventory.hasPro()) {
items.add(
NavigationDrawerAction(
context.getString(R.string.name_your_price),
R.drawable.ic_outline_attach_money_24px,
NavigationDrawerFragment.REQUEST_PURCHASE))
}
items.add(
NavigationDrawerAction(
context.getString(R.string.TLA_menu_settings),
R.drawable.ic_outline_settings_24px,
Intent(context, MainPreferences::class.java),
NavigationDrawerFragment.REQUEST_SETTINGS))
items.add(
NavigationDrawerAction(
context.getString(R.string.help_and_feedback),
R.drawable.ic_outline_help_outline_24px,
Intent(context, HelpAndFeedback::class.java),
0))
}
return items
}
private val filters: List<Filter>
get() {
val filters = ArrayList(builtInFilterExposer.filters)
val filter = timerFilterExposer.filters
if (filter != null) {
filters.add(filter)
private fun addTags(showCreate: Boolean): List<FilterListItem> =
if (!preferences.getBoolean(R.string.p_tags_enabled, true)) {
emptyList()
} else {
val collapsed = preferences.getBoolean(R.string.p_collapse_tags, false)
listOf(
NavigationDrawerSubheader(
context.getString(R.string.tags),
false,
collapsed,
SubheaderType.PREFERENCE,
R.string.p_collapse_tags.toLong()))
.apply { if (collapsed) return this }
.plus(tagDataDao.getTagFilters()
.filterIf(preferences.getBoolean(R.string.p_tags_hide_unused, false)) {
it.count > 0
}
.map(TagFilters::toTagFilter)
.sortedWith(AlphanumComparator.FILTER))
.plusIf(showCreate) {
NavigationDrawerAction(
context.getString(R.string.new_tag),
R.drawable.ic_outline_add_24px,
Intent(context, TagSettingsActivity::class.java),
NavigationDrawerFragment.REQUEST_NEW_LIST)
}
}
filters.addAll(filterDao.getFilters()
.map(::CustomFilter)
.sortedWith(AlphanumComparator.FILTER))
return filters
}
private val googleTaskFilters: Set<Map.Entry<GoogleTaskAccount, List<Filter>>>
get() {
val accounts = googleTaskListDao.getAccounts()
val filters = LinkedHashMap<GoogleTaskAccount, List<Filter>>()
for (account in accounts) {
filters[account] = if (account.isCollapsed) {
emptyList()
} else {
googleTaskListDao
.getGoogleTaskFilters(account.account!!)
.map(GoogleTaskFilters::toGtasksFilter)
.sortedWith(AlphanumComparator.FILTER)
}
private fun addPlaces(showCreate: Boolean): List<FilterListItem> =
if (!preferences.getBoolean(R.string.p_places_enabled, true)) {
emptyList()
} else {
val collapsed = preferences.getBoolean(R.string.p_collapse_locations, false)
listOf(
NavigationDrawerSubheader(
context.getString(R.string.places),
false,
collapsed,
SubheaderType.PREFERENCE,
R.string.p_collapse_locations.toLong()))
.apply { if (collapsed) return this }
.plus(locationDao.getPlaceFilters()
.filterIf(preferences.getBoolean(R.string.p_places_hide_unused, false)) {
it.count > 0
}
.map(LocationFilters::toLocationFilter)
.sortedWith(AlphanumComparator.FILTER))
.plusIf(showCreate) {
NavigationDrawerAction(
context.getString(R.string.add_place),
R.drawable.ic_outline_add_24px,
Intent(context, LocationPickerActivity::class.java),
NavigationDrawerFragment.REQUEST_NEW_PLACE)
}
}
return filters.entries
}
private val caldavFilters: Set<Map.Entry<CaldavAccount, List<Filter>>>
get() {
val accounts = caldavDao.getAccounts().ifEmpty {
listOf(caldavDao.setupLocalAccount(context))
}
val filters = LinkedHashMap<CaldavAccount, List<Filter>>()
for (account in accounts) {
if (account.accountType == TYPE_LOCAL) {
account.name = context.getString(R.string.lists)
private fun getAllFilters(showCreate: Boolean): List<FilterListItem> =
addFilters(showCreate)
.plus(addTags(showCreate))
.plus(addPlaces(showCreate))
.plus(googleTaskFilters(showCreate))
.plus(caldavFilters(showCreate))
private val navDrawerFooter: List<FilterListItem>
get() = listOf(NavigationDrawerSeparator())
.plusIf(BuildConfig.FLAVOR == "generic") {
NavigationDrawerAction(
context.getString(R.string.TLA_menu_donate),
R.drawable.ic_outline_attach_money_24px,
NavigationDrawerFragment.REQUEST_DONATE)
}
filters[account] = if (account.isCollapsed) {
emptyList()
} else {
caldavDao
.getCaldavFilters(account.uuid!!)
.map(CaldavFilters::toCaldavFilter)
.sortedWith(AlphanumComparator.FILTER)
.plusIf(!inventory.hasPro()) {
NavigationDrawerAction(
context.getString(R.string.name_your_price),
R.drawable.ic_outline_attach_money_24px,
NavigationDrawerFragment.REQUEST_PURCHASE)
}
}
return filters.entries
}
.plus(NavigationDrawerAction(
context.getString(R.string.TLA_menu_settings),
R.drawable.ic_outline_settings_24px,
Intent(context, MainPreferences::class.java),
NavigationDrawerFragment.REQUEST_SETTINGS))
.plus(NavigationDrawerAction(
context.getString(R.string.help_and_feedback),
R.drawable.ic_outline_help_outline_24px,
Intent(context, HelpAndFeedback::class.java),
0))
private fun getSubmenu(title: Int, prefId: Int, getFilters: () -> List<Filter>): List<FilterListItem> {
val collapsed = preferences.getBoolean(prefId, false)
val subheader = NavigationDrawerSubheader(context.getString(title), false, collapsed, SubheaderType.PREFERENCE, prefId.toLong())
return listOf(subheader).plus(if (collapsed) emptyList() else getFilters.invoke())
}
private fun googleTaskFilters(showCreate: Boolean = true): List<FilterListItem> =
googleTaskListDao
.getAccounts()
.flatMap {
listOf(
NavigationDrawerSubheader(
it.account,
it.error?.isNotBlank() ?: false,
it.isCollapsed,
SubheaderType.GOOGLE_TASKS,
it.id))
.plusAllIf(!it.isCollapsed) {
googleTaskListDao
.getGoogleTaskFilters(it.account!!)
.map(GoogleTaskFilters::toGtasksFilter)
.sortedWith(AlphanumComparator.FILTER)
}
.plusIf(showCreate && !it.isCollapsed) {
NavigationDrawerAction(
context.getString(R.string.new_list),
R.drawable.ic_outline_add_24px,
Intent(context, GoogleTaskListSettingsActivity::class.java)
.putExtra(GoogleTaskListSettingsActivity.EXTRA_ACCOUNT, it),
NavigationDrawerFragment.REQUEST_NEW_LIST)
}
}
private fun getSubmenu(
title: String?,
error: Boolean,
filters: List<Filter>,
hideIfEmpty: Boolean,
collapsed: Boolean,
type: SubheaderType,
id: Long): List<FilterListItem> {
return if (hideIfEmpty && filters.isEmpty() && !collapsed) {
listOf()
} else {
listOf(NavigationDrawerSubheader(title, error, collapsed, type, id)).plus(filters)
}
private fun caldavFilters(showCreate: Boolean = true): List<FilterListItem> =
caldavDao.getAccounts()
.ifEmpty { listOf(caldavDao.setupLocalAccount(context)) }
.filter { it.accountType != TYPE_LOCAL || preferences.getBoolean(R.string.p_lists_enabled, true) }
.flatMap {
listOf(
NavigationDrawerSubheader(
if (it.accountType == TYPE_LOCAL) {
context.getString(R.string.lists)
} else {
it.name
},
it.error?.isNotBlank() ?: false,
it.isCollapsed,
SubheaderType.CALDAV,
it.id))
.plusAllIf(!it.isCollapsed) {
caldavDao
.getCaldavFilters(it.uuid!!)
.map(CaldavFilters::toCaldavFilter)
.sortedWith(AlphanumComparator.FILTER)
}
.plusIf(showCreate && !it.isCollapsed) {
NavigationDrawerAction(
context.getString(R.string.new_list),
R.drawable.ic_outline_add_24px,
Intent(context, it.listSettingsClass())
.putExtra(BaseCaldavCalendarSettingsActivity.EXTRA_CALDAV_ACCOUNT, it),
NavigationDrawerFragment.REQUEST_NEW_LIST)
}
}
companion object {
private fun <T> Collection<T>.plusAllIf(predicate: Boolean, item: () -> Iterable<T>): List<T> =
plus(if (predicate) item.invoke() else emptyList())
private fun <T> Collection<T>.plusIf(predicate: Boolean, item: () -> T): List<T> =
if (predicate) plus(item.invoke()) else this.toList()
private fun <T> Iterable<T>.filterIf(predicate: Boolean, predicate2: (T) -> Boolean): List<T> =
if (predicate) filter(predicate2) else this.toList()
}
}

@ -152,7 +152,7 @@ class NavigationDrawerFragment : InjectingFragment() {
}
private fun updateFilters() =
Single.fromCallable { filterProvider.getItems(true) }
Single.fromCallable { filterProvider.navDrawerItems }
.map { items: List<FilterListItem> -> refreshFilterCount(items) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

Loading…
Cancel
Save