diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index b420d1e3e..000000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ba4c49124..703e5d4b8 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,48 +1,11 @@
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0e2c3f21..4fa287372 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
Change Log
---
+### 7.3.2 (2019-12-12)
+
+* Fix slow query for subtasks
+* Fix duplicated multi-level subtask count
+* Fix setting icon on new CalDAV list
+* Fix clear completed for subtasks
+* Fix crash when clearing 1000+ tasks
+
### 7.3.1 (2019-12-05)
* Fix crash on missing filter
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 10470d559..ee85ef122 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -36,8 +36,8 @@ android {
defaultConfig {
testApplicationId = "org.tasks.test"
applicationId = "org.tasks"
- versionCode = 632
- versionName = "7.3.1"
+ versionCode = 633
+ versionName = "7.3.2"
targetSdkVersion(Versions.targetSdk)
minSdkVersion(Versions.minSdk)
multiDexEnabled = true
diff --git a/app/src/androidTest/java/org/tasks/TestUtilities.java b/app/src/androidTest/java/org/tasks/TestUtilities.java
index bcae02589..f978b2d93 100644
--- a/app/src/androidTest/java/org/tasks/TestUtilities.java
+++ b/app/src/androidTest/java/org/tasks/TestUtilities.java
@@ -59,7 +59,7 @@ public class TestUtilities {
private static at.bitfire.ical4android.Task fromString(String task) {
try {
- return at.bitfire.ical4android.Task.Companion.fromReader(new StringReader(task)).get(0);
+ return at.bitfire.ical4android.Task.Companion.tasksFromReader(new StringReader(task)).get(0);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
diff --git a/app/src/androidTest/java/org/tasks/db/QueryUtilsTest.java b/app/src/androidTest/java/org/tasks/db/QueryUtilsTest.java
new file mode 100644
index 000000000..f972ad0a1
--- /dev/null
+++ b/app/src/androidTest/java/org/tasks/db/QueryUtilsTest.java
@@ -0,0 +1,42 @@
+package org.tasks.db;
+
+import static org.junit.Assert.assertEquals;
+import static org.tasks.db.QueryUtils.showCompleted;
+import static org.tasks.db.QueryUtils.showHidden;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.todoroo.andlib.sql.Functions;
+import com.todoroo.astrid.data.Task;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class QueryUtilsTest {
+ @Test
+ public void replaceHiddenLT() {
+ assertEquals(
+ "(1)",
+ showHidden(Task.HIDE_UNTIL.lt(Functions.now()).toString()));
+ }
+
+ @Test
+ public void replaceHiddenLTE() {
+ assertEquals(
+ "(1)",
+ showHidden(Task.HIDE_UNTIL.lte(Functions.now()).toString()));
+ }
+
+ @Test
+ public void replaceUncompletedEQ() {
+ assertEquals(
+ "(1)",
+ showCompleted(Task.COMPLETION_DATE.eq(0).toString()));
+ }
+
+ @Test
+ public void replaceUncompletedLTE() {
+ assertEquals(
+ "(1)",
+ showCompleted(Task.COMPLETION_DATE.lte(0).toString()));
+ }
+}
diff --git a/app/src/main/java/com/todoroo/andlib/sql/Criterion.java b/app/src/main/java/com/todoroo/andlib/sql/Criterion.java
index 5a3ef4a91..9fef13186 100644
--- a/app/src/main/java/com/todoroo/andlib/sql/Criterion.java
+++ b/app/src/main/java/com/todoroo/andlib/sql/Criterion.java
@@ -15,13 +15,6 @@ import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class Criterion {
- public static final Criterion all =
- new Criterion(Operator.exists) {
- @Override
- protected void populate(StringBuilder sb) {
- sb.append(1);
- }
- };
final Operator operator;
public Criterion(Operator operator) {
diff --git a/app/src/main/java/com/todoroo/andlib/sql/Field.java b/app/src/main/java/com/todoroo/andlib/sql/Field.java
index 511e8e63d..c35b4998f 100644
--- a/app/src/main/java/com/todoroo/andlib/sql/Field.java
+++ b/app/src/main/java/com/todoroo/andlib/sql/Field.java
@@ -39,6 +39,10 @@ public class Field extends DBObject {
return UnaryCriterion.gt(this, value);
}
+ public Criterion gte(Object value) {
+ return UnaryCriterion.gte(this, value);
+ }
+
public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value);
}
diff --git a/app/src/main/java/com/todoroo/andlib/sql/Operator.java b/app/src/main/java/com/todoroo/andlib/sql/Operator.java
index 4c6deae3b..ff312e12d 100644
--- a/app/src/main/java/com/todoroo/andlib/sql/Operator.java
+++ b/app/src/main/java/com/todoroo/andlib/sql/Operator.java
@@ -19,6 +19,7 @@ public final class Operator {
static final Operator neq = new Operator("<>");
static final Operator isNotNull = new Operator("IS NOT NULL");
static final Operator gt = new Operator(">");
+ static final Operator gte = new Operator(">=");
static final Operator lt = new Operator("<");
static final Operator lte = new Operator("<=");
private final String operator;
diff --git a/app/src/main/java/com/todoroo/andlib/sql/UnaryCriterion.java b/app/src/main/java/com/todoroo/andlib/sql/UnaryCriterion.java
index c87b32f5d..118e75013 100644
--- a/app/src/main/java/com/todoroo/andlib/sql/UnaryCriterion.java
+++ b/app/src/main/java/com/todoroo/andlib/sql/UnaryCriterion.java
@@ -36,6 +36,10 @@ public class UnaryCriterion extends Criterion {
return new UnaryCriterion(field, Operator.gt, value);
}
+ static Criterion gte(Field field, Object value) {
+ return new UnaryCriterion(field, Operator.gte, value);
+ }
+
static Criterion lt(Field field, Object value) {
return new UnaryCriterion(field, Operator.lt, value);
}
diff --git a/app/src/main/java/com/todoroo/astrid/api/TagFilter.java b/app/src/main/java/com/todoroo/astrid/api/TagFilter.java
index 722c759dd..e74e9e316 100644
--- a/app/src/main/java/com/todoroo/astrid/api/TagFilter.java
+++ b/app/src/main/java/com/todoroo/astrid/api/TagFilter.java
@@ -4,7 +4,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.todoroo.andlib.sql.Criterion;
-import com.todoroo.andlib.sql.Field;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.astrid.dao.TaskDao;
@@ -51,10 +50,8 @@ public class TagFilter extends Filter {
private static QueryTemplate queryTemplate(String uuid) {
return new QueryTemplate()
- .join(Join.inner(Tag.TABLE.as("mtags"), Task.UUID.eq(Field.field("mtags.task_uid"))))
- .where(
- Criterion.and(
- Field.field("mtags.tag_uid").eq(uuid), TaskDao.TaskCriteria.activeAndVisible()));
+ .join(Join.inner(Tag.TABLE, Task.ID.eq(Tag.TASK)))
+ .where(Criterion.and(Tag.TAG_UID.eq(uuid), TaskDao.TaskCriteria.activeAndVisible()));
}
private static Map getValuesForNewTask(TagData tagData) {
diff --git a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java
index d949dd005..d1b035e86 100644
--- a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java
+++ b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java
@@ -80,8 +80,7 @@ public final class BuiltInFilterExposer {
Criterion.and(
Criterion.not(
Task.UUID.in(Query.select(Field.field("task_uid")).from(Tag.TABLE))),
- TaskCriteria.isActive(),
- TaskCriteria.isVisible())));
+ TaskCriteria.activeAndVisible())));
}
public static boolean isInbox(Context context, Filter filter) {
diff --git a/app/src/main/java/com/todoroo/astrid/core/SortHelper.java b/app/src/main/java/com/todoroo/astrid/core/SortHelper.java
index 5afe3c72f..35c35ddec 100644
--- a/app/src/main/java/com/todoroo/astrid/core/SortHelper.java
+++ b/app/src/main/java/com/todoroo/astrid/core/SortHelper.java
@@ -6,7 +6,9 @@
package com.todoroo.astrid.core;
-import static com.todoroo.astrid.dao.TaskDao.TaskCriteria.isVisible;
+import static org.tasks.db.QueryUtils.showCompleted;
+import static org.tasks.db.QueryUtils.showHidden;
+import static org.tasks.db.QueryUtils.showRecentlyCompleted;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
@@ -57,19 +59,12 @@ public class SortHelper {
// flags
if (preferences.getBoolean(R.string.p_show_completed_tasks, false)) {
- adjustedSql =
- adjustedSql.replace(Task.COMPLETION_DATE.eq(0).toString(), Criterion.all.toString());
+ adjustedSql = showCompleted(adjustedSql);
} else if (preferences.getBoolean(R.string.p_temporarily_show_completed_tasks, false)) {
- adjustedSql =
- adjustedSql.replace(
- Task.COMPLETION_DATE.eq(0).toString(),
- Criterion.or(
- Task.COMPLETION_DATE.lte(0),
- Task.COMPLETION_DATE.gt(DateUtilities.now() - 60000))
- .toString());
+ adjustedSql = showRecentlyCompleted(adjustedSql);
}
if (preferences.getBoolean(R.string.p_show_hidden_tasks, false)) {
- adjustedSql = adjustedSql.replace(isVisible().toString(), Criterion.all.toString());
+ adjustedSql = showHidden(adjustedSql);
}
return adjustedSql;
diff --git a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java
index 5fba30da7..3b6ca46b2 100644
--- a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java
+++ b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.java
@@ -136,9 +136,11 @@ public abstract class TaskDao {
public abstract int clearCompletedCalendarEvents();
@Transaction
- public List fetchTasks(Function> getQueries) {
+ public List fetchTasks(QueryCallback callback) {
long start = BuildConfig.DEBUG ? now() : 0;
- List queries = getQueries.apply(atLeastLollipop() && hasSubtasks());
+ boolean includeGoogleSubtasks = atLeastLollipop() && hasGoogleTaskSubtasks();
+ boolean includeCaldavSubtasks = atLeastLollipop() && hasCaldavSubtasks();
+ List queries = callback.getQueries(includeGoogleSubtasks, includeCaldavSubtasks);
SupportSQLiteDatabase db = database.getOpenHelper().getWritableDatabase();
int last = queries.size() - 1;
for (int i = 0 ; i < last ; i++) {
@@ -155,12 +157,11 @@ public abstract class TaskDao {
@RawQuery
abstract int count(SimpleSQLiteQuery query);
- @Query(
- "SELECT EXISTS("
- + "SELECT 1 FROM google_tasks WHERE gt_parent > 0 AND gt_deleted = 0"
- + " UNION ALL "
- + "SELECT 1 FROM caldav_tasks WHERE cd_parent > 0 AND cd_deleted = 0);")
- public abstract boolean hasSubtasks();
+ @Query("SELECT EXISTS(SELECT 1 FROM caldav_tasks WHERE cd_parent > 0 AND cd_deleted = 0)")
+ abstract boolean hasCaldavSubtasks();
+
+ @Query("SELECT EXISTS(SELECT 1 FROM google_tasks WHERE gt_parent > 0 AND gt_deleted = 0)")
+ abstract boolean hasGoogleTaskSubtasks();
@Query("UPDATE tasks SET modified = datetime('now', 'localtime') WHERE _id in (:ids)")
public abstract void touch(List ids);
@@ -258,31 +259,16 @@ public abstract class TaskDao {
/** Generates SQL clauses */
public static class TaskCriteria {
- /** @return tasks that were not deleted */
- public static Criterion notDeleted() {
- return Task.DELETION_DATE.eq(0);
- }
-
- public static Criterion notCompleted() {
- return Task.COMPLETION_DATE.eq(0);
- }
-
/** @return tasks that have not yet been completed or deleted */
public static Criterion activeAndVisible() {
return Criterion.and(
- Task.COMPLETION_DATE.eq(0),
- Task.DELETION_DATE.eq(0),
+ Task.COMPLETION_DATE.lte(0),
+ Task.DELETION_DATE.lte(0),
Task.HIDE_UNTIL.lt(Functions.now()));
}
+ }
- /** @return tasks that have not yet been completed or deleted */
- public static Criterion isActive() {
- return Criterion.and(Task.COMPLETION_DATE.eq(0), Task.DELETION_DATE.eq(0));
- }
-
- /** @return tasks that are not hidden at current time */
- public static Criterion isVisible() {
- return Task.HIDE_UNTIL.lt(Functions.now());
- }
+ public interface QueryCallback {
+ List getQueries(boolean includeGoogleTaskSubtasks, boolean includeCaldavSubtasks);
}
}
diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java
index 708910118..ae7a6c2cd 100644
--- a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java
+++ b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java
@@ -1,9 +1,7 @@
package com.todoroo.astrid.service;
-import static com.todoroo.andlib.sql.Criterion.all;
-import static com.todoroo.astrid.dao.TaskDao.TaskCriteria.isVisible;
-import static com.todoroo.astrid.dao.TaskDao.TaskCriteria.notCompleted;
-import static org.tasks.db.DbUtils.batch;
+import static org.tasks.db.DbUtils.collect;
+import static org.tasks.db.QueryUtils.showHiddenAndCompleted;
import com.google.common.collect.ImmutableList;
import com.todoroo.astrid.api.Filter;
@@ -67,13 +65,13 @@ public class TaskDeleter {
public List markDeleted(List taskIds) {
Set ids = new HashSet<>(taskIds);
- batch(taskIds, i -> ids.addAll(googleTaskDao.getChildren(i)));
- batch(taskIds, i -> ids.addAll(caldavDao.getChildren(i)));
+ ids.addAll(collect(taskIds, googleTaskDao::getChildren));
+ ids.addAll(collect(taskIds, caldavDao::getChildren));
deletionDao.markDeleted(ids);
- workManager.cleanup(taskIds);
+ workManager.cleanup(ids);
workManager.sync(false);
localBroadcastManager.broadcastRefresh();
- return taskDao.fetch(taskIds);
+ return collect(ids, taskDao::fetch);
}
public void delete(Task task) {
@@ -89,13 +87,12 @@ public class TaskDeleter {
public int clearCompleted(Filter filter) {
List completed = new ArrayList<>();
Filter deleteFilter = new Filter(null, null);
- deleteFilter.setFilterQueryOverride(
- filter
- .getOriginalSqlQuery()
- .replace(isVisible().toString(), all.toString())
- .replace(notCompleted().toString(), all.toString()));
- List tasks = taskDao.fetchTasks(
- hasSubtasks -> TaskListViewModel.getQuery(preferences, deleteFilter, hasSubtasks));
+ deleteFilter.setFilterQueryOverride(showHiddenAndCompleted(filter.getOriginalSqlQuery()));
+ List tasks =
+ taskDao.fetchTasks(
+ (includeGoogleSubtasks, includeCaldavSubtasks) ->
+ TaskListViewModel.getQuery(
+ preferences, deleteFilter, includeGoogleSubtasks, includeCaldavSubtasks));
for (TaskContainer task : tasks) {
if (task.isCompleted()) {
completed.add(task.getId());
diff --git a/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java b/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java
index 390d16cbc..458aee468 100644
--- a/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java
+++ b/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksFilterUpdater.java
@@ -1,5 +1,7 @@
package com.todoroo.astrid.subtasks;
+import static org.tasks.db.QueryUtils.showHiddenAndCompleted;
+
import android.text.TextUtils;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao;
@@ -133,10 +135,7 @@ public class SubtasksFilterUpdater {
query = query.replaceAll("ORDER BY .*", "");
query = query + String.format("ORDER BY %s", getOrderString());
- query =
- query.replace(
- TaskDao.TaskCriteria.activeAndVisible().toString(),
- TaskDao.TaskCriteria.notDeleted().toString());
+ query = showHiddenAndCompleted(query);
filter.setFilterQueryOverride(query);
}
@@ -165,10 +164,7 @@ public class SubtasksFilterUpdater {
Set idsInQuery = new HashSet<>();
String sql = filter.getSqlQuery().replaceAll("ORDER BY .*", ""); // $NON-NLS-1$//$NON-NLS-2$
sql = sql + " ORDER BY created"; // $NON-NLS-1$
- sql =
- sql.replace(
- TaskDao.TaskCriteria.activeAndVisible().toString(),
- TaskDao.TaskCriteria.notDeleted().toString());
+ sql = showHiddenAndCompleted(sql);
List tasks = taskDao.fetchFiltered(sql);
for (Task task : tasks) {
String id = task.getUuid();
diff --git a/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java b/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java
index 0e0f8423f..d11be95fc 100644
--- a/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java
+++ b/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java
@@ -1,12 +1,14 @@
package com.todoroo.astrid.subtasks;
+import static org.tasks.db.QueryUtils.showHidden;
+
import android.content.Context;
import android.text.TextUtils;
-import com.todoroo.andlib.sql.Criterion;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.GtasksFilter;
import com.todoroo.astrid.core.BuiltInFilterExposer;
import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.subtasks.SubtasksFilterUpdater.Node;
import java.util.ArrayList;
@@ -146,8 +148,7 @@ public class SubtasksHelper {
query = query.replaceAll("ORDER BY .*", "");
query = query + String.format(" ORDER BY %s", getOrderString(tagData, tlm));
- query =
- query.replace(TaskDao.TaskCriteria.isVisible().toString(), Criterion.all.toString());
+ query = showHidden(query);
}
filter.setFilterQueryOverride(query);
diff --git a/app/src/main/java/org/tasks/db/DbUtils.java b/app/src/main/java/org/tasks/db/DbUtils.java
index e4f05f017..26df62ab0 100644
--- a/app/src/main/java/org/tasks/db/DbUtils.java
+++ b/app/src/main/java/org/tasks/db/DbUtils.java
@@ -1,10 +1,12 @@
package org.tasks.db;
+import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.partition;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.tasks.Callback;
@@ -12,9 +14,9 @@ public class DbUtils {
private static final int MAX_SQLITE_ARGS = 990;
- public static List collect(List items, Function, List> func) {
+ public static List collect(Collection items, Function, List> func) {
if (items.size() < MAX_SQLITE_ARGS) {
- return func.apply(items);
+ return func.apply(items instanceof List ? (List) items : newArrayList(items));
}
List result = new ArrayList<>();
batch(items, b -> result.addAll(func.apply(b)));
diff --git a/app/src/main/java/org/tasks/db/QueryUtils.java b/app/src/main/java/org/tasks/db/QueryUtils.java
new file mode 100644
index 000000000..34f89bb73
--- /dev/null
+++ b/app/src/main/java/org/tasks/db/QueryUtils.java
@@ -0,0 +1,36 @@
+package org.tasks.db;
+
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.data.Task;
+import java.util.regex.Pattern;
+
+public class QueryUtils {
+
+ private static final Pattern HIDDEN =
+ Pattern.compile("tasks\\.hideUntil<=?\\(strftime\\('%s','now'\\)\\*1000\\)");
+
+ private static final Pattern UNCOMPLETED = Pattern.compile("tasks\\.completed=0");
+
+ public static String showHidden(String query) {
+ return HIDDEN.matcher(query).replaceAll("1");
+ }
+
+ public static String showCompleted(String query) {
+ return UNCOMPLETED.matcher(query).replaceAll("1");
+ }
+
+ public static String showHiddenAndCompleted(String query) {
+ return showCompleted(showHidden(query));
+ }
+
+ public static String showRecentlyCompleted(String query) {
+ return UNCOMPLETED
+ .matcher(query)
+ .replaceAll(
+ Criterion.or(
+ Task.COMPLETION_DATE.lte(0),
+ Task.COMPLETION_DATE.gte(DateUtilities.now() - 59999))
+ .toString());
+ }
+}
diff --git a/app/src/main/java/org/tasks/jobs/WorkManager.java b/app/src/main/java/org/tasks/jobs/WorkManager.java
index 601f2833f..113dd2c9b 100644
--- a/app/src/main/java/org/tasks/jobs/WorkManager.java
+++ b/app/src/main/java/org/tasks/jobs/WorkManager.java
@@ -87,7 +87,7 @@ public class WorkManager {
.build());
}
- public void cleanup(List ids) {
+ public void cleanup(Iterable ids) {
batch(
ids,
MAX_CLEANUP_LENGTH,
diff --git a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java
index ab9336054..1112408ed 100644
--- a/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java
+++ b/app/src/main/java/org/tasks/tasklist/TaskListRecyclerAdapter.java
@@ -37,6 +37,8 @@ import org.tasks.intents.TaskIntents;
public class TaskListRecyclerAdapter extends RecyclerView.Adapter
implements ViewHolder.ViewHolderCallbacks, ListUpdateCallback {
+ private static final int LONG_LIST_SIZE = 500;
+
private final TaskAdapter adapter;
private final TaskListFragment taskList;
private final RecyclerView recyclerView;
@@ -227,7 +229,9 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter
assertNotMainThread();
DiffCallback cb = new DiffCallback(last.first, next, adapter);
- DiffResult result = DiffUtil.calculateDiff(cb, true);
+ boolean shortList = next.size() < LONG_LIST_SIZE;
+ boolean calculateDiff = last.first.size() != next.size() || shortList;
+ DiffResult result = calculateDiff ? DiffUtil.calculateDiff(cb, shortList) : null;
return Pair.create(next, result);
}
@@ -248,7 +252,11 @@ public class TaskListRecyclerAdapter extends RecyclerView.Adapter
Pair, DiffResult> update = updates.poll();
while (update != null) {
list = update.first;
- update.second.dispatchUpdatesTo((ListUpdateCallback) this);
+ if (update.second == null) {
+ notifyDataSetChanged();
+ } else {
+ update.second.dispatchUpdatesTo((ListUpdateCallback) this);
+ }
update = updates.poll();
}
}
diff --git a/app/src/main/java/org/tasks/ui/TaskListViewModel.java b/app/src/main/java/org/tasks/ui/TaskListViewModel.java
index 9bc489c9a..89fe6a076 100644
--- a/app/src/main/java/org/tasks/ui/TaskListViewModel.java
+++ b/app/src/main/java/org/tasks/ui/TaskListViewModel.java
@@ -100,10 +100,15 @@ public class TaskListViewModel extends ViewModel {
tasks.observe(owner, observer);
}
- public static List getQuery(Preferences preferences, Filter filter, boolean subtasks) {
+ public static List getQuery(
+ Preferences preferences,
+ Filter filter,
+ boolean includeGoogleTaskSubtasks,
+ boolean includeCaldavSubtasks) {
List fields = newArrayList(TASKS, GTASK, CALDAV, GEOFENCE, PLACE);
- if (subtasks && !(preferences.isManualSort() && filter.supportsManualSort())) {
+ if ((includeGoogleTaskSubtasks || includeCaldavSubtasks)
+ && !(preferences.isManualSort() && filter.supportsManualSort())) {
String tagQuery =
Query.select(field("group_concat(distinct(tag_uid))"))
.from(Tag.TABLE)
@@ -118,10 +123,12 @@ public class TaskListViewModel extends ViewModel {
+ Tag.TASK;
fields.add(field("(" + tagQuery + ")").as("tags"));
fields.add(INDENT);
- fields.add(field("(SELECT count(distinct task) FROM recursive_tasks WHERE parent = tasks._id GROUP BY parent)").as("children"));
+ fields.add(CHILDREN);
- String joinedQuery = Join.inner(RECURSIVE, Task.ID.eq(RECURSIVE_TASK)) + JOINS +
- " WHERE recursive_tasks.hidden = 0";
+ String joinedQuery = Join.inner(RECURSIVE, Task.ID.eq(RECURSIVE_TASK))
+ + " LEFT JOIN (SELECT parent, count(recursive_tasks.task) AS children FROM recursive_tasks GROUP BY parent) AS recursive_children ON recursive_children.parent = tasks._id "
+ + JOINS;
+ String where = " WHERE recursive_tasks.hidden = 0";
String parentQuery;
QueryTemplate subtaskQuery = new QueryTemplate();
if (filter instanceof CaldavFilter) {
@@ -144,8 +151,6 @@ public class TaskListViewModel extends ViewModel {
Join.inner(
CaldavTask.TABLE,
Criterion.and(
- CaldavTask.CALENDAR.eq(calendar.getUuid()),
- CaldavTask.PARENT.gt(0),
CaldavTask.TASK.eq(Task.ID),
CaldavTask.DELETED.eq(0))))
.where(TaskCriteria.activeAndVisible());
@@ -169,37 +174,23 @@ public class TaskListViewModel extends ViewModel {
Join.inner(
GoogleTask.TABLE,
Criterion.and(
- GoogleTask.LIST.eq(list.getRemoteId()),
- GoogleTask.PARENT.gt(0),
GoogleTask.TASK.eq(Task.ID),
GoogleTask.DELETED.eq(0))))
.where(TaskCriteria.activeAndVisible());
} else {
parentQuery = PermaSql.replacePlaceholdersForQuery(filter.getSqlQuery());
- subtaskQuery
- .join(
- Join.left(
- GoogleTask.TABLE,
- Criterion.and(
- GoogleTask.PARENT.gt(0),
- GoogleTask.TASK.eq(Task.ID),
- GoogleTask.DELETED.eq(0))))
- .join(
- Join.left(
- CaldavTask.TABLE,
- Criterion.and(
- CaldavTask.PARENT.gt(0),
- CaldavTask.TASK.eq(Task.ID),
- CaldavTask.DELETED.eq(0))))
- .join(
- Join.inner(
- RECURSIVE,
- Criterion.or(
- GoogleTask.PARENT.eq(RECURSIVE_TASK),
- CaldavTask.PARENT.eq(RECURSIVE_TASK))))
- .where(TaskCriteria.activeAndVisible());
- joinedQuery += " AND indent = (select max(indent) from recursive_tasks where tasks._id = recursive_tasks.task) ";
+ if (includeGoogleTaskSubtasks && includeCaldavSubtasks) {
+ addGoogleAndCaldavSubtasks(subtaskQuery);
+ } else if (includeGoogleTaskSubtasks) {
+ addGoogleSubtasks(subtaskQuery);
+ } else {
+ addCaldavSubtasks(subtaskQuery);
+ }
+ subtaskQuery.where(TaskCriteria.activeAndVisible());
+ joinedQuery += " LEFT JOIN (SELECT task, max(indent) AS max_indent FROM recursive_tasks GROUP BY task) AS recursive_indents ON recursive_indents.task = tasks._id ";
+ where += " AND indent = max_indent ";
}
+ joinedQuery += where;
String sortSelect = SortHelper.orderSelectForSortTypeRecursive(preferences.getSortMode());
String withClause = "CREATE TEMPORARY TABLE `recursive_tasks` AS\n"
@@ -266,6 +257,50 @@ public class TaskListViewModel extends ViewModel {
}
}
+ private static void addGoogleSubtasks(QueryTemplate subtaskQuery) {
+ subtaskQuery
+ .join(Join.inner(RECURSIVE, GoogleTask.PARENT.eq(RECURSIVE_TASK)))
+ .join(
+ Join.inner(
+ GoogleTask.TABLE,
+ Criterion.and(
+ GoogleTask.TASK.eq(Task.ID),
+ GoogleTask.DELETED.eq(0))));
+ }
+
+ private static void addCaldavSubtasks(QueryTemplate subtaskQuery) {
+ subtaskQuery
+ .join(Join.inner(RECURSIVE, CaldavTask.PARENT.eq(RECURSIVE_TASK)))
+ .join(
+ Join.inner(
+ CaldavTask.TABLE,
+ Criterion.and(
+ CaldavTask.TASK.eq(Task.ID),
+ CaldavTask.DELETED.eq(0))));
+ }
+
+ private static void addGoogleAndCaldavSubtasks(QueryTemplate subtaskQuery) {
+ subtaskQuery
+ .join(
+ Join.inner(
+ RECURSIVE,
+ Criterion.or(
+ GoogleTask.PARENT.eq(RECURSIVE_TASK),
+ CaldavTask.PARENT.eq(RECURSIVE_TASK))))
+ .join(
+ Join.left(
+ GoogleTask.TABLE,
+ Criterion.and(
+ GoogleTask.TASK.eq(Task.ID),
+ GoogleTask.DELETED.eq(0))))
+ .join(
+ Join.left(
+ CaldavTask.TABLE,
+ Criterion.and(
+ CaldavTask.TASK.eq(Task.ID),
+ CaldavTask.DELETED.eq(0))));
+ }
+
public void searchByFilter(Filter filter) {
this.filter = filter;
invalidate();
@@ -279,7 +314,13 @@ public class TaskListViewModel extends ViewModel {
disposable.add(
Single.fromCallable(
() ->
- taskDao.fetchTasks(hasSubtasks -> getQuery(preferences, filter, hasSubtasks)))
+ taskDao.fetchTasks(
+ ((includeGoogleSubtasks, includeCaldavSubtasks) ->
+ getQuery(
+ preferences,
+ filter,
+ includeGoogleSubtasks,
+ includeCaldavSubtasks))))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(tasks::postValue, Timber::e));
diff --git a/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.java b/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.java
index 062911f97..e3aed1674 100644
--- a/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.java
+++ b/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.java
@@ -82,7 +82,10 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
@Override
public void onDataSetChanged() {
updateSettings();
- tasks = taskDao.fetchTasks(hasSubtasks -> getQuery(filter, hasSubtasks));
+ tasks =
+ taskDao.fetchTasks(
+ (includeGoogleSubtasks, includeCaldavSubtasks) ->
+ getQuery(filter, includeGoogleSubtasks, includeCaldavSubtasks));
}
@Override
@@ -196,7 +199,8 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
return position < tasks.size() ? tasks.get(position) : null;
}
- private List getQuery(Filter filter, boolean hasSubtasks) {
+ private List getQuery(
+ Filter filter, boolean includeGoogleSubtasks, boolean includeCaldavSubtasks) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.scrollable_widget);
rv.setTextViewText(R.id.widget_title, filter.listingTitle);
@@ -204,7 +208,9 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
rv.setInt(R.id.widget, "setLayoutDirection", Locale.getInstance(context).getDirectionality());
}
appWidgetManager.partiallyUpdateAppWidget(widgetId, rv);
- List queries = TaskListViewModel.getQuery(preferences, filter, hasSubtasks);
+ List queries =
+ TaskListViewModel.getQuery(
+ preferences, filter, includeGoogleSubtasks, includeCaldavSubtasks);
int last = queries.size() - 1;
queries.set(last, subtasksHelper.applySubtasksToWidgetFilter(filter, queries.get(last)));
return queries;
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index beb6fde67..ec71966aa 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -1,5 +1,5 @@
object Versions {
- const val kotlin = "1.3.40"
+ const val kotlin = "1.3.61"
const val targetSdk = 29
const val minSdk = 16
const val dagger = "2.25.2"