Save custom filter criteria on rotate

pull/996/head
Alex Baker 5 years ago
parent 347dadfcb0
commit 2d11a5c55c

@ -9,6 +9,7 @@ package com.todoroo.astrid.api;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
import java.util.Map;
/**
@ -84,4 +85,30 @@ public class MultipleSelectCriterion extends CustomFilterCriterion implements Pa
dest.writeStringArray(entryValues);
writeToParcel(dest);
}
@Override
public String toString() {
return "MultipleSelectCriterion{"
+ "entryTitles="
+ Arrays.toString(entryTitles)
+ ", entryValues="
+ Arrays.toString(entryValues)
+ ", valuesForNewTasks="
+ valuesForNewTasks
+ ", identifier='"
+ identifier
+ '\''
+ ", text='"
+ text
+ '\''
+ ", sql='"
+ sql
+ '\''
+ ", name='"
+ name
+ '\''
+ ", icon="
+ icon
+ '}';
}
}

@ -79,4 +79,32 @@ public class TextInputCriterion extends CustomFilterCriterion implements Parcela
dest.writeString(hint);
writeToParcel(dest);
}
@Override
public String toString() {
return "TextInputCriterion{"
+ "hint='"
+ hint
+ '\''
+ ", prompt='"
+ prompt
+ '\''
+ ", valuesForNewTasks="
+ valuesForNewTasks
+ ", identifier='"
+ identifier
+ '\''
+ ", text='"
+ text
+ '\''
+ ", sql='"
+ sql
+ '\''
+ ", name='"
+ name
+ '\''
+ ", icon="
+ icon
+ '}';
}
}

@ -0,0 +1,87 @@
package com.todoroo.astrid.core;
import com.todoroo.astrid.api.CustomFilterCriterion;
import com.todoroo.astrid.api.MultipleSelectCriterion;
import com.todoroo.astrid.api.TextInputCriterion;
public class CriterionInstance {
static final int TYPE_ADD = 0;
static final int TYPE_SUBTRACT = 1;
static final int TYPE_INTERSECT = 2;
static final int TYPE_UNIVERSE = 3;
/** criteria for this instance */
public CustomFilterCriterion criterion;
/** which of the entries is selected (MultipleSelect) */
int selectedIndex = -1;
/** text of selection (TextInput) */
String selectedText = null;
/** type of join */
public int type = TYPE_INTERSECT;
public int end;
/** statistics for filter count */
int start;
int max;
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$
}
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 String toString() {
return "CriterionInstance{"
+ "criterion="
+ criterion
+ ", selectedIndex="
+ selectedIndex
+ ", selectedText='"
+ selectedText
+ '\''
+ ", type="
+ type
+ ", end="
+ end
+ ", start="
+ start
+ ", max="
+ max
+ '}';
}
}

@ -7,7 +7,9 @@
package com.todoroo.astrid.core;
import static android.text.TextUtils.isEmpty;
import static com.google.common.collect.Lists.transform;
import static com.todoroo.andlib.utility.AndroidUtilities.mapToSerializedString;
import static java.util.Arrays.asList;
import android.content.Context;
import android.content.Intent;
@ -19,8 +21,13 @@ import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.todoroo.andlib.data.Property.CountProperty;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.sql.UnaryCriterion;
@ -36,6 +43,7 @@ import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -49,6 +57,7 @@ import org.tasks.filters.FilterCriteriaProvider;
import org.tasks.injection.ActivityComponent;
import org.tasks.injection.ThemedInjectingAppCompatActivity;
import org.tasks.locale.Locale;
import timber.log.Timber;
/**
* Activity that allows users to build custom filters
@ -60,7 +69,7 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
static final int MENU_GROUP_CONTEXT_TYPE = 1;
static final int MENU_GROUP_CONTEXT_DELETE = 2;
private static final String IDENTIFIER_UNIVERSE = "active"; // $NON-NLS-1$
private static final String EXTRA_CRITERIA = "extra_criteria";
private static final int MENU_GROUP_FILTER = 0;
@Inject Database database;
@ -75,26 +84,59 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
private CustomFilterAdapter adapter;
private static String serializeFilters(CustomFilterAdapter adapter) {
StringBuilder values = new StringBuilder();
List<String> rows = new ArrayList<>();
for (int i = 0; i < adapter.getCount(); i++) {
CriterionInstance item = adapter.getItem(i);
// criterion|entry|text|type|sql
values
.append(escape(item.criterion.identifier))
.append(AndroidUtilities.SERIALIZATION_SEPARATOR);
values
.append(escape(item.getValueFromCriterion()))
.append(AndroidUtilities.SERIALIZATION_SEPARATOR);
values.append(escape(item.criterion.text)).append(AndroidUtilities.SERIALIZATION_SEPARATOR);
values.append(item.type).append(AndroidUtilities.SERIALIZATION_SEPARATOR);
if (item.criterion.sql != null) {
values.append(item.criterion.sql);
}
values.append('\n');
String row =
Joiner.on(AndroidUtilities.SERIALIZATION_SEPARATOR)
.join(
asList(
escape(item.criterion.identifier),
escape(item.getValueFromCriterion()),
escape(item.criterion.text),
item.type,
item.criterion.sql == null ? "" : item.criterion.sql));
Timber.d("%s -> %s", item, row);
rows.add(row);
}
return Joiner.on("\n").join(rows);
}
private List<CriterionInstance> deserializeCriterion(@Nullable String criterion) {
if (Strings.isNullOrEmpty(criterion)) {
return Collections.emptyList();
}
List<CriterionInstance> entries = new ArrayList<>();
for (String row : criterion.split("\n")) {
CriterionInstance entry = new CriterionInstance();
List<String> split =
transform(
Splitter.on(AndroidUtilities.SERIALIZATION_SEPARATOR).splitToList(row),
CustomFilterActivity::unescape);
if (split.size() != 4 && split.size() != 5) {
Timber.e("invalid row: %s", row);
return Collections.emptyList();
}
return values.toString();
entry.criterion = filterCriteriaProvider.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) {
@ -105,6 +147,21 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
AndroidUtilities.SERIALIZATION_SEPARATOR, AndroidUtilities.SEPARATOR_ESCAPE);
}
private static String unescape(String item) {
if (Strings.isNullOrEmpty(item)) {
return "";
}
return item.replace(
AndroidUtilities.SEPARATOR_ESCAPE, AndroidUtilities.SERIALIZATION_SEPARATOR);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_CRITERIA, serializeFilters(adapter));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -121,9 +178,19 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
themeColor.apply(toolbar);
listView = findViewById(android.R.id.list);
List<CriterionInstance> startingCriteria = new ArrayList<>();
startingCriteria.add(getStartingUniverse());
adapter = new CustomFilterAdapter(this, dialogBuilder, startingCriteria, locale);
List<CriterionInstance> criteria =
new ArrayList<>(
deserializeCriterion(
savedInstanceState == null
? getIntent().getStringExtra(EXTRA_CRITERIA)
: savedInstanceState.getString(EXTRA_CRITERIA)));
if (criteria.isEmpty()) {
CriterionInstance instance = new CriterionInstance();
instance.criterion = filterCriteriaProvider.getStartingUniverse();
instance.type = CriterionInstance.TYPE_UNIVERSE;
criteria.add(instance);
}
adapter = new CustomFilterAdapter(this, dialogBuilder, criteria, locale);
listView.setAdapter(adapter);
updateList();
@ -135,22 +202,6 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
component.inject(this);
}
private CriterionInstance getStartingUniverse() {
CriterionInstance instance = new CriterionInstance();
instance.criterion =
new MultipleSelectCriterion(
IDENTIFIER_UNIVERSE,
getString(R.string.CFA_universe_all),
null,
null,
null,
null,
null,
null);
instance.type = CriterionInstance.TYPE_UNIVERSE;
return instance;
}
private void setUpListeners() {
findViewById(R.id.add).setOnClickListener(v -> listView.showContextMenu());
@ -270,11 +321,10 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
case CriterionInstance.TYPE_INTERSECT:
sql.append("AND ");
break;
case CriterionInstance.TYPE_UNIVERSE:
}
// special code for all tasks universe
if (instance.criterion.sql == null) {
if (instance.type == CriterionInstance.TYPE_UNIVERSE || instance.criterion.sql == null) {
sql.append(TaskCriteria.activeAndVisible()).append(' ');
} else {
String subSql = instance.criterion.sql.replace("?", UnaryCriterion.sanitize(value));
@ -381,65 +431,4 @@ public class CustomFilterActivity extends ThemedInjectingAppCompatActivity
storeObject.setId(filterDao.insertOrUpdate(storeObject));
return storeObject.getId() >= 0 ? storeObject : null;
}
public static 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;
/** criteria for this instance */
public CustomFilterCriterion criterion;
/** which of the entries is selected (MultipleSelect) */
public int selectedIndex = -1;
/** text of selection (TextInput) */
public String selectedText = null;
/** type of join */
public int type = TYPE_INTERSECT;
public int end;
/** statistics for filter count */
int start;
int max;
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$
}
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$
}
}
}

@ -20,7 +20,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import com.todoroo.astrid.api.MultipleSelectCriterion;
import com.todoroo.astrid.api.TextInputCriterion;
import com.todoroo.astrid.core.CustomFilterActivity.CriterionInstance;
import java.util.List;
import org.tasks.R;
import org.tasks.dialogs.AlertDialogBuilder;

@ -14,6 +14,7 @@ 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.core.CustomFilterActivity.CriterionInstance;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Task.Priority;
@ -35,13 +36,14 @@ import org.tasks.injection.ForApplication;
public class FilterCriteriaProvider {
private static final String IDENTIFIER_TITLE = "title"; // $NON-NLS-1$
private static final String IDENTIFIER_IMPORTANCE = "importance"; // $NON-NLS-1$
private static final String IDENTIFIER_DUEDATE = "dueDate"; // $NON-NLS-1$
private static final String IDENTIFIER_GTASKS = "gtaskslist"; // $NON-NLS-1$
private static final String IDENTIFIER_CALDAV = "caldavlist"; // $NON-NLS-1$
private static final String IDENTIFIER_TAG_IS = "tag_is"; // $NON-NLS-1$
private static final String IDENTIFIER_TAG_CONTAINS = "tag_contains"; // $NON-NLS-1$
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 TagDataDao tagDataDao;
@ -63,6 +65,41 @@ public class FilterCriteriaProvider {
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.CFA_universe_all),
null,
null,
null,
null,
null,
null);
}
public List<CustomFilterCriterion> getAll() {
List<CustomFilterCriterion> result = newArrayList();

Loading…
Cancel
Save