diff --git a/app/src/main/java/com/todoroo/astrid/timers/TimerFilterExposer.java b/app/src/main/java/com/todoroo/astrid/timers/TimerFilterExposer.java deleted file mode 100644 index e9ee7829d..000000000 --- a/app/src/main/java/com/todoroo/astrid/timers/TimerFilterExposer.java +++ /dev/null @@ -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 - */ -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); - } -} diff --git a/app/src/main/java/com/todoroo/astrid/timers/TimerPlugin.java b/app/src/main/java/com/todoroo/astrid/timers/TimerPlugin.java index c58fca881..c9a24cdac 100644 --- a/app/src/main/java/com/todoroo/astrid/timers/TimerPlugin.java +++ b/app/src/main/java/com/todoroo/astrid/timers/TimerPlugin.java @@ -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; + } } diff --git a/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java b/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java index 7cb9be713..a8d3a3952 100644 --- a/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java +++ b/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java @@ -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))); diff --git a/app/src/main/java/org/tasks/data/LocationDao.kt b/app/src/main/java/org/tasks/data/LocationDao.kt index ecf633230..84bbad1bc 100644 --- a/app/src/main/java/org/tasks/data/LocationDao.kt +++ b/app/src/main/java/org/tasks/data/LocationDao.kt @@ -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 + fun getPlaceFilters(now: Long = currentTimeMillis()): List } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/TagDataDao.kt b/app/src/main/java/org/tasks/data/TagDataDao.kt index 9b84f2904..2708793b2 100644 --- a/app/src/main/java/org/tasks/data/TagDataDao.kt +++ b/app/src/main/java/org/tasks/data/TagDataDao.kt @@ -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 + abstract fun getTagFilters(now: Long = currentTimeMillis()): List } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/filters/FilterProvider.kt b/app/src/main/java/org/tasks/filters/FilterProvider.kt index 62906f7a0..4c02827db 100644 --- a/app/src/main/java/org/tasks/filters/FilterProvider.kt +++ b/app/src/main/java/org/tasks/filters/FilterProvider.kt @@ -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 get() { AndroidUtilities.assertNotMainThread() - val items: MutableList = 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, 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 + get() { + AndroidUtilities.assertNotMainThread() + return arrayListOf(builtInFilterExposer.myTasksFilter) + .plus(getAllFilters(true)) + .plus(navDrawerFooter) } - } - private fun addTags(items: MutableList, 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, 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 + get() { + AndroidUtilities.assertNotMainThread() + return arrayListOf(builtInFilterExposer.myTasksFilter) + .plus(getAllFilters(false)) } - } - fun getItems(navigationDrawer: Boolean): List { - AndroidUtilities.assertNotMainThread() - val items: MutableList = 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 = + 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 - get() { - val filters = ArrayList(builtInFilterExposer.filters) - val filter = timerFilterExposer.filters - if (filter != null) { - filters.add(filter) + private fun addTags(showCreate: Boolean): List = + 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>> - get() { - val accounts = googleTaskListDao.getAccounts() - val filters = LinkedHashMap>() - 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 = + 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>> - get() { - val accounts = caldavDao.getAccounts().ifEmpty { - listOf(caldavDao.setupLocalAccount(context)) - } - val filters = LinkedHashMap>() - for (account in accounts) { - if (account.accountType == TYPE_LOCAL) { - account.name = context.getString(R.string.lists) + private fun getAllFilters(showCreate: Boolean): List = + addFilters(showCreate) + .plus(addTags(showCreate)) + .plus(addPlaces(showCreate)) + .plus(googleTaskFilters(showCreate)) + .plus(caldavFilters(showCreate)) + + private val navDrawerFooter: List + 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): List { - 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 = + 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, - hideIfEmpty: Boolean, - collapsed: Boolean, - type: SubheaderType, - id: Long): List { - return if (hideIfEmpty && filters.isEmpty() && !collapsed) { - listOf() - } else { - listOf(NavigationDrawerSubheader(title, error, collapsed, type, id)).plus(filters) - } + private fun caldavFilters(showCreate: Boolean = true): List = + 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 Collection.plusAllIf(predicate: Boolean, item: () -> Iterable): List = + plus(if (predicate) item.invoke() else emptyList()) + + private fun Collection.plusIf(predicate: Boolean, item: () -> T): List = + if (predicate) plus(item.invoke()) else this.toList() + + private fun Iterable.filterIf(predicate: Boolean, predicate2: (T) -> Boolean): List = + if (predicate) filter(predicate2) else this.toList() } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt index 9b95e1d47..c86b93e7b 100644 --- a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt +++ b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt @@ -152,7 +152,7 @@ class NavigationDrawerFragment : InjectingFragment() { } private fun updateFilters() = - Single.fromCallable { filterProvider.getItems(true) } + Single.fromCallable { filterProvider.navDrawerItems } .map { items: List -> refreshFilterCount(items) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())