Convert remaining Room DAOs to Kotlin

pull/996/head
Alex Baker 4 years ago
parent 36b80fcbbf
commit 0fa9149f28

@ -28,35 +28,35 @@ class TaskDaoTests : InjectingTestCase() {
/** Test basic task creation, fetch, and save */
@Test
fun testTaskCreation() {
assertEquals(0, taskDao.all.size)
assertEquals(0, taskDao.getAll().size)
// create task "happy"
var task = Task()
task.setTitle("happy")
taskDao.createNew(task)
assertEquals(1, taskDao.all.size)
assertEquals(1, taskDao.getAll().size)
val happyId = task.getId()
assertNotSame(Task.NO_ID, happyId)
task = taskDao.fetch(happyId)
task = taskDao.fetch(happyId)!!
assertEquals("happy", task.getTitle())
// create task "sad"
task = Task()
task.setTitle("sad")
taskDao.createNew(task)
assertEquals(2, taskDao.all.size)
assertEquals(2, taskDao.getAll().size)
// rename sad to melancholy
val sadId = task.getId()
assertNotSame(Task.NO_ID, sadId)
task.setTitle("melancholy")
taskDao.save(task)
assertEquals(2, taskDao.all.size)
assertEquals(2, taskDao.getAll().size)
// check state
task = taskDao.fetch(happyId)
task = taskDao.fetch(happyId)!!
assertEquals("happy", task.getTitle())
task = taskDao.fetch(sadId)
task = taskDao.fetch(sadId)!!
assertEquals("melancholy", task.getTitle())
}
@ -96,26 +96,26 @@ class TaskDaoTests : InjectingTestCase() {
taskDao.createNew(task)
// check is active
assertEquals(5, taskDao.activeTasks.size)
assertEquals(5, taskDao.getActiveTasks().size)
// check is visible
assertEquals(5, taskDao.visibleTasks.size)
assertEquals(5, taskDao.getActiveTasks().size)
}
/** Test task deletion */
@Test
fun testTDeletion() {
assertEquals(0, taskDao.all.size)
assertEquals(0, taskDao.getAll().size)
// create task "happy"
val task = Task()
task.setTitle("happy")
taskDao.createNew(task)
assertEquals(1, taskDao.all.size)
assertEquals(1, taskDao.getAll().size)
// delete
taskDeleter.delete(task)
assertEquals(0, taskDao.all.size)
assertEquals(0, taskDao.getAll().size)
}
/** Test save without prior create doesn't work */
@ -126,18 +126,18 @@ class TaskDaoTests : InjectingTestCase() {
task.setTitle("happy")
task.setId(1L)
taskDao.save(task)
assertEquals(0, taskDao.all.size)
assertEquals(0, taskDao.getAll().size)
}
/** Test passing invalid task indices to various things */
@Test
fun testInvalidIndex() {
assertEquals(0, taskDao.all.size)
assertEquals(0, taskDao.getAll().size)
assertNull(taskDao.fetch(1))
taskDeleter.delete(listOf(1L))
// make sure db still works
assertEquals(0, taskDao.all.size)
assertEquals(0, taskDao.getAll().size)
}
@Test

@ -50,7 +50,7 @@ class TaskMoverTest : InjectingTestCase() {
createTasks(1)
googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1")))
moveToGoogleTasks("2", 1)
assertEquals("2", googleTaskDao.getByTaskId(1).listId)
assertEquals("2", googleTaskDao.getByTaskId(1)!!.listId)
}
@Test
@ -74,7 +74,7 @@ class TaskMoverTest : InjectingTestCase() {
assertEquals(1, deleted.size.toLong())
assertEquals(2, deleted[0].task)
assertTrue(deleted[0].deleted > 0)
val task = googleTaskDao.getByTaskId(2)
val task = googleTaskDao.getByTaskId(2)!!
assertEquals(1, task.parent)
assertEquals("2", task.listId)
}
@ -121,7 +121,7 @@ class TaskMoverTest : InjectingTestCase() {
assertEquals(3, deleted.size.toLong())
val task = caldavDao.getTask(3)
assertEquals("2", task!!.calendar)
assertEquals(2, taskDao.fetch(3).getParent())
assertEquals(2, taskDao.fetch(3)!!.getParent())
}
@Test
@ -132,7 +132,7 @@ class TaskMoverTest : InjectingTestCase() {
moveToCaldavList("1", 1)
val task = caldavDao.getTask(2)
assertEquals("1", task!!.calendar)
assertEquals(1, taskDao.fetch(2).getParent())
assertEquals(1, taskDao.fetch(2)!!.getParent())
}
@Test
@ -141,8 +141,8 @@ class TaskMoverTest : InjectingTestCase() {
createSubtask(2, 1)
createSubtask(3, 2)
moveToGoogleTasks("1", 1)
assertEquals(1, googleTaskDao.getByTaskId(3).parent)
assertEquals(0, taskDao.fetch(3).getParent())
assertEquals(1, googleTaskDao.getByTaskId(3)!!.parent)
assertEquals(0, taskDao.fetch(3)!!.getParent())
}
@Test
@ -150,7 +150,7 @@ class TaskMoverTest : InjectingTestCase() {
createTasks(1)
createSubtask(2, 1)
moveToGoogleTasks("1", 2)
assertEquals(0, taskDao.fetch(2).getParent())
assertEquals(0, taskDao.fetch(2)!!.getParent())
}
@Test
@ -158,7 +158,7 @@ class TaskMoverTest : InjectingTestCase() {
createTasks(1)
createSubtask(2, 1)
moveToCaldavList("1", 2)
assertEquals(0, taskDao.fetch(2).getParent())
assertEquals(0, taskDao.fetch(2)!!.getParent())
}
@Test
@ -180,7 +180,7 @@ class TaskMoverTest : InjectingTestCase() {
with(CALENDAR, "1"),
with(REMOTE_PARENT, "b"))))
moveToGoogleTasks("1", 1)
val task = googleTaskDao.getByTaskId(3L)
val task = googleTaskDao.getByTaskId(3L)!!
assertEquals(1, task.parent)
}
@ -190,7 +190,7 @@ class TaskMoverTest : InjectingTestCase() {
googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1")))
googleTaskDao.insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L)))
moveToGoogleTasks("2", 2)
val task = googleTaskDao.getByTaskId(2)
val task = googleTaskDao.getByTaskId(2)!!
assertEquals(0L, task.parent)
assertEquals("2", task.listId)
}
@ -209,7 +209,7 @@ class TaskMoverTest : InjectingTestCase() {
with(REMOTE_PARENT, "a"))))
moveToCaldavList("2", 2)
assertEquals("2", caldavDao.getTask(2)!!.calendar)
assertEquals(0, taskDao.fetch(2).getParent())
assertEquals(0, taskDao.fetch(2)!!.getParent())
}
@Test
@ -225,7 +225,7 @@ class TaskMoverTest : InjectingTestCase() {
createTasks(1)
caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1")))
moveToGoogleTasks("2", 1)
assertEquals("2", googleTaskDao.getByTaskId(1L).listId)
assertEquals("2", googleTaskDao.getByTaskId(1L)!!.listId)
}
@Test
@ -235,7 +235,7 @@ class TaskMoverTest : InjectingTestCase() {
createSubtask(3, 2)
moveToCaldavList("1", 1)
assertEquals("1", caldavDao.getTask(3)!!.calendar)
assertEquals(2, taskDao.fetch(3).getParent())
assertEquals(2, taskDao.fetch(3)!!.getParent())
}
@Test
@ -244,7 +244,7 @@ class TaskMoverTest : InjectingTestCase() {
googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1")))
dontSync(1)
assertNull(googleTaskDao.getByTaskId(1))
assertFalse(taskDao.fetch(1).isDeleted)
assertFalse(taskDao.fetch(1)!!.isDeleted)
}
@Test
@ -253,7 +253,7 @@ class TaskMoverTest : InjectingTestCase() {
caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1")))
dontSync(1)
assertNull(caldavDao.getTask(1))
assertFalse(taskDao.fetch(1).isDeleted)
assertFalse(taskDao.fetch(1)!!.isDeleted)
}
@Test
@ -263,10 +263,10 @@ class TaskMoverTest : InjectingTestCase() {
googleTaskDao.insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L)))
dontSync(1)
assertNull(googleTaskDao.getByTaskId(2))
val task = taskDao.fetch(2)
val task = taskDao.fetch(2)!!
assertFalse(task.isDeleted)
assertEquals(1, task.getParent())
assertEquals(taskDao.fetch(1).uuid, task.getParentUuid())
assertEquals(taskDao.fetch(1)!!.uuid, task.getParentUuid())
}
@Test
@ -289,10 +289,10 @@ class TaskMoverTest : InjectingTestCase() {
with(REMOTE_PARENT, "b"))))
dontSync(1)
assertNull(caldavDao.getTask(3))
val task = taskDao.fetch(3)
val task = taskDao.fetch(3)!!
assertFalse(task.isDeleted)
assertEquals(2, task.getParent())
assertEquals(taskDao.fetch(2).uuid, task.getParentUuid())
assertEquals(taskDao.fetch(2)!!.uuid, task.getParentUuid())
}
@Test

@ -34,7 +34,7 @@ class DeletionDaoTests : InjectingTestCase() {
var task = newTask(MakeItEasy.with(CREATION_TIME, DateTime().minusMinutes(1)))
taskDao.createNew(task)
deletionDao.markDeleted(listOf(task.getId()))
task = taskDao.fetch(task.getId())
task = taskDao.fetch(task.getId())!!
assertTrue(task.modificationDate > task.creationDate)
assertTrue(task.modificationDate < DateTimeUtils.currentTimeMillis())
}
@ -44,7 +44,7 @@ class DeletionDaoTests : InjectingTestCase() {
var task = newTask(MakeItEasy.with(CREATION_TIME, DateTime().minusMinutes(1)))
taskDao.createNew(task)
deletionDao.markDeleted(listOf(task.getId()))
task = taskDao.fetch(task.getId())
task = taskDao.fetch(task.getId())!!
assertTrue(task.deletionDate > task.creationDate)
assertTrue(task.deletionDate < DateTimeUtils.currentTimeMillis())
}

@ -105,9 +105,9 @@ class GoogleTaskDaoTests : InjectingTestCase() {
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false)
val two = getByRemoteId("2")
googleTaskDao.move(two, 0, 0)
assertEquals(0, googleTaskDao.getByRemoteId("2").order)
assertEquals(1, googleTaskDao.getByRemoteId("1").order)
assertEquals(2, googleTaskDao.getByRemoteId("3").order)
assertEquals(0, googleTaskDao.getByRemoteId("2")!!.order)
assertEquals(1, googleTaskDao.getByRemoteId("1")!!.order)
assertEquals(2, googleTaskDao.getByRemoteId("3")!!.order)
}
@Test
@ -117,9 +117,9 @@ class GoogleTaskDaoTests : InjectingTestCase() {
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false)
val one = getByRemoteId("1")
googleTaskDao.move(one, 0, 1)
assertEquals(0, googleTaskDao.getByRemoteId("2").order)
assertEquals(1, googleTaskDao.getByRemoteId("1").order)
assertEquals(2, googleTaskDao.getByRemoteId("3").order)
assertEquals(0, googleTaskDao.getByRemoteId("2")!!.order)
assertEquals(1, googleTaskDao.getByRemoteId("1")!!.order)
assertEquals(2, googleTaskDao.getByRemoteId("3")!!.order)
}
@Test
@ -129,9 +129,9 @@ class GoogleTaskDaoTests : InjectingTestCase() {
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false)
val three = getByRemoteId("3")
googleTaskDao.move(three, 0, 0)
assertEquals(0, googleTaskDao.getByRemoteId("3").order)
assertEquals(1, googleTaskDao.getByRemoteId("1").order)
assertEquals(2, googleTaskDao.getByRemoteId("2").order)
assertEquals(0, googleTaskDao.getByRemoteId("3")!!.order)
assertEquals(1, googleTaskDao.getByRemoteId("1")!!.order)
assertEquals(2, googleTaskDao.getByRemoteId("2")!!.order)
}
@Test
@ -141,9 +141,9 @@ class GoogleTaskDaoTests : InjectingTestCase() {
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false)
val one = getByRemoteId("1")
googleTaskDao.move(one, 0, 2)
assertEquals(0, googleTaskDao.getByRemoteId("2").order)
assertEquals(1, googleTaskDao.getByRemoteId("3").order)
assertEquals(2, googleTaskDao.getByRemoteId("1").order)
assertEquals(0, googleTaskDao.getByRemoteId("2")!!.order)
assertEquals(1, googleTaskDao.getByRemoteId("3")!!.order)
assertEquals(2, googleTaskDao.getByRemoteId("1")!!.order)
}
@Test
@ -169,7 +169,7 @@ class GoogleTaskDaoTests : InjectingTestCase() {
}
private fun getByRemoteId(remoteId: String): SubsetGoogleTask {
val googleTask = googleTaskDao.getByRemoteId(remoteId)
val googleTask = googleTaskDao.getByRemoteId(remoteId)!!
val result = SubsetGoogleTask()
result.gt_id = googleTask.id
result.gt_list_id = googleTask.listId

@ -1,343 +0,0 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.dao;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.transform;
import static com.todoroo.andlib.sql.SqlConstants.COUNT;
import static com.todoroo.andlib.utility.DateUtilities.now;
import static org.tasks.db.DbUtils.batch;
import androidx.paging.DataSource;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.RawQuery;
import androidx.room.Transaction;
import androidx.room.Update;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteDatabase;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Field;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.UUIDHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.tasks.BuildConfig;
import org.tasks.data.Place;
import org.tasks.data.SubtaskInfo;
import org.tasks.data.TaskContainer;
import org.tasks.data.TaskListQuery;
import org.tasks.jobs.WorkManager;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
@Dao
public abstract class TaskDao {
public static final String TRANS_SUPPRESS_REFRESH = "suppress-refresh";
private final Database database;
private WorkManager workManager;
public TaskDao(Database database) {
this.database = database;
}
public void initialize(WorkManager workManager) {
this.workManager = workManager;
}
public List<Task> needsRefresh() {
return needsRefresh(now());
}
@Query(
"SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
abstract List<Task> needsRefresh(long now);
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
public abstract Task fetch(long id);
@Query("SELECT * FROM tasks WHERE _id IN (:taskIds)")
public abstract List<Task> fetch(List<Long> taskIds);
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
public abstract int activeTimers();
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
public abstract List<Task> activeNotifications();
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
public abstract Task fetch(String remoteId);
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract List<Task> getActiveTasks();
@Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)")
abstract List<Task> getVisibleTasks();
@Query(
"SELECT * FROM tasks WHERE remoteId IN (:remoteIds) "
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0")
public abstract List<Task> getRecurringTasks(List<String> remoteIds);
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId")
public abstract void setCompletionDate(String remoteId, long completionDate);
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)")
public abstract void snooze(List<Long> taskIds, long millis);
@Query(
"SELECT tasks.* FROM tasks "
+ "LEFT JOIN google_tasks ON tasks._id = google_tasks.gt_task "
+ "WHERE gt_list_id IN (SELECT gtl_remote_id FROM google_task_lists WHERE gtl_account = :account)"
+ "AND (tasks.modified > google_tasks.gt_last_sync OR google_tasks.gt_remote_id = '' OR google_tasks.gt_deleted > 0) "
+ "ORDER BY CASE WHEN gt_parent = 0 THEN 0 ELSE 1 END, gt_order ASC")
public abstract List<Task> getGoogleTasksToPush(String account);
@Query(
"SELECT tasks.* FROM tasks "
+ "LEFT JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task "
+ "WHERE caldav_tasks.cd_calendar = :calendar "
+ "AND tasks.modified > caldav_tasks.cd_last_sync")
public abstract List<Task> getCaldavTasksToPush(String calendar);
@Query(
"SELECT * FROM TASKS "
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)")
public abstract List<Task> getTasksWithReminders();
// --- SQL clause generators
@Query("SELECT * FROM tasks")
public abstract List<Task> getAll();
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
public abstract List<String> getAllCalendarEvents();
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
public abstract int clearAllCalendarEvents();
@Query(
"SELECT calendarUri FROM tasks "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
public abstract List<String> getCompletedCalendarEvents();
@Query(
"UPDATE tasks SET calendarUri = '' "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
public abstract int clearCompletedCalendarEvents();
@Transaction
public List<TaskContainer> fetchTasks(QueryCallback callback) {
return fetchTasks(callback, getSubtaskInfo());
}
@Transaction
public List<TaskContainer> fetchTasks(QueryCallback callback, SubtaskInfo subtasks) {
long start = BuildConfig.DEBUG ? now() : 0;
List<String> queries = callback.getQueries(subtasks);
SupportSQLiteDatabase db = database.getOpenHelper().getWritableDatabase();
int last = queries.size() - 1;
for (int i = 0 ; i < last ; i++) {
db.execSQL(queries.get(i));
}
List<TaskContainer> result = fetchTasks(new SimpleSQLiteQuery(queries.get(last)));
Timber.v("%sms: %s", now() - start, Joiner.on(";").join(queries));
return result;
}
public List<TaskContainer> fetchTasks(Preferences preferences, Filter filter) {
return fetchTasks(subtasks -> TaskListQuery.getQuery(preferences, filter, subtasks));
}
@RawQuery
abstract List<TaskContainer> fetchTasks(SimpleSQLiteQuery query);
@RawQuery
abstract int count(SimpleSQLiteQuery query);
@Query(
"SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks,"
+ "EXISTS(SELECT 1 FROM google_tasks "
+ " INNER JOIN tasks ON gt_task = _id "
+ " WHERE deleted = 0 AND gt_parent > 0 AND gt_deleted = 0) AS hasGoogleSubtasks")
public abstract SubtaskInfo getSubtaskInfo();
@RawQuery(observedEntities = {Place.class})
public abstract DataSource.Factory<Integer, TaskContainer> getTaskFactory(
SimpleSQLiteQuery query);
public void touch(Long id) {
touch(ImmutableList.of(id));
}
public void touch(List<Long> ids) {
touchInternal(ids);
workManager.sync(false);
}
@Query("UPDATE tasks SET modified = strftime('%s','now')*1000 WHERE _id in (:ids)")
abstract void touchInternal(List<Long> ids);
@Query(
"UPDATE tasks SET parent = IFNULL(("
+ " SELECT parent._id FROM tasks AS parent"
+ " WHERE parent.remoteId = tasks.parent_uuid AND parent.deleted = 0), 0)"
+ "WHERE parent_uuid IS NOT NULL AND parent_uuid != ''")
public abstract void updateParents();
@Query(
"UPDATE tasks SET parent_uuid = "
+ " (SELECT parent.remoteId FROM tasks AS parent WHERE parent._id = tasks.parent)"
+ " WHERE parent > 0 AND _id IN (:tasks)")
public abstract void updateParentUids(List<Long> tasks);
@Query("UPDATE tasks SET parent = :parent, parent_uuid = :parentUuid WHERE _id IN (:children)")
public abstract void setParent(long parent, String parentUuid, List<Long> children);
@Transaction
public List<Task> fetchChildren(long id) {
return fetch(getChildren(id));
}
public List<Long> getChildren(long id) {
return getChildren(Collections.singletonList(id));
}
@Query(
"WITH RECURSIVE "
+ " recursive_tasks (task) AS ( "
+ " SELECT _id "
+ " FROM tasks "
+ "WHERE parent IN (:ids)"
+ "UNION ALL "
+ " SELECT _id "
+ " FROM tasks "
+ " INNER JOIN recursive_tasks "
+ " ON recursive_tasks.task = tasks.parent"
+ " WHERE tasks.deleted = 0)"
+ "SELECT task FROM recursive_tasks")
public abstract List<Long> getChildren(List<Long> ids);
public List<Long> findChildrenInList(List<Long> ids) {
List<Long> result = new ArrayList<>(ids);
result.retainAll(getChildren(ids));
return result;
}
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
public abstract void setCollapsed(long id, boolean collapsed);
@Transaction
public void setCollapsed(List<TaskContainer> tasks, boolean collapsed) {
batch(
transform(filter(tasks, TaskContainer::hasChildren), TaskContainer::getId),
l -> collapse(l, collapsed));
}
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)")
abstract void collapse(List<Long> ids, boolean collapsed);
/**
* Saves the given task to the database.getDatabase(). Task must already exist. Returns true on
* success.
*/
public void save(Task task) {
save(task, fetch(task.getId()));
}
// --- save
// TODO: get rid of this super-hack
public void save(Task task, Task original) {
if (!task.insignificantChange(original)) {
task.setModificationDate(now());
}
if (update(task) == 1) {
workManager.afterSave(task, original);
}
}
@Insert
abstract long insert(Task task);
@Update
abstract int update(Task task);
public void createNew(Task task) {
task.id = null;
if (task.created == 0) {
task.created = now();
}
if (Task.isUuidEmpty(task.remoteId)) {
task.remoteId = UUIDHelper.newUUID();
}
long insert = insert(task);
task.setId(insert);
}
@Query(
"SELECT * FROM tasks "
+ "WHERE completed = 0 AND deleted = 0 AND hideUntil < (strftime('%s','now')*1000) "
+ "ORDER BY (CASE WHEN (dueDate=0) THEN (strftime('%s','now')*1000)*2 ELSE ((CASE WHEN (dueDate / 1000) % 60 > 0 THEN dueDate ELSE (dueDate + 43140000) END)) END) + 172800000 * importance ASC "
+ "LIMIT 100")
public abstract List<Task> getAstrid2TaskProviderTasks();
public int count(Filter filter) {
SimpleSQLiteQuery query = getQuery(filter.sqlQuery, COUNT);
long start = BuildConfig.DEBUG ? now() : 0;
int count = count(query);
Timber.v("%sms: %s", now() - start, query.getSql());
return count;
}
public List<Task> fetchFiltered(Filter filter) {
return fetchFiltered(filter.getSqlQuery());
}
public List<Task> fetchFiltered(String queryTemplate) {
SimpleSQLiteQuery query = getQuery(queryTemplate, Task.FIELDS);
long start = BuildConfig.DEBUG ? now() : 0;
List<TaskContainer> tasks = fetchTasks(query);
Timber.v("%sms: %s", now() - start, query.getSql());
return transform(tasks, TaskContainer::getTask);
}
private static SimpleSQLiteQuery getQuery(String queryTemplate, Field... fields) {
return new SimpleSQLiteQuery(
com.todoroo.andlib.sql.Query.select(fields)
.withQueryTemplate(PermaSql.replacePlaceholdersForQuery(queryTemplate))
.from(Task.TABLE)
.toString());
}
/** Generates SQL clauses */
public static class TaskCriteria {
/** @return tasks that have not yet been completed or deleted */
public static Criterion activeAndVisible() {
return Criterion.and(
Task.COMPLETION_DATE.lte(0),
Task.DELETION_DATE.lte(0),
Task.HIDE_UNTIL.lte(Functions.now()));
}
}
public interface QueryCallback {
List<String> getQueries(SubtaskInfo subtasks);
}
}

@ -0,0 +1,302 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.dao
import androidx.paging.DataSource
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Field
import com.todoroo.andlib.sql.Functions
import com.todoroo.andlib.sql.SqlConstants
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.BuildConfig
import org.tasks.data.Place
import org.tasks.data.SubtaskInfo
import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery
import org.tasks.db.DbUtils
import org.tasks.jobs.WorkManager
import org.tasks.preferences.Preferences
import timber.log.Timber
import java.util.*
@Dao
abstract class TaskDao(private val database: Database) {
private lateinit var workManager: WorkManager
fun initialize(workManager: WorkManager) {
this.workManager = workManager
}
fun needsRefresh(): List<Task> {
return needsRefresh(DateUtilities.now())
}
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
abstract fun needsRefresh(now: Long): List<Task>
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
abstract fun fetch(id: Long): Task?
@Query("SELECT * FROM tasks WHERE _id IN (:taskIds)")
abstract fun fetch(taskIds: List<Long>): List<Task>
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
abstract fun activeTimers(): Int
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
abstract fun activeNotifications(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
abstract fun fetch(remoteId: String): Task?
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract fun getActiveTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)")
abstract fun getVisibleTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) "
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0")
abstract fun getRecurringTasks(remoteIds: List<String>): List<Task>
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId")
abstract fun setCompletionDate(remoteId: String, completionDate: Long)
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)")
abstract fun snooze(taskIds: List<Long>, millis: Long)
@Query("SELECT tasks.* FROM tasks "
+ "LEFT JOIN google_tasks ON tasks._id = google_tasks.gt_task "
+ "WHERE gt_list_id IN (SELECT gtl_remote_id FROM google_task_lists WHERE gtl_account = :account)"
+ "AND (tasks.modified > google_tasks.gt_last_sync OR google_tasks.gt_remote_id = '' OR google_tasks.gt_deleted > 0) "
+ "ORDER BY CASE WHEN gt_parent = 0 THEN 0 ELSE 1 END, gt_order ASC")
abstract fun getGoogleTasksToPush(account: String): List<Task>
@Query("SELECT tasks.* FROM tasks "
+ "LEFT JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task "
+ "WHERE caldav_tasks.cd_calendar = :calendar "
+ "AND tasks.modified > caldav_tasks.cd_last_sync")
abstract fun getCaldavTasksToPush(calendar: String): List<Task>
@Query("SELECT * FROM TASKS "
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)")
abstract fun getTasksWithReminders(): List<Task>
// --- SQL clause generators
@Query("SELECT * FROM tasks")
abstract fun getAll(): List<Task>
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun getAllCalendarEvents(): List<String>
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun clearAllCalendarEvents(): Int
@Query("SELECT calendarUri FROM tasks "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun getCompletedCalendarEvents(): List<String>
@Query("UPDATE tasks SET calendarUri = '' "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun clearCompletedCalendarEvents(): Int
@Transaction
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> {
return fetchTasks(callback, getSubtaskInfo())
}
@Transaction
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> {
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val queries = callback.invoke(subtasks)
val db = database.openHelper.writableDatabase
val last = queries.size - 1
for (i in 0 until last) {
db.execSQL(queries[i])
}
val result = fetchTasks(SimpleSQLiteQuery(queries[last]))
Timber.v("%sms: %s", DateUtilities.now() - start, queries.joinToString(";"))
return result
}
fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> {
return fetchTasks {
TaskListQuery.getQuery(preferences, filter, it)
}
}
@RawQuery
abstract fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer>
@RawQuery
abstract fun count(query: SimpleSQLiteQuery): Int
@Query("SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks,"
+ "EXISTS(SELECT 1 FROM google_tasks "
+ " INNER JOIN tasks ON gt_task = _id "
+ " WHERE deleted = 0 AND gt_parent > 0 AND gt_deleted = 0) AS hasGoogleSubtasks")
abstract fun getSubtaskInfo(): SubtaskInfo
@RawQuery(observedEntities = [Place::class])
abstract fun getTaskFactory(
query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer>
fun touch(id: Long) {
touch(listOf(id))
}
fun touch(ids: List<Long>) {
touchInternal(ids)
workManager.sync(false)
}
@Query("UPDATE tasks SET modified = strftime('%s','now')*1000 WHERE _id in (:ids)")
abstract fun touchInternal(ids: List<Long>)
@Query("UPDATE tasks SET parent = IFNULL(("
+ " SELECT parent._id FROM tasks AS parent"
+ " WHERE parent.remoteId = tasks.parent_uuid AND parent.deleted = 0), 0)"
+ "WHERE parent_uuid IS NOT NULL AND parent_uuid != ''")
abstract fun updateParents()
@Query("UPDATE tasks SET parent_uuid = "
+ " (SELECT parent.remoteId FROM tasks AS parent WHERE parent._id = tasks.parent)"
+ " WHERE parent > 0 AND _id IN (:tasks)")
abstract fun updateParentUids(tasks: List<Long>)
@Query("UPDATE tasks SET parent = :parent, parent_uuid = :parentUuid WHERE _id IN (:children)")
abstract fun setParent(parent: Long, parentUuid: String, children: List<Long>)
@Transaction
open fun fetchChildren(id: Long): List<Task> {
return fetch(getChildren(id))
}
fun getChildren(id: Long): List<Long> {
return getChildren(listOf(id))
}
@Query("WITH RECURSIVE "
+ " recursive_tasks (task) AS ( "
+ " SELECT _id "
+ " FROM tasks "
+ "WHERE parent IN (:ids)"
+ "UNION ALL "
+ " SELECT _id "
+ " FROM tasks "
+ " INNER JOIN recursive_tasks "
+ " ON recursive_tasks.task = tasks.parent"
+ " WHERE tasks.deleted = 0)"
+ "SELECT task FROM recursive_tasks")
abstract fun getChildren(ids: List<Long>): List<Long>
fun findChildrenInList(ids: List<Long>): List<Long> {
val result = ArrayList(ids)
result.retainAll(getChildren(ids))
return result
}
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean)
@Transaction
open fun setCollapsed(tasks: List<TaskContainer>, collapsed: Boolean) {
DbUtils.batch(tasks.filter(TaskContainer::hasChildren).map(TaskContainer::getId)) {
collapse(it, collapsed)
}
}
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)")
abstract fun collapse(ids: List<Long>, collapsed: Boolean)
// --- save
// TODO: get rid of this super-hack
/**
* Saves the given task to the database.getDatabase(). Task must already exist. Returns true on
* success.
*/
@JvmOverloads
fun save(task: Task, original: Task? = fetch(task.getId())) {
if (!task.insignificantChange(original)) {
task.modificationDate = DateUtilities.now()
}
if (update(task) == 1) {
workManager.afterSave(task, original)
}
}
@Insert
abstract fun insert(task: Task): Long
@Update
abstract fun update(task: Task): Int
fun createNew(task: Task) {
task.id = null
if (task.created == 0L) {
task.created = DateUtilities.now()
}
if (Task.isUuidEmpty(task.remoteId)) {
task.remoteId = UUIDHelper.newUUID()
}
val insert = insert(task)
task.setId(insert)
}
@Query("SELECT * FROM tasks "
+ "WHERE completed = 0 AND deleted = 0 AND hideUntil < (strftime('%s','now')*1000) "
+ "ORDER BY (CASE WHEN (dueDate=0) THEN (strftime('%s','now')*1000)*2 ELSE ((CASE WHEN (dueDate / 1000) % 60 > 0 THEN dueDate ELSE (dueDate + 43140000) END)) END) + 172800000 * importance ASC "
+ "LIMIT 100")
abstract fun getAstrid2TaskProviderTasks(): List<Task>
fun count(filter: Filter): Int {
val query = getQuery(filter.sqlQuery, SqlConstants.COUNT)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val count = count(query)
Timber.v("%sms: %s", DateUtilities.now() - start, query.sql)
return count
}
fun fetchFiltered(filter: Filter): List<Task> {
return fetchFiltered(filter.getSqlQuery())
}
fun fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val tasks = fetchTasks(query)
Timber.v("%sms: %s", DateUtilities.now() - start, query.sql)
return tasks.map(TaskContainer::getTask)
}
/** Generates SQL clauses */
object TaskCriteria {
/** @return tasks that have not yet been completed or deleted
*/
@JvmStatic fun activeAndVisible(): Criterion {
return Criterion.and(
Task.COMPLETION_DATE.lte(0),
Task.DELETION_DATE.lte(0),
Task.HIDE_UNTIL.lte(Functions.now()))
}
}
companion object {
const val TRANS_SUPPRESS_REFRESH = "suppress-refresh"
private fun getQuery(queryTemplate: String, vararg fields: Field): SimpleSQLiteQuery {
return SimpleSQLiteQuery(
com.todoroo.andlib.sql.Query.select(*fields)
.withQueryTemplate(PermaSql.replacePlaceholdersForQuery(queryTemplate))
.from(Task.TABLE)
.toString())
}
}
}

@ -105,7 +105,7 @@ class iCalendar @Inject constructor(
if (categories.isEmpty()) {
return emptyList()
}
val tags = tagDataDao.getTags(categories)
val tags = tagDataDao.getTags(categories).toMutableList()
val existing = Lists.transform(tags) { obj: TagData? -> obj!!.name }
val toCreate = difference(newHashSet(categories), newHashSet(existing))
for (name in toCreate) {
@ -153,7 +153,7 @@ class iCalendar @Inject constructor(
taskDao.createNew(task)
caldavTask = CaldavTask(task.getId(), calendar.uuid, remote.uid, obj)
} else {
task = taskDao.fetch(existing.task)
task = taskDao.fetch(existing.task)!!
caldavTask = existing
}
CaldavConverter.apply(task, remote)

@ -1,205 +0,0 @@
package org.tasks.data;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.RoomWarnings;
import androidx.room.Transaction;
import androidx.room.Update;
import com.todoroo.astrid.data.Task;
import java.util.List;
import timber.log.Timber;
@Dao
public abstract class GoogleTaskDao {
@Insert
public abstract void insert(GoogleTask task);
@Insert
public abstract void insert(Iterable<GoogleTask> tasks);
@Transaction
public void insertAndShift(GoogleTask task, boolean top) {
if (top) {
task.setOrder(0);
shiftDown(task.getListId(), task.getParent(), 0);
} else {
task.setOrder(getBottom(task.getListId(), task.getParent()));
}
insert(task);
}
@Query(
"UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position")
abstract void shiftDown(String listId, long parent, long position);
@Query(
"UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order > :from AND gt_order <= :to")
abstract void shiftUp(String listId, long parent, long from, long to);
@Query(
"UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order < :from AND gt_order >= :to")
abstract void shiftDown(String listId, long parent, long from, long to);
@Query(
"UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position")
abstract void shiftUp(String listId, long parent, long position);
@Transaction
public void move(SubsetGoogleTask task, long newParent, long newPosition) {
long previousParent = task.getParent();
long previousPosition = task.getOrder();
if (newParent == previousParent) {
if (previousPosition < newPosition) {
shiftUp(task.getListId(), newParent, previousPosition, newPosition);
} else {
shiftDown(task.getListId(), newParent, previousPosition, newPosition);
}
} else {
shiftUp(task.getListId(), previousParent, previousPosition);
shiftDown(task.getListId(), newParent, newPosition);
}
task.setParent(newParent);
task.setOrder(newPosition);
update(task);
}
@Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id")
public abstract void setCollapsed(long id, boolean collapsed);
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1")
public abstract GoogleTask getByTaskId(long taskId);
@Update
public abstract void update(GoogleTask googleTask);
private void update(SubsetGoogleTask googleTask) {
update(googleTask.getId(), googleTask.getParent(), googleTask.getOrder());
}
@Query("UPDATE google_tasks SET gt_order = :order, gt_parent = :parent, gt_moved = 1 WHERE gt_id = :id")
abstract void update(long id, long parent, long order);
@Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task")
public abstract void markDeleted(long now, long task);
@Delete
public abstract void delete(GoogleTask deleted);
@Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1")
public abstract GoogleTask getByRemoteId(String remoteId);
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0")
public abstract List<GoogleTask> getDeletedByTaskId(long taskId);
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId")
public abstract List<GoogleTask> getAllByTaskId(long taskId);
@Query("SELECT DISTINCT gt_list_id FROM google_tasks WHERE gt_deleted = 0 AND gt_task IN (:tasks)")
public abstract List<String> getLists(List<Long> tasks);
@Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids)")
public abstract List<Long> getChildren(List<Long> ids);
@Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId")
public abstract List<Task> getChildTasks(long taskId);
@Query("SELECT gt_task FROM google_tasks WHERE gt_task IN (:taskIds) AND gt_parent IN (:taskIds) AND gt_deleted = 0")
public abstract List<Long> findChildrenInList(List<Long> taskIds);
@Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0")
public abstract List<GoogleTask> getChildren(Long id);
@Query(
"SELECT IFNULL(MAX(gt_order), -1) + 1 FROM google_tasks WHERE gt_list_id = :listId AND gt_parent = :parent")
public abstract long getBottom(String listId, long parent);
@Query(
"SELECT gt_remote_id FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE deleted = 0 AND gt_list_id = :listId AND gt_parent = :parent AND gt_order < :order AND gt_remote_id IS NOT NULL AND gt_remote_id != '' ORDER BY gt_order DESC")
public abstract String getPrevious(String listId, long parent, long order);
@Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task")
public abstract String getRemoteId(long task);
@Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId")
public abstract long getTask(String remoteId);
@SuppressWarnings({RoomWarnings.CURSOR_MISMATCH})
@Query(
"SELECT google_tasks.*, gt_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_order AS primary_sort, c.gt_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC")
abstract List<GoogleTask> getByLocalOrder(String listId);
@SuppressWarnings({RoomWarnings.CURSOR_MISMATCH})
@Query(
"SELECT google_tasks.*, gt_remote_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_remote_order AS primary_sort, c.gt_remote_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC")
abstract List<GoogleTask> getByRemoteOrder(String listId);
@Query(
"UPDATE google_tasks"
+ " SET gt_parent = IFNULL(("
+ " SELECT gt_task FROM google_tasks AS p"
+ " WHERE p.gt_remote_id = google_tasks.gt_remote_parent"
+ " AND p.gt_list_id = google_tasks.gt_list_id "
+ " AND p.gt_deleted = 0),"
+ " 0)"
+ " WHERE gt_moved = 0")
public abstract void updateParents();
@Query(
"UPDATE google_tasks SET gt_parent = IFNULL((SELECT gt_task FROM google_tasks AS p WHERE p.gt_remote_id = google_tasks.gt_remote_parent), 0) WHERE gt_list_id = :listId AND gt_moved = 0")
abstract void updateParents(String listId);
@Query(
"UPDATE google_tasks SET gt_remote_parent = :parent, gt_remote_order = :position WHERE gt_remote_id = :id")
public abstract void updatePosition(String id, String parent, String position);
@Transaction
public void reposition(String listId) {
updateParents(listId);
List<GoogleTask> orderedTasks = getByRemoteOrder(listId);
int subtasks = 0;
int parent = 0;
for (int i = 0; i < orderedTasks.size(); i++) {
GoogleTask task = orderedTasks.get(i);
if (task.getParent() > 0) {
if (task.getOrder() != subtasks && !task.isMoved()) {
task.setOrder(subtasks);
update(task);
}
subtasks++;
} else {
subtasks = 0;
if (task.getOrder() != parent && !task.isMoved()) {
task.setOrder(parent);
update(task);
}
parent++;
}
}
}
public void validateSorting(String listId) {
List<GoogleTask> orderedTasks = getByLocalOrder(listId);
int subtasks = 0;
int parent = 0;
for (int i = 0; i < orderedTasks.size(); i++) {
GoogleTask task = orderedTasks.get(i);
if (task.getParent() > 0) {
if (task.getOrder() != subtasks) {
Timber.e("Subtask violation expected %s but was %s", subtasks, task.getOrder());
}
subtasks++;
} else {
subtasks = 0;
if (task.getOrder() != parent) {
Timber.e("Parent violation expected %s but was %s", parent, task.getOrder());
}
parent++;
}
}
}
}

@ -0,0 +1,182 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task
import timber.log.Timber
@Dao
abstract class GoogleTaskDao {
@Insert
abstract fun insert(task: GoogleTask)
@Insert
abstract fun insert(tasks: Iterable<GoogleTask>)
@Transaction
open fun insertAndShift(task: GoogleTask, top: Boolean) {
if (top) {
task.order = 0
shiftDown(task.listId!!, task.parent, 0)
} else {
task.order = getBottom(task.listId!!, task.parent)
}
insert(task)
}
@Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position")
abstract fun shiftDown(listId: String, parent: Long, position: Long)
@Query("UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order > :from AND gt_order <= :to")
abstract fun shiftUp(listId: String, parent: Long, from: Long, to: Long)
@Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order < :from AND gt_order >= :to")
abstract fun shiftDown(listId: String, parent: Long, from: Long, to: Long)
@Query("UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position")
abstract fun shiftUp(listId: String, parent: Long, position: Long)
@Transaction
open fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) {
val previousParent = task.parent
val previousPosition = task.order
if (newParent == previousParent) {
if (previousPosition < newPosition) {
shiftUp(task.listId, newParent, previousPosition, newPosition)
} else {
shiftDown(task.listId, newParent, previousPosition, newPosition)
}
} else {
shiftUp(task.listId, previousParent, previousPosition)
shiftDown(task.listId, newParent, newPosition)
}
task.parent = newParent
task.order = newPosition
update(task)
}
@Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean)
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1")
abstract fun getByTaskId(taskId: Long): GoogleTask?
@Update
abstract fun update(googleTask: GoogleTask)
private fun update(googleTask: SubsetGoogleTask) {
update(googleTask.id, googleTask.parent, googleTask.order)
}
@Query("UPDATE google_tasks SET gt_order = :order, gt_parent = :parent, gt_moved = 1 WHERE gt_id = :id")
abstract fun update(id: Long, parent: Long, order: Long)
@Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task")
abstract fun markDeleted(now: Long, task: Long)
@Delete
abstract fun delete(deleted: GoogleTask)
@Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1")
abstract fun getByRemoteId(remoteId: String): GoogleTask?
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0")
abstract fun getDeletedByTaskId(taskId: Long): List<GoogleTask>
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId")
abstract fun getAllByTaskId(taskId: Long): List<GoogleTask>
@Query("SELECT DISTINCT gt_list_id FROM google_tasks WHERE gt_deleted = 0 AND gt_task IN (:tasks)")
abstract fun getLists(tasks: List<Long>): List<String>
@Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids)")
abstract fun getChildren(ids: List<Long>): List<Long>
@Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId")
abstract fun getChildTasks(taskId: Long): List<Task>
@Query("SELECT gt_task FROM google_tasks WHERE gt_task IN (:taskIds) AND gt_parent IN (:taskIds) AND gt_deleted = 0")
abstract fun findChildrenInList(taskIds: List<Long>): List<Long>
@Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0")
abstract fun getChildren(id: Long): List<GoogleTask>
@Query("SELECT IFNULL(MAX(gt_order), -1) + 1 FROM google_tasks WHERE gt_list_id = :listId AND gt_parent = :parent")
abstract fun getBottom(listId: String, parent: Long): Long
@Query("SELECT gt_remote_id FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE deleted = 0 AND gt_list_id = :listId AND gt_parent = :parent AND gt_order < :order AND gt_remote_id IS NOT NULL AND gt_remote_id != '' ORDER BY gt_order DESC")
abstract fun getPrevious(listId: String, parent: Long, order: Long): String?
@Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task")
abstract fun getRemoteId(task: Long): String?
@Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId")
abstract fun getTask(remoteId: String): Long
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT google_tasks.*, gt_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_order AS primary_sort, c.gt_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC")
abstract fun getByLocalOrder(listId: String): List<GoogleTask>
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT google_tasks.*, gt_remote_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_remote_order AS primary_sort, c.gt_remote_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC")
abstract fun getByRemoteOrder(listId: String): List<GoogleTask>
@Query("UPDATE google_tasks"
+ " SET gt_parent = IFNULL(("
+ " SELECT gt_task FROM google_tasks AS p"
+ " WHERE p.gt_remote_id = google_tasks.gt_remote_parent"
+ " AND p.gt_list_id = google_tasks.gt_list_id "
+ " AND p.gt_deleted = 0),"
+ " 0)"
+ " WHERE gt_moved = 0")
abstract fun updateParents()
@Query("UPDATE google_tasks SET gt_parent = IFNULL((SELECT gt_task FROM google_tasks AS p WHERE p.gt_remote_id = google_tasks.gt_remote_parent), 0) WHERE gt_list_id = :listId AND gt_moved = 0")
abstract fun updateParents(listId: String)
@Query("UPDATE google_tasks SET gt_remote_parent = :parent, gt_remote_order = :position WHERE gt_remote_id = :id")
abstract fun updatePosition(id: String, parent: String, position: String)
@Transaction
open fun reposition(listId: String) {
updateParents(listId)
val orderedTasks = getByRemoteOrder(listId)
var subtasks = 0L
var parent = 0L
for (task in orderedTasks) {
if (task.parent > 0) {
if (task.order != subtasks && !task.isMoved) {
task.order = subtasks
update(task)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent && !task.isMoved) {
task.order = parent
update(task)
}
parent++
}
}
}
fun validateSorting(listId: String) {
val orderedTasks = getByLocalOrder(listId)
var subtasks = 0L
var parent = 0L
for (task in orderedTasks) {
if (task.parent > 0) {
if (task.order != subtasks) {
Timber.e("Subtask violation expected %s but was %s", subtasks, task.order)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
Timber.e("Parent violation expected %s but was %s", parent, task.order)
}
parent++
}
}
}
}

@ -1,65 +0,0 @@
package org.tasks.data;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import com.todoroo.astrid.data.Task;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@Dao
public abstract class TagDao {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
public abstract void rename(String tagUid, String name);
@Insert
public abstract void insert(Tag tag);
@Insert
public abstract void insert(Iterable<Tag> tags);
@Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)")
public abstract void deleteTags(long taskId, List<String> tagUids);
@Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC")
public abstract List<String> getTagNames(long taskId);
@Query("SELECT * FROM tags WHERE tag_uid = :tagUid")
public abstract List<Tag> getByTagUid(String tagUid);
@Query("SELECT * FROM tags WHERE task = :taskId")
public abstract List<Tag> getTagsForTask(long taskId);
@Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid")
public abstract Tag getTagByTaskAndTagUid(long taskId, String tagUid);
@Delete
public abstract void delete(List<Tag> tags);
@Transaction
public boolean applyTags(Task task, TagDataDao tagDataDao,List<TagData> current) {
long taskId = task.getId();
Set<TagData> existing = newHashSet(tagDataDao.getTagDataForTask(taskId));
Set<TagData> selected = newHashSet(current);
Set<TagData> added = difference(selected, existing);
Set<TagData> removed = difference(existing, selected);
deleteTags(taskId, newArrayList(transform(removed, TagData::getRemoteId)));
insert(task, added);
return !(removed.isEmpty() && added.isEmpty());
}
public void insert(Task task, Collection<TagData> tags) {
if (!tags.isEmpty()) {
insert(transform(tags, td -> new Tag(task, td)));
}
}
}

@ -0,0 +1,52 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task
@Dao
abstract class TagDao {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
abstract fun rename(tagUid: String, name: String)
@Insert
abstract fun insert(tag: Tag)
@Insert
abstract fun insert(tags: Iterable<Tag>)
@Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)")
abstract fun deleteTags(taskId: Long, tagUids: List<String>)
@Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC")
abstract fun getTagNames(taskId: Long): List<String>
@Query("SELECT * FROM tags WHERE tag_uid = :tagUid")
abstract fun getByTagUid(tagUid: String): List<Tag>
@Query("SELECT * FROM tags WHERE task = :taskId")
abstract fun getTagsForTask(taskId: Long): List<Tag>
@Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid")
abstract fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag?
@Delete
abstract fun delete(tags: List<Tag>)
@Transaction
open fun applyTags(task: Task, tagDataDao: TagDataDao, current: List<TagData>): Boolean {
val taskId = task.getId()
val existing = HashSet(tagDataDao.getTagDataForTask(taskId))
val selected = HashSet<TagData>(current)
val added = selected subtract existing
val removed = existing subtract selected
deleteTags(taskId, removed.map { td -> td.remoteId!! })
insert(task, added)
return removed.isNotEmpty() || added.isNotEmpty()
}
fun insert(task: Task, tags: Collection<TagData>) {
if (!tags.isEmpty()) {
insert(tags.map { Tag(task, it) })
}
}
}

@ -1,175 +0,0 @@
package org.tasks.data;
import static com.google.common.collect.FluentIterable.concat;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.Collections.emptySet;
import static org.tasks.db.DbUtils.MAX_SQLITE_ARGS;
import androidx.core.util.Pair;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.Update;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.UUIDHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.tasks.filters.AlphanumComparator;
import org.tasks.filters.TagFilters;
@Dao
public abstract class TagDataDao {
@Query("SELECT * FROM tagdata")
public abstract LiveData<List<TagData>> subscribeToTags();
@Query("SELECT * FROM tagdata WHERE name = :name COLLATE NOCASE LIMIT 1")
public abstract TagData getTagByName(String name);
/**
* If a tag already exists in the database that case insensitively matches the given tag, return
* that. Otherwise, return the argument
*/
public String getTagWithCase(String tag) {
TagData tagData = getTagByName(tag);
return tagData != null ? tagData.getName() : tag;
}
public List<TagData> searchTags(String query) {
List<TagData> results = searchTagsInternal("%" + query + "%");
Collections.sort(results, new AlphanumComparator<>(TagData::getName));
return results;
}
@Query("SELECT * FROM tagdata WHERE name LIKE :query AND name NOT NULL AND name != ''")
protected abstract List<TagData> searchTagsInternal(String query);
@Query("SELECT * FROM tagdata")
public abstract List<TagData> getAll();
@Query("SELECT * FROM tagdata WHERE remoteId = :uuid LIMIT 1")
public abstract TagData getByUuid(String uuid);
@Query("SELECT * FROM tagdata WHERE remoteId IN (:uuids)")
public abstract List<TagData> getByUuid(Collection<String> uuids);
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
public abstract List<TagData> tagDataOrderedByName();
@Delete
abstract void deleteTagData(TagData tagData);
@Query("DELETE FROM tags WHERE tag_uid = :tagUid")
abstract void deleteTags(String tagUid);
@Query("SELECT * FROM tags WHERE task IN (:tasks) AND tag_uid NOT IN (:tagsToKeep)")
abstract List<Tag> tagsToDelete(List<Long> tasks, List<String> tagsToKeep);
public Pair<Set<String>, Set<String>> getTagSelections(List<Long> tasks) {
List<String> allTags = getAllTags(tasks);
Collection<Set<String>> tags =
Collections2.transform(allTags, t -> t == null ? emptySet() : newHashSet(t.split(",")));
Set<String> partialTags = newHashSet(concat(tags));
Set<String> commonTags = null;
if (tags.isEmpty()) {
commonTags = emptySet();
} else {
for (Set<String> s : tags) {
if (commonTags == null) {
commonTags = s;
} else {
commonTags.retainAll(s);
}
}
}
partialTags.removeAll(commonTags);
return Pair.create(partialTags, commonTags);
}
@Query(
"SELECT GROUP_CONCAT(DISTINCT(tag_uid)) FROM tasks"
+ " LEFT JOIN tags ON tags.task = tasks._id"
+ " WHERE tasks._id IN (:tasks)"
+ " GROUP BY tasks._id")
abstract List<String> getAllTags(List<Long> tasks);
@Transaction
public List<Long> applyTags(
List<Task> tasks, List<TagData> partiallySelected, List<TagData> selected) {
Set<Long> modified = newHashSet();
List<String> keep =
concat(partiallySelected, selected).transform(TagData::getRemoteId).toList();
for (List<Task> sublist : Iterables.partition(tasks, MAX_SQLITE_ARGS - keep.size())) {
List<Tag> tags = tagsToDelete(from(sublist).transform(Task::getId).toList(), keep);
deleteTags(tags);
modified.addAll(transform(tags, Tag::getTask));
}
for (Task task : tasks) {
Set<TagData> added =
difference(newHashSet(selected), newHashSet(getTagDataForTask(task.getId())));
if (added.size() > 0) {
modified.add(task.getId());
insert(transform(added, td -> new Tag(task, td)));
}
}
return new ArrayList<>(modified);
}
@Transaction
public void delete(TagData tagData) {
deleteTags(tagData.getRemoteId());
deleteTagData(tagData);
}
@Delete
public abstract void delete(List<TagData> tagData);
@Delete
abstract void deleteTags(List<Tag> tags);
@Query("SELECT tagdata.* FROM tagdata "
+ "INNER JOIN tags ON tags.tag_uid = tagdata.remoteId "
+ "WHERE tags.task = :id "
+ "ORDER BY UPPER(tagdata.name) ASC")
public abstract List<TagData> getTagDataForTask(long id);
@Query("SELECT * FROM tagdata WHERE name IN (:names)")
public abstract List<TagData> getTags(List<String> names);
@Update
public abstract void update(TagData tagData);
@Insert
abstract long insert(TagData tag);
@Insert
public abstract void insert(Iterable<Tag> tags);
public void createNew(TagData tag) {
if (Task.isUuidEmpty(tag.getRemoteId())) {
tag.setRemoteId(UUIDHelper.newUUID());
}
tag.setId(insert(tag));
}
@Query(
"SELECT tagdata.*, COUNT(tasks._id) AS count"
+ " FROM tagdata"
+ " LEFT JOIN tags ON tags.tag_uid = tagdata.remoteId"
+ " 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")
public abstract List<TagFilters> getTagFilters(long now);
}

@ -0,0 +1,151 @@
package org.tasks.data
import androidx.core.util.Pair
import androidx.lifecycle.LiveData
import androidx.room.*
import com.google.common.collect.Iterables
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.db.DbUtils
import org.tasks.filters.AlphanumComparator
import org.tasks.filters.TagFilters
import java.util.*
import kotlin.collections.HashSet
@Dao
abstract class TagDataDao {
@Query("SELECT * FROM tagdata")
abstract fun subscribeToTags(): LiveData<List<TagData>>
@Query("SELECT * FROM tagdata WHERE name = :name COLLATE NOCASE LIMIT 1")
abstract fun getTagByName(name: String): TagData?
/**
* If a tag already exists in the database that case insensitively matches the given tag, return
* that. Otherwise, return the argument
*/
fun getTagWithCase(tag: String): String? {
return getTagByName(tag)?.name ?: tag
}
fun searchTags(query: String): List<TagData> {
return searchTagsInternal("%$query%")
.sortedWith(AlphanumComparator(TagData::name))
.toMutableList()
}
@Query("SELECT * FROM tagdata WHERE name LIKE :query AND name NOT NULL AND name != ''")
protected abstract fun searchTagsInternal(query: String): List<TagData>
@Query("SELECT * FROM tagdata")
abstract fun getAll(): List<TagData>
@Query("SELECT * FROM tagdata WHERE remoteId = :uuid LIMIT 1")
abstract fun getByUuid(uuid: String): TagData?
@Query("SELECT * FROM tagdata WHERE remoteId IN (:uuids)")
abstract fun getByUuid(uuids: Collection<String>): List<TagData>
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
abstract fun tagDataOrderedByName(): List<TagData>
@Delete
abstract fun deleteTagData(tagData: TagData)
@Query("DELETE FROM tags WHERE tag_uid = :tagUid")
abstract fun deleteTags(tagUid: String)
@Query("SELECT * FROM tags WHERE task IN (:tasks) AND tag_uid NOT IN (:tagsToKeep)")
abstract fun tagsToDelete(tasks: List<Long>, tagsToKeep: List<String>): List<Tag>
fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> {
val allTags = getAllTags(tasks)
val tags = allTags.map { t: String? -> HashSet<String>(t?.split(",") ?: emptySet()) }
val partialTags = tags.flatten().toMutableSet()
var commonTags: MutableSet<String>? = null
if (tags.isEmpty()) {
commonTags = HashSet()
} else {
for (s in tags) {
if (commonTags == null) {
commonTags = s.toMutableSet()
} else {
commonTags.retainAll(s)
}
}
}
partialTags.removeAll(commonTags!!)
return Pair(partialTags, commonTags)
}
@Query("SELECT GROUP_CONCAT(DISTINCT(tag_uid)) FROM tasks"
+ " LEFT JOIN tags ON tags.task = tasks._id"
+ " WHERE tasks._id IN (:tasks)"
+ " GROUP BY tasks._id")
abstract fun getAllTags(tasks: List<Long>): List<String>
@Transaction
open fun applyTags(
tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> {
val modified = HashSet<Long>()
val keep = partiallySelected.plus(selected).map { it.remoteId!! }
for (sublist in Iterables.partition(tasks, DbUtils.MAX_SQLITE_ARGS - keep.size)) {
val tags = tagsToDelete(sublist.map(Task::id), keep)
deleteTags(tags)
modified.addAll(tags.map(Tag::task))
}
for (task in tasks) {
val added = selected subtract getTagDataForTask(task.getId())
if (added.isNotEmpty()) {
modified.add(task.getId())
insert(added.map { Tag(task, it) })
}
}
return ArrayList(modified)
}
@Transaction
open fun delete(tagData: TagData) {
deleteTags(tagData.remoteId!!)
deleteTagData(tagData)
}
@Delete
abstract fun delete(tagData: List<TagData>)
@Delete
abstract fun deleteTags(tags: List<Tag>)
@Query("SELECT tagdata.* FROM tagdata "
+ "INNER JOIN tags ON tags.tag_uid = tagdata.remoteId "
+ "WHERE tags.task = :id "
+ "ORDER BY UPPER(tagdata.name) ASC")
abstract fun getTagDataForTask(id: Long): List<TagData>
@Query("SELECT * FROM tagdata WHERE name IN (:names)")
abstract fun getTags(names: List<String>): List<TagData>
@Update
abstract fun update(tagData: TagData)
@Insert
abstract fun insert(tag: TagData): Long
@Insert
abstract fun insert(tags: Iterable<Tag>)
fun createNew(tag: TagData) {
if (Task.isUuidEmpty(tag.remoteId)) {
tag.remoteId = UUIDHelper.newUUID()
}
tag.id = insert(tag)
}
@Query("SELECT tagdata.*, COUNT(tasks._id) AS count"
+ " FROM tagdata"
+ " LEFT JOIN tags ON tags.tag_uid = tagdata.remoteId"
+ " LEFT JOIN tasks ON tags.task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now"
+ " WHERE tagdata.name IS NOT NULL AND tagdata.name != ''"
+ " GROUP BY tagdata.remoteId")
abstract fun getTagFilters(now: Long): List<TagFilters>
}

@ -238,7 +238,7 @@ class DateTimePicker : InjectingBottomSheetDialogFragment() {
val dueDate = selected?.millis ?: 0
if (dueDate != arguments?.getLong(EXTRA_TIMESTAMP)) {
if (taskId > 0) {
val task: Task = taskDao.fetch(taskId)
val task: Task = taskDao.fetch(taskId)!!
if (newDateTime(dueDate).isAfterNow) {
notificationManager.cancel(task.getId())
}

@ -179,7 +179,7 @@ class Advanced : InjectingPreferenceFragment() {
performAction(
R.string.EPr_manage_delete_completed_gcal_status,
Callable {
calendarEventProvider.deleteEvents(taskDao.completedCalendarEvents)
calendarEventProvider.deleteEvents(taskDao.getCompletedCalendarEvents())
taskDao.clearCompletedCalendarEvents()
})
}
@ -195,7 +195,7 @@ class Advanced : InjectingPreferenceFragment() {
performAction(
R.string.EPr_manage_delete_all_gcal_status,
Callable {
calendarEventProvider.deleteEvents(taskDao.allCalendarEvents)
calendarEventProvider.deleteEvents(taskDao.getAllCalendarEvents())
taskDao.clearAllCalendarEvents()
})
}

Loading…
Cancel
Save