Convert more custom filter code to Kotlin

pull/1066/head
Alex Baker 4 years ago
parent e97e0d3a22
commit 6fd1cb3e44

@ -1,201 +0,0 @@
package com.todoroo.astrid.core;
import static com.google.common.collect.Lists.transform;
import static java.util.Arrays.asList;
import static org.tasks.Strings.isNullOrEmpty;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.api.CustomFilterCriterion;
import com.todoroo.astrid.api.MultipleSelectCriterion;
import com.todoroo.astrid.api.TextInputCriterion;
import com.todoroo.astrid.helper.UUIDHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.tasks.filters.FilterCriteriaProvider;
import timber.log.Timber;
public class CriterionInstance {
public static final int TYPE_ADD = 0;
public static final int TYPE_SUBTRACT = 1;
public static final int TYPE_INTERSECT = 2;
public static final int TYPE_UNIVERSE = 3;
public CustomFilterCriterion criterion;
public int selectedIndex = -1;
public String selectedText = null;
public int type = TYPE_INTERSECT;
public int end;
public int start;
public int max;
private String id = UUIDHelper.newUUID();
public CriterionInstance() {}
public CriterionInstance(CriterionInstance other) {
id = other.id;
criterion = other.criterion;
selectedIndex = other.selectedIndex;
selectedText = other.selectedText;
type = other.type;
end = other.end;
start = other.start;
max = other.max;
}
public static List<CriterionInstance> fromString(
FilterCriteriaProvider provider, String criterion) {
if (isNullOrEmpty(criterion)) {
return Collections.emptyList();
}
List<CriterionInstance> entries = new ArrayList<>();
for (String row : criterion.split("\n")) {
List<String> split =
transform(
Splitter.on(AndroidUtilities.SERIALIZATION_SEPARATOR).splitToList(row),
CriterionInstance::unescape);
if (split.size() != 4 && split.size() != 5) {
Timber.e("invalid row: %s", row);
return Collections.emptyList();
}
CriterionInstance entry = new CriterionInstance();
entry.criterion = provider.getFilterCriteria(split.get(0));
String value = split.get(1);
if (entry.criterion instanceof TextInputCriterion) {
entry.selectedText = value;
} else if (entry.criterion instanceof MultipleSelectCriterion) {
MultipleSelectCriterion multipleSelectCriterion = (MultipleSelectCriterion) entry.criterion;
if (multipleSelectCriterion.entryValues != null) {
entry.selectedIndex = asList(multipleSelectCriterion.entryValues).indexOf(value);
}
} else {
Timber.d("Ignored value %s for %s", value, entry.criterion);
}
entry.type = Integer.parseInt(split.get(3));
entry.criterion.sql = split.get(4);
Timber.d("%s -> %s", row, entry);
entries.add(entry);
}
return entries;
}
private static String escape(String item) {
if (item == null) {
return ""; // $NON-NLS-1$
}
return item.replace(
AndroidUtilities.SERIALIZATION_SEPARATOR, AndroidUtilities.SEPARATOR_ESCAPE);
}
private static String unescape(String item) {
if (isNullOrEmpty(item)) {
return "";
}
return item.replace(
AndroidUtilities.SEPARATOR_ESCAPE, AndroidUtilities.SERIALIZATION_SEPARATOR);
}
public String getId() {
return id;
}
public String getTitleFromCriterion() {
if (criterion instanceof MultipleSelectCriterion) {
if (selectedIndex >= 0
&& ((MultipleSelectCriterion) criterion).entryTitles != null
&& selectedIndex < ((MultipleSelectCriterion) criterion).entryTitles.length) {
String title = ((MultipleSelectCriterion) criterion).entryTitles[selectedIndex];
return criterion.text.replace("?", title);
}
return criterion.text;
} else if (criterion instanceof TextInputCriterion) {
if (selectedText == null) {
return criterion.text;
}
return criterion.text.replace("?", selectedText);
}
throw new UnsupportedOperationException("Unknown criterion type"); // $NON-NLS-1$
}
public String getValueFromCriterion() {
if (type == TYPE_UNIVERSE) {
return null;
}
if (criterion instanceof MultipleSelectCriterion) {
if (selectedIndex >= 0
&& ((MultipleSelectCriterion) criterion).entryValues != null
&& selectedIndex < ((MultipleSelectCriterion) criterion).entryValues.length) {
return ((MultipleSelectCriterion) criterion).entryValues[selectedIndex];
}
return criterion.text;
} else if (criterion instanceof TextInputCriterion) {
return selectedText;
}
throw new UnsupportedOperationException("Unknown criterion type"); // $NON-NLS-1$
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CriterionInstance)) {
return false;
}
CriterionInstance that = (CriterionInstance) o;
return selectedIndex == that.selectedIndex
&& type == that.type
&& end == that.end
&& start == that.start
&& max == that.max
&& Objects.equals(criterion, that.criterion)
&& Objects.equals(selectedText, that.selectedText)
&& Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(criterion, selectedIndex, selectedText, type, end, start, max, id);
}
@Override
public String toString() {
return "CriterionInstance{"
+ "criterion="
+ criterion
+ ", selectedIndex="
+ selectedIndex
+ ", selectedText='"
+ selectedText
+ '\''
+ ", type="
+ type
+ ", end="
+ end
+ ", start="
+ start
+ ", max="
+ max
+ '}';
}
public static String serialize(List<CriterionInstance> criterion) {
return Joiner.on("\n").join(transform(criterion, CriterionInstance::serialize));
}
private String serialize() {
// criterion|entry|text|type|sql
return Joiner.on(AndroidUtilities.SERIALIZATION_SEPARATOR)
.join(
asList(
escape(criterion.identifier),
escape(getValueFromCriterion()),
escape(criterion.text),
type,
criterion.sql == null ? "" : criterion.sql));
}
}

@ -0,0 +1,174 @@
package com.todoroo.astrid.core
import com.google.common.base.Joiner
import com.google.common.base.Splitter
import com.google.common.collect.Lists
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.astrid.api.CustomFilterCriterion
import com.todoroo.astrid.api.MultipleSelectCriterion
import com.todoroo.astrid.api.TextInputCriterion
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.Strings.isNullOrEmpty
import org.tasks.filters.FilterCriteriaProvider
import timber.log.Timber
import java.util.*
class CriterionInstance {
lateinit var criterion: CustomFilterCriterion
var selectedIndex = -1
var selectedText: String? = null
var type = TYPE_INTERSECT
var end = 0
var start = 0
var max = 0
var id: String = UUIDHelper.newUUID()
private set
constructor()
constructor(other: CriterionInstance) {
id = other.id
criterion = other.criterion
selectedIndex = other.selectedIndex
selectedText = other.selectedText
type = other.type
end = other.end
start = other.start
max = other.max
}
// $NON-NLS-1$
val titleFromCriterion: String
get() {
if (criterion is MultipleSelectCriterion) {
if (selectedIndex >= 0 && (criterion as MultipleSelectCriterion).entryTitles != null && selectedIndex < (criterion as MultipleSelectCriterion).entryTitles.size) {
val title = (criterion as MultipleSelectCriterion).entryTitles[selectedIndex]
return criterion.text.replace("?", title)
}
return criterion.text
} else if (criterion is TextInputCriterion) {
return if (selectedText == null) {
criterion.text
} else criterion.text.replace("?", selectedText!!)
}
throw UnsupportedOperationException("Unknown criterion type") // $NON-NLS-1$
}
// $NON-NLS-1$
val valueFromCriterion: String?
get() {
if (type == TYPE_UNIVERSE) {
return null
}
if (criterion is MultipleSelectCriterion) {
return if (selectedIndex >= 0 && (criterion as MultipleSelectCriterion).entryValues != null && selectedIndex < (criterion as MultipleSelectCriterion).entryValues.size) {
(criterion as MultipleSelectCriterion).entryValues[selectedIndex]
} else criterion.text
} else if (criterion is TextInputCriterion) {
return selectedText
}
throw UnsupportedOperationException("Unknown criterion type") // $NON-NLS-1$
}
private fun serialize(): String {
// criterion|entry|text|type|sql
return Joiner.on(AndroidUtilities.SERIALIZATION_SEPARATOR)
.join(
listOf(
escape(criterion.identifier),
escape(valueFromCriterion),
escape(criterion.text),
type,
if (criterion.sql == null) "" else criterion.sql))
}
override fun toString(): String {
return "CriterionInstance(criterion=$criterion, selectedIndex=$selectedIndex, selectedText=$selectedText, type=$type, end=$end, start=$start, max=$max, id='$id')"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is CriterionInstance) return false
if (criterion != other.criterion) return false
if (selectedIndex != other.selectedIndex) return false
if (selectedText != other.selectedText) return false
if (type != other.type) return false
if (end != other.end) return false
if (start != other.start) return false
if (max != other.max) return false
if (id != other.id) return false
return true
}
override fun hashCode(): Int {
var result = criterion.hashCode()
result = 31 * result + selectedIndex
result = 31 * result + (selectedText?.hashCode() ?: 0)
result = 31 * result + type
result = 31 * result + end
result = 31 * result + start
result = 31 * result + max
result = 31 * result + (id.hashCode() ?: 0)
return result
}
companion object {
const val TYPE_ADD = 0
const val TYPE_SUBTRACT = 1
const val TYPE_INTERSECT = 2
const val TYPE_UNIVERSE = 3
suspend fun fromString(
provider: FilterCriteriaProvider, criterion: String): List<CriterionInstance> {
if (isNullOrEmpty(criterion)) {
return emptyList()
}
val entries: MutableList<CriterionInstance> = ArrayList()
for (row in criterion.split("\n".toRegex()).toTypedArray()) {
val split = Lists.transform(
Splitter.on(AndroidUtilities.SERIALIZATION_SEPARATOR).splitToList(row)) { item: String? -> unescape(item) }
if (split.size != 4 && split.size != 5) {
Timber.e("invalid row: %s", row)
return emptyList()
}
val entry = CriterionInstance()
entry.criterion = provider.getFilterCriteria(split[0])
val value = split[1]
if (entry.criterion is TextInputCriterion) {
entry.selectedText = value
} else if (entry.criterion is MultipleSelectCriterion) {
val multipleSelectCriterion = entry.criterion as MultipleSelectCriterion?
if (multipleSelectCriterion!!.entryValues != null) {
entry.selectedIndex = listOf(*multipleSelectCriterion.entryValues).indexOf(value)
}
} else {
Timber.d("Ignored value %s for %s", value, entry.criterion)
}
entry.type = split[3].toInt()
entry.criterion.sql = split[4]
Timber.d("%s -> %s", row, entry)
entries.add(entry)
}
return entries
}
private fun escape(item: String?): String {
return item?.replace(
AndroidUtilities.SERIALIZATION_SEPARATOR, AndroidUtilities.SEPARATOR_ESCAPE)
?: "" // $NON-NLS-1$
}
private fun unescape(item: String?): String {
return if (isNullOrEmpty(item)) {
""
} else item!!.replace(
AndroidUtilities.SEPARATOR_ESCAPE, AndroidUtilities.SERIALIZATION_SEPARATOR)
}
fun serialize(criterion: List<CriterionInstance>?): String {
return Joiner.on("\n").join(Lists.transform<CriterionInstance, String?>(criterion!!) { obj: CriterionInstance? -> obj!!.serialize() })
}
}
}

@ -10,6 +10,7 @@ import android.view.MenuItem
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.FrameLayout
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -33,6 +34,7 @@ import com.todoroo.astrid.core.CustomFilterItemTouchHelper
import com.todoroo.astrid.dao.Database
import com.todoroo.astrid.data.Task
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.Strings
import org.tasks.data.Filter
@ -76,39 +78,44 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
name.setText(filter!!.listingTitle)
}
when {
savedInstanceState != null -> {
criteria = CriterionInstance.fromString(
filterCriteriaProvider, savedInstanceState.getString(EXTRA_CRITERIA))
savedInstanceState != null -> lifecycleScope.launch {
setCriteria(CriterionInstance.fromString(
filterCriteriaProvider, savedInstanceState.getString(EXTRA_CRITERIA)!!))
}
filter != null -> {
criteria = CriterionInstance.fromString(filterCriteriaProvider, filter!!.criterion)
filter != null -> lifecycleScope.launch {
setCriteria(CriterionInstance.fromString(
filterCriteriaProvider, filter!!.criterion))
}
intent.hasExtra(EXTRA_CRITERIA) -> {
intent.hasExtra(EXTRA_CRITERIA) -> lifecycleScope.launch {
name.setText(intent.getStringExtra(EXTRA_TITLE))
criteria = CriterionInstance.fromString(
filterCriteriaProvider, intent.getStringExtra(EXTRA_CRITERIA))
setCriteria(CriterionInstance.fromString(
filterCriteriaProvider, intent.getStringExtra(EXTRA_CRITERIA)!!))
}
else -> {
val instance = CriterionInstance()
instance.criterion = filterCriteriaProvider.startingUniverse
instance.type = CriterionInstance.TYPE_UNIVERSE
criteria = mutableListOf(instance)
setCriteria(mutableListOf(instance))
}
}
adapter = CustomFilterAdapter(criteria, locale) { replaceId: String -> onClick(replaceId) }
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
ItemTouchHelper(
CustomFilterItemTouchHelper(this, this::onMove, this::onDelete, this::updateList))
.attachToRecyclerView(recyclerView)
fab.isExtended = isNew || adapter.itemCount <= 1
if (isNew) {
toolbar.inflateMenu(R.menu.menu_help)
}
updateList()
updateTheme()
}
private fun setCriteria(criteria: List<CriterionInstance>) {
this.criteria = criteria.toMutableList()
adapter = CustomFilterAdapter(criteria, locale) { replaceId: String -> onClick(replaceId) }
recyclerView.adapter = adapter
fab.isExtended = isNew || adapter.itemCount <= 1
updateList()
}
private fun onDelete(index: Int) {
criteria.removeAt(index)
updateList()
@ -161,19 +168,21 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
fun addCriteria() {
AndroidUtilities.hideKeyboard(this)
fab.shrink()
val all = filterCriteriaProvider.all
val names = all.map(CustomFilterCriterion::getName)
dialogBuilder.newDialog()
.setItems(names) { dialog: DialogInterface, which: Int ->
val instance = CriterionInstance()
instance.criterion = all[which]
showOptionsFor(instance, Runnable {
criteria.add(instance)
updateList()
})
dialog.dismiss()
}
.show()
lifecycleScope.launch {
val all = filterCriteriaProvider.all()
val names = all.map(CustomFilterCriterion::getName)
dialogBuilder.newDialog()
.setItems(names) { dialog: DialogInterface, which: Int ->
val instance = CriterionInstance()
instance.criterion = all[which]
showOptionsFor(instance, Runnable {
criteria.add(instance)
updateList()
})
dialog.dismiss()
}
.show()
}
}
/** Show options menu for the given criterioninstance */
@ -317,7 +326,7 @@ class FilterSettingsActivity : BaseListSettingsActivity() {
if (instance.type == CriterionInstance.TYPE_UNIVERSE || instance.criterion.sql == null) {
sql.append(activeAndVisible()).append(' ')
} else {
var subSql: String? = instance.criterion.sql.replace("?", UnaryCriterion.sanitize(value))
var subSql: String? = instance.criterion.sql.replace("?", UnaryCriterion.sanitize(value!!))
subSql = PermaSql.replacePlaceholdersForQuery(subSql)
sql.append(Task.ID).append(" IN (").append(subSql).append(") ")
}

@ -1,11 +0,0 @@
package org.tasks.data
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Deprecated("use coroutines")
class CaldavDaoBlocking @Inject constructor(private val dao: CaldavDao) {
fun getCalendars(): List<CaldavCalendar> = runBlocking {
dao.getCalendars()
}
}

@ -5,10 +5,6 @@ import javax.inject.Inject
@Deprecated("use coroutines")
class GoogleTaskListDaoBlocking @Inject constructor(private val dao: GoogleTaskListDao) {
fun getAccounts(): List<GoogleTaskAccount> = runBlocking {
dao.getAccounts()
}
fun getById(id: Long): GoogleTaskList? = runBlocking {
dao.getById(id)
}
@ -25,10 +21,6 @@ class GoogleTaskListDaoBlocking @Inject constructor(private val dao: GoogleTaskL
dao.findExistingList(remoteId)
}
fun getAllLists(): List<GoogleTaskList> = runBlocking {
dao.getAllLists()
}
fun insertOrReplace(googleTaskList: GoogleTaskList): Long = runBlocking {
dao.insertOrReplace(googleTaskList)
}

@ -1,11 +0,0 @@
package org.tasks.data
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Deprecated("use coroutines")
class TagDataDaoBlocking @Inject constructor(private val dao: TagDataDao) {
fun tagDataOrderedByName(): List<TagData> = runBlocking {
dao.tagDataOrderedByName()
}
}

@ -6,7 +6,10 @@ import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.todoroo.astrid.api.CustomFilterCriterion
import com.todoroo.astrid.core.CriterionInstance
import com.todoroo.astrid.core.CriterionInstance.*
import com.todoroo.astrid.core.CriterionInstance.Companion.TYPE_INTERSECT
import com.todoroo.astrid.core.CriterionInstance.Companion.TYPE_SUBTRACT
import com.todoroo.astrid.core.CriterionInstance.Companion.TYPE_UNIVERSE
import com.todoroo.astrid.core.CriterionInstance.Companion.serialize
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.activities.FilterSettingsActivity

@ -1,278 +0,0 @@
package org.tasks.filters;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Sets.newLinkedHashSet;
import android.content.Context;
import android.content.res.Resources;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Field;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.api.CustomFilterCriterion;
import com.todoroo.astrid.api.MultipleSelectCriterion;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.api.TextInputCriterion;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Task.Priority;
import dagger.hilt.android.qualifiers.ApplicationContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDaoBlocking;
import org.tasks.data.CaldavTask;
import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskList;
import org.tasks.data.GoogleTaskListDaoBlocking;
import org.tasks.data.Tag;
import org.tasks.data.TagData;
import org.tasks.data.TagDataDaoBlocking;
import org.tasks.data.TaskDao;
public class FilterCriteriaProvider {
private static final String IDENTIFIER_UNIVERSE = "active";
private static final String IDENTIFIER_TITLE = "title";
private static final String IDENTIFIER_IMPORTANCE = "importance";
private static final String IDENTIFIER_DUEDATE = "dueDate";
private static final String IDENTIFIER_GTASKS = "gtaskslist";
private static final String IDENTIFIER_CALDAV = "caldavlist";
private static final String IDENTIFIER_TAG_IS = "tag_is";
private static final String IDENTIFIER_TAG_CONTAINS = "tag_contains";
private final Context context;
private final TagDataDaoBlocking tagDataDao;
private final Resources r;
private final GoogleTaskListDaoBlocking googleTaskListDao;
private final CaldavDaoBlocking caldavDao;
@Inject
public FilterCriteriaProvider(
@ApplicationContext Context context,
TagDataDaoBlocking tagDataDao,
GoogleTaskListDaoBlocking googleTaskListDao,
CaldavDaoBlocking caldavDao) {
this.context = context;
this.tagDataDao = tagDataDao;
r = context.getResources();
this.googleTaskListDao = googleTaskListDao;
this.caldavDao = caldavDao;
}
public CustomFilterCriterion getFilterCriteria(String identifier) {
switch (identifier) {
case IDENTIFIER_UNIVERSE:
return getStartingUniverse();
case IDENTIFIER_TITLE:
return getTaskTitleContainsFilter();
case IDENTIFIER_IMPORTANCE:
return getPriorityFilter();
case IDENTIFIER_DUEDATE:
return getDueDateFilter();
case IDENTIFIER_GTASKS:
return getGtasksFilterCriteria();
case IDENTIFIER_CALDAV:
return getCaldavFilterCriteria();
case IDENTIFIER_TAG_IS:
return getTagFilter();
case IDENTIFIER_TAG_CONTAINS:
return getTagNameContainsFilter();
default:
throw new RuntimeException("Unknown identifier: " + identifier);
}
}
public CustomFilterCriterion getStartingUniverse() {
return new MultipleSelectCriterion(
IDENTIFIER_UNIVERSE,
context.getString(R.string.BFE_Active),
null,
null,
null,
null,
null);
}
public List<CustomFilterCriterion> getAll() {
List<CustomFilterCriterion> result = new ArrayList<>();
result.add(getTagFilter());
result.add(getTagNameContainsFilter());
result.add(getDueDateFilter());
result.add(getPriorityFilter());
result.add(getTaskTitleContainsFilter());
if (!googleTaskListDao.getAccounts().isEmpty()) {
result.add(getGtasksFilterCriteria());
}
result.add(getCaldavFilterCriteria());
return result;
}
private CustomFilterCriterion getTagFilter() {
// TODO: adding to hash set because duplicate tag name bug hasn't been fixed yet
String[] tagNames =
newLinkedHashSet(transform(tagDataDao.tagDataOrderedByName(), TagData::getName))
.toArray(new String[0]);
Map<String, Object> values = new HashMap<>();
values.put(Tag.KEY, "?");
return new MultipleSelectCriterion(
IDENTIFIER_TAG_IS,
context.getString(R.string.CFC_tag_text),
Query.select(Tag.TASK)
.from(Tag.TABLE)
.join(Join.inner(Task.TABLE, Tag.TASK.eq(Task.ID)))
.where(Criterion.and(TaskDao.TaskCriteria.activeAndVisible(), Tag.NAME.eq("?")))
.toString(),
values,
tagNames,
tagNames,
context.getString(R.string.CFC_tag_name));
}
public CustomFilterCriterion getTagNameContainsFilter() {
return new TextInputCriterion(
IDENTIFIER_TAG_CONTAINS,
context.getString(R.string.CFC_tag_contains_text),
Query.select(Tag.TASK)
.from(Tag.TABLE)
.join(Join.inner(Task.TABLE, Tag.TASK.eq(Task.ID)))
.where(Criterion.and(TaskDao.TaskCriteria.activeAndVisible(), Tag.NAME.like("%?%")))
.toString(),
context.getString(R.string.CFC_tag_contains_name),
"",
context.getString(R.string.CFC_tag_contains_name));
}
public CustomFilterCriterion getDueDateFilter() {
String[] entryValues =
new String[] {
"0",
PermaSql.VALUE_EOD_YESTERDAY,
PermaSql.VALUE_EOD,
PermaSql.VALUE_EOD_TOMORROW,
PermaSql.VALUE_EOD_DAY_AFTER,
PermaSql.VALUE_EOD_NEXT_WEEK,
PermaSql.VALUE_EOD_NEXT_MONTH,
};
Map<String, Object> values = new HashMap<>();
values.put(Task.DUE_DATE.name, "?");
return new MultipleSelectCriterion(
IDENTIFIER_DUEDATE,
r.getString(R.string.CFC_dueBefore_text),
Query.select(Task.ID)
.from(Task.TABLE)
.where(
Criterion.and(
TaskDao.TaskCriteria.activeAndVisible(),
Criterion.or(Field.field("?").eq(0), Task.DUE_DATE.gt(0)),
Task.DUE_DATE.lte("?")))
.toString(),
values,
r.getStringArray(R.array.CFC_dueBefore_entries),
entryValues,
r.getString(R.string.CFC_dueBefore_name));
}
public CustomFilterCriterion getPriorityFilter() {
String[] entryValues =
new String[] {
Integer.toString(Priority.HIGH),
Integer.toString(Priority.MEDIUM),
Integer.toString(Priority.LOW),
Integer.toString(Priority.NONE),
};
String[] entries = new String[] {"!!!", "!!", "!", "o"};
Map<String, Object> values = new HashMap<>();
values.put(Task.IMPORTANCE.name, "?");
return new MultipleSelectCriterion(
IDENTIFIER_IMPORTANCE,
r.getString(R.string.CFC_importance_text),
Query.select(Task.ID)
.from(Task.TABLE)
.where(Criterion.and(TaskDao.TaskCriteria.activeAndVisible(), Task.IMPORTANCE.lte("?")))
.toString(),
values,
entries,
entryValues,
r.getString(R.string.CFC_importance_name));
}
private CustomFilterCriterion getTaskTitleContainsFilter() {
return new TextInputCriterion(
IDENTIFIER_TITLE,
r.getString(R.string.CFC_title_contains_text),
Query.select(Task.ID)
.from(Task.TABLE)
.where(Criterion.and(TaskDao.TaskCriteria.activeAndVisible(), Task.TITLE.like("%?%")))
.toString(),
r.getString(R.string.CFC_title_contains_name),
"",
r.getString(R.string.CFC_title_contains_name));
}
private CustomFilterCriterion getGtasksFilterCriteria() {
List<GoogleTaskList> lists = googleTaskListDao.getAllLists();
String[] listNames = new String[lists.size()];
String[] listIds = new String[lists.size()];
for (int i = 0; i < lists.size(); i++) {
listNames[i] = lists.get(i).getTitle();
listIds[i] = lists.get(i).getRemoteId();
}
Map<String, Object> values = new HashMap<>();
values.put(GoogleTask.KEY, "?");
return new MultipleSelectCriterion(
IDENTIFIER_GTASKS,
context.getString(R.string.CFC_gtasks_list_text),
Query.select(GoogleTask.TASK)
.from(GoogleTask.TABLE)
.join(Join.inner(Task.TABLE, GoogleTask.TASK.eq(Task.ID)))
.where(
Criterion.and(
TaskDao.TaskCriteria.activeAndVisible(),
GoogleTask.DELETED.eq(0),
GoogleTask.LIST.eq("?")))
.toString(),
values,
listNames,
listIds,
context.getString(R.string.CFC_gtasks_list_name));
}
private CustomFilterCriterion getCaldavFilterCriteria() {
List<CaldavCalendar> calendars = caldavDao.getCalendars();
String[] names = new String[calendars.size()];
String[] ids = new String[calendars.size()];
for (int i = 0; i < calendars.size(); i++) {
names[i] = calendars.get(i).getName();
ids[i] = calendars.get(i).getUuid();
}
Map<String, Object> values = new HashMap<>();
values.put(CaldavTask.KEY, "?");
return new MultipleSelectCriterion(
IDENTIFIER_CALDAV,
context.getString(R.string.CFC_gtasks_list_text),
Query.select(CaldavTask.TASK)
.from(CaldavTask.TABLE)
.join(Join.inner(Task.TABLE, CaldavTask.TASK.eq(Task.ID)))
.where(
Criterion.and(
TaskDao.TaskCriteria.activeAndVisible(),
CaldavTask.DELETED.eq(0),
CaldavTask.CALENDAR.eq("?")))
.toString(),
values,
names,
ids,
context.getString(R.string.CFC_list_name));
}
}

@ -0,0 +1,233 @@
package org.tasks.filters
import android.content.Context
import com.todoroo.andlib.sql.Criterion.Companion.and
import com.todoroo.andlib.sql.Criterion.Companion.or
import com.todoroo.andlib.sql.Field.Companion.field
import com.todoroo.andlib.sql.Join.Companion.inner
import com.todoroo.andlib.sql.Query.Companion.select
import com.todoroo.astrid.api.CustomFilterCriterion
import com.todoroo.astrid.api.MultipleSelectCriterion
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.api.TextInputCriterion
import com.todoroo.astrid.data.Task
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.R
import org.tasks.data.*
import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible
import java.util.*
import javax.inject.Inject
class FilterCriteriaProvider @Inject constructor(
@param:ApplicationContext private val context: Context,
private val tagDataDao: TagDataDao,
private val googleTaskListDao: GoogleTaskListDao,
private val caldavDao: CaldavDao) {
private val r = context.resources
suspend fun getFilterCriteria(identifier: String): CustomFilterCriterion {
return when (identifier) {
IDENTIFIER_UNIVERSE -> startingUniverse
IDENTIFIER_TITLE -> taskTitleContainsFilter
IDENTIFIER_IMPORTANCE -> priorityFilter
IDENTIFIER_DUEDATE -> dueDateFilter
IDENTIFIER_GTASKS -> gtasksFilterCriteria()
IDENTIFIER_CALDAV -> caldavFilterCriteria()
IDENTIFIER_TAG_IS -> tagFilter()
IDENTIFIER_TAG_CONTAINS -> tagNameContainsFilter
else -> throw RuntimeException("Unknown identifier: $identifier")
}
}
val startingUniverse: CustomFilterCriterion
get() = MultipleSelectCriterion(
IDENTIFIER_UNIVERSE,
context.getString(R.string.BFE_Active),
null,
null,
null,
null,
null)
suspend fun all(): List<CustomFilterCriterion> {
val result: MutableList<CustomFilterCriterion> = ArrayList()
result.add(tagFilter())
result.add(tagNameContainsFilter)
result.add(dueDateFilter)
result.add(priorityFilter)
result.add(taskTitleContainsFilter)
if (googleTaskListDao.getAccounts().isNotEmpty()) {
result.add(gtasksFilterCriteria())
}
result.add(caldavFilterCriteria())
return result
}
// TODO: adding to hash set because duplicate tag name bug hasn't been fixed yet
private suspend fun tagFilter(): CustomFilterCriterion {
// TODO: adding to hash set because duplicate tag name bug hasn't been fixed yet
val tagNames = tagDataDao
.tagDataOrderedByName()
.map(TagData::name)
.distinct()
.toTypedArray()
val values: MutableMap<String, Any> = HashMap()
values[Tag.KEY] = "?"
return MultipleSelectCriterion(
IDENTIFIER_TAG_IS,
context.getString(R.string.CFC_tag_text),
select(Tag.TASK)
.from(Tag.TABLE)
.join(inner(Task.TABLE, Tag.TASK.eq(Task.ID)))
.where(and(activeAndVisible(), Tag.NAME.eq("?")))
.toString(),
values,
tagNames,
tagNames,
context.getString(R.string.CFC_tag_name))
}
val tagNameContainsFilter: CustomFilterCriterion
get() = TextInputCriterion(
IDENTIFIER_TAG_CONTAINS,
context.getString(R.string.CFC_tag_contains_text),
select(Tag.TASK)
.from(Tag.TABLE)
.join(inner(Task.TABLE, Tag.TASK.eq(Task.ID)))
.where(and(activeAndVisible(), Tag.NAME.like("%?%")))
.toString(),
context.getString(R.string.CFC_tag_contains_name),
"",
context.getString(R.string.CFC_tag_contains_name))
val dueDateFilter: CustomFilterCriterion
get() {
val entryValues = arrayOf(
"0",
PermaSql.VALUE_EOD_YESTERDAY,
PermaSql.VALUE_EOD,
PermaSql.VALUE_EOD_TOMORROW,
PermaSql.VALUE_EOD_DAY_AFTER,
PermaSql.VALUE_EOD_NEXT_WEEK,
PermaSql.VALUE_EOD_NEXT_MONTH)
val values: MutableMap<String?, Any> = HashMap()
values[Task.DUE_DATE.name] = "?"
return MultipleSelectCriterion(
IDENTIFIER_DUEDATE,
r.getString(R.string.CFC_dueBefore_text),
select(Task.ID)
.from(Task.TABLE)
.where(
and(
activeAndVisible(),
or(field("?").eq(0), Task.DUE_DATE.gt(0)),
Task.DUE_DATE.lte("?")))
.toString(),
values,
r.getStringArray(R.array.CFC_dueBefore_entries),
entryValues,
r.getString(R.string.CFC_dueBefore_name))
}
val priorityFilter: CustomFilterCriterion
get() {
val entryValues = arrayOf(
Task.Priority.HIGH.toString(),
Task.Priority.MEDIUM.toString(),
Task.Priority.LOW.toString(),
Task.Priority.NONE.toString())
val entries = arrayOf("!!!", "!!", "!", "o")
val values: MutableMap<String?, Any> = HashMap()
values[Task.IMPORTANCE.name] = "?"
return MultipleSelectCriterion(
IDENTIFIER_IMPORTANCE,
r.getString(R.string.CFC_importance_text),
select(Task.ID)
.from(Task.TABLE)
.where(and(activeAndVisible(), Task.IMPORTANCE.lte("?")))
.toString(),
values,
entries,
entryValues,
r.getString(R.string.CFC_importance_name))
}
private val taskTitleContainsFilter: CustomFilterCriterion
get() = TextInputCriterion(
IDENTIFIER_TITLE,
r.getString(R.string.CFC_title_contains_text),
select(Task.ID)
.from(Task.TABLE)
.where(and(activeAndVisible(), Task.TITLE.like("%?%")))
.toString(),
r.getString(R.string.CFC_title_contains_name),
"",
r.getString(R.string.CFC_title_contains_name))
private suspend fun gtasksFilterCriteria(): CustomFilterCriterion {
val lists = googleTaskListDao.getAllLists()
val listNames = arrayOfNulls<String>(lists.size)
val listIds = arrayOfNulls<String>(lists.size)
for (i in lists.indices) {
listNames[i] = lists[i].title
listIds[i] = lists[i].remoteId
}
val values: MutableMap<String, Any> = HashMap()
values[GoogleTask.KEY] = "?"
return MultipleSelectCriterion(
IDENTIFIER_GTASKS,
context.getString(R.string.CFC_gtasks_list_text),
select(GoogleTask.TASK)
.from(GoogleTask.TABLE)
.join(inner(Task.TABLE, GoogleTask.TASK.eq(Task.ID)))
.where(
and(
activeAndVisible(),
GoogleTask.DELETED.eq(0),
GoogleTask.LIST.eq("?")))
.toString(),
values,
listNames,
listIds,
context.getString(R.string.CFC_gtasks_list_name))
}
private suspend fun caldavFilterCriteria(): CustomFilterCriterion {
val calendars = caldavDao.getCalendars()
val names = arrayOfNulls<String>(calendars.size)
val ids = arrayOfNulls<String>(calendars.size)
for (i in calendars.indices) {
names[i] = calendars[i].name
ids[i] = calendars[i].uuid
}
val values: MutableMap<String, Any> = HashMap()
values[CaldavTask.KEY] = "?"
return MultipleSelectCriterion(
IDENTIFIER_CALDAV,
context.getString(R.string.CFC_gtasks_list_text),
select(CaldavTask.TASK)
.from(CaldavTask.TABLE)
.join(inner(Task.TABLE, CaldavTask.TASK.eq(Task.ID)))
.where(
and(
activeAndVisible(),
CaldavTask.DELETED.eq(0),
CaldavTask.CALENDAR.eq("?")))
.toString(),
values,
names,
ids,
context.getString(R.string.CFC_list_name))
}
companion object {
private const val IDENTIFIER_UNIVERSE = "active"
private const val IDENTIFIER_TITLE = "title"
private const val IDENTIFIER_IMPORTANCE = "importance"
private const val IDENTIFIER_DUEDATE = "dueDate"
private const val IDENTIFIER_GTASKS = "gtaskslist"
private const val IDENTIFIER_CALDAV = "caldavlist"
private const val IDENTIFIER_TAG_IS = "tag_is"
private const val IDENTIFIER_TAG_CONTAINS = "tag_contains"
}
}
Loading…
Cancel
Save