mirror of https://github.com/tasks/tasks
Convert remaining Room DAOs to Kotlin
parent
36b80fcbbf
commit
0fa9149f28
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
}
|
Loading…
Reference in New Issue