diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 083f7a801..9a6598f13 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -54,8 +54,8 @@ android { defaultConfig { testApplicationId = "org.tasks.test" applicationId = "org.tasks" - versionCode = 130102 - versionName = "13.1.2" + versionCode = 130200 + versionName = "13.2" targetSdk = Versions.targetSdk minSdk = Versions.minSdk testInstrumentationRunner = "org.tasks.TestRunner" diff --git a/app/schemas/com.todoroo.astrid.dao.Database/88.json b/app/schemas/com.todoroo.astrid.dao.Database/88.json new file mode 100644 index 000000000..3ed3ff7a0 --- /dev/null +++ b/app/schemas/com.todoroo.astrid.dao.Database/88.json @@ -0,0 +1,1303 @@ +{ + "formatVersion": 1, + "database": { + "version": 88, + "identityHash": "a2597380bdfaeeb040d40bab2fc653c1", + "entities": [ + { + "tableName": "notification", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL, `location` INTEGER, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "taskId", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_notification_task", + "unique": true, + "columnNames": [ + "task" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_notification_task` ON `${TABLE_NAME}` (`task`)" + } + ], + "foreignKeys": [ + { + "table": "tasks", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "task" + ], + "referencedColumns": [ + "_id" + ] + } + ] + }, + { + "tableName": "tagdata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT, `td_icon` INTEGER, `td_order` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tagOrdering", + "columnName": "tagOrdering", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "td_icon", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "order", + "columnName": "td_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "userActivity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "picture", + "columnName": "picture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetId", + "columnName": "target_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "attachment_file", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`file_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_uuid` TEXT NOT NULL, `filename` TEXT NOT NULL, `uri` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "file_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "file_uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "file_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "task_list_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tagUuid", + "columnName": "tag_uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filter", + "columnName": "filter", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskIds", + "columnName": "task_ids", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `recurrence` TEXT, `repeat_from` INTEGER NOT NULL DEFAULT 0, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL, `order` INTEGER, `read_only` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "importance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hideUntil", + "columnName": "hideUntil", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationDate", + "columnName": "created", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modificationDate", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completionDate", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deletionDate", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "estimatedSeconds", + "columnName": "estimatedSeconds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elapsedSeconds", + "columnName": "elapsedSeconds", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timerStart", + "columnName": "timerStart", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ringFlags", + "columnName": "notificationFlags", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reminderLast", + "columnName": "lastNotified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recurrence", + "columnName": "recurrence", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "repeatFrom", + "columnName": "repeat_from", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "calendarURI", + "columnName": "calendarUri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCollapsed", + "columnName": "collapsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "readOnly", + "columnName": "read_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "t_rid", + "unique": true, + "columnNames": [ + "remoteId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `${TABLE_NAME}` (`remoteId`)" + }, + { + "name": "active_and_visible", + "unique": false, + "columnNames": [ + "completed", + "deleted", + "hideUntil" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `active_and_visible` ON `${TABLE_NAME}` (`completed`, `deleted`, `hideUntil`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "alarms", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL, `type` INTEGER NOT NULL DEFAULT 0, `repeat` INTEGER NOT NULL DEFAULT 0, `interval` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "repeat", + "columnName": "repeat", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "interval", + "columnName": "interval", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_alarms_task", + "unique": false, + "columnNames": [ + "task" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_alarms_task` ON `${TABLE_NAME}` (`task`)" + } + ], + "foreignKeys": [ + { + "table": "tasks", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "task" + ], + "referencedColumns": [ + "_id" + ] + } + ] + }, + { + "tableName": "places", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`place_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `name` TEXT, `address` TEXT, `phone` TEXT, `url` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `place_color` INTEGER NOT NULL, `place_icon` INTEGER NOT NULL, `place_order` INTEGER NOT NULL, `radius` INTEGER NOT NULL DEFAULT 250)", + "fields": [ + { + "fieldPath": "id", + "columnName": "place_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "phone", + "columnName": "phone", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "place_color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "place_icon", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "place_order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "radius", + "columnName": "radius", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "250" + } + ], + "primaryKey": { + "columnNames": [ + "place_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "place_uid", + "unique": true, + "columnNames": [ + "uid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `place_uid` ON `${TABLE_NAME}` (`uid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "geofences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "geofence_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "place", + "columnName": "place", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isArrival", + "columnName": "arrival", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeparture", + "columnName": "departure", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "geofence_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_geofences_task", + "unique": false, + "columnNames": [ + "task" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_geofences_task` ON `${TABLE_NAME}` (`task`)" + } + ], + "foreignKeys": [ + { + "table": "tasks", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "task" + ], + "referencedColumns": [ + "_id" + ] + } + ] + }, + { + "tableName": "tags", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tagUid", + "columnName": "tag_uid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskUid", + "columnName": "task_uid", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_tags_task", + "unique": false, + "columnNames": [ + "task" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_tags_task` ON `${TABLE_NAME}` (`task`)" + } + ], + "foreignKeys": [ + { + "table": "tasks", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "task" + ], + "referencedColumns": [ + "_id" + ] + } + ] + }, + { + "tableName": "filters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `sql` TEXT, `values` TEXT, `criterion` TEXT, `f_color` INTEGER, `f_icon` INTEGER, `f_order` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sql", + "columnName": "sql", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "criterion", + "columnName": "criterion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "f_color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "f_icon", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "order", + "columnName": "f_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "caldav_lists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cdl_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cdl_account` TEXT, `cdl_uuid` TEXT, `cdl_name` TEXT, `cdl_color` INTEGER NOT NULL, `cdl_ctag` TEXT, `cdl_url` TEXT, `cdl_icon` INTEGER, `cdl_order` INTEGER NOT NULL, `cdl_access` INTEGER NOT NULL, `cdl_last_sync` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "cdl_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "account", + "columnName": "cdl_account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "cdl_uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "cdl_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "cdl_color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ctag", + "columnName": "cdl_ctag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "cdl_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "cdl_icon", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "order", + "columnName": "cdl_order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "access", + "columnName": "cdl_access", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSync", + "columnName": "cdl_last_sync", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "cdl_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "caldav_tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, FOREIGN KEY(`cd_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "cd_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "cd_task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "calendar", + "columnName": "cd_calendar", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "object", + "columnName": "cd_object", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "cd_remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "cd_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSync", + "columnName": "cd_last_sync", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "cd_deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteParent", + "columnName": "cd_remote_parent", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isMoved", + "columnName": "gt_moved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteOrder", + "columnName": "gt_remote_order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "cd_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_caldav_tasks_cd_task", + "unique": false, + "columnNames": [ + "cd_task" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_caldav_tasks_cd_task` ON `${TABLE_NAME}` (`cd_task`)" + } + ], + "foreignKeys": [ + { + "table": "tasks", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "cd_task" + ], + "referencedColumns": [ + "_id" + ] + } + ] + }, + { + "tableName": "caldav_accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cda_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cda_uuid` TEXT, `cda_name` TEXT, `cda_url` TEXT, `cda_username` TEXT, `cda_password` TEXT, `cda_error` TEXT, `cda_account_type` INTEGER NOT NULL, `cda_collapsed` INTEGER NOT NULL, `cda_server_type` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "cda_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "cda_uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "cda_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "cda_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "cda_username", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "password", + "columnName": "cda_password", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "cda_error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountType", + "columnName": "cda_account_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCollapsed", + "columnName": "cda_collapsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serverType", + "columnName": "cda_server_type", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "cda_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "principals", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` INTEGER NOT NULL, `href` TEXT NOT NULL, `email` TEXT, `display_name` TEXT, FOREIGN KEY(`account`) REFERENCES `caldav_accounts`(`cda_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "href", + "columnName": "href", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "display_name", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_principals_account_href", + "unique": true, + "columnNames": [ + "account", + "href" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_principals_account_href` ON `${TABLE_NAME}` (`account`, `href`)" + } + ], + "foreignKeys": [ + { + "table": "caldav_accounts", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account" + ], + "referencedColumns": [ + "cda_id" + ] + } + ] + }, + { + "tableName": "principal_access", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `principal` INTEGER NOT NULL, `list` INTEGER NOT NULL, `invite` INTEGER NOT NULL, `access` INTEGER NOT NULL, FOREIGN KEY(`principal`) REFERENCES `principals`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`list`) REFERENCES `caldav_lists`(`cdl_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "principal", + "columnName": "principal", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "list", + "columnName": "list", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "invite", + "columnName": "invite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "access", + "columnName": "access", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_principal_access_list_principal", + "unique": true, + "columnNames": [ + "list", + "principal" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_access_list_principal` ON `${TABLE_NAME}` (`list`, `principal`)" + }, + { + "name": "index_principal_access_principal", + "unique": false, + "columnNames": [ + "principal" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_principal_access_principal` ON `${TABLE_NAME}` (`principal`)" + } + ], + "foreignKeys": [ + { + "table": "principals", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "principal" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "caldav_lists", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "list" + ], + "referencedColumns": [ + "cdl_id" + ] + } + ] + }, + { + "tableName": "attachment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`attachment_id` INTEGER PRIMARY KEY AUTOINCREMENT, `task` INTEGER NOT NULL, `file` INTEGER NOT NULL, `file_uuid` TEXT NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`file`) REFERENCES `attachment_file`(`file_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "attachment_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fileId", + "columnName": "file", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "attachmentUid", + "columnName": "file_uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "attachment_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_attachment_task_file", + "unique": true, + "columnNames": [ + "task", + "file" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_attachment_task_file` ON `${TABLE_NAME}` (`task`, `file`)" + }, + { + "name": "index_attachment_task", + "unique": false, + "columnNames": [ + "task" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_attachment_task` ON `${TABLE_NAME}` (`task`)" + }, + { + "name": "index_attachment_file", + "unique": false, + "columnNames": [ + "file" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_attachment_file` ON `${TABLE_NAME}` (`file`)" + } + ], + "foreignKeys": [ + { + "table": "tasks", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "task" + ], + "referencedColumns": [ + "_id" + ] + }, + { + "table": "attachment_file", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "file" + ], + "referencedColumns": [ + "file_id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a2597380bdfaeeb040d40bab2fc653c1')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapterTest.kt b/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapterTest.kt index 9f28a289e..3840c84be 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapterTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapterTest.kt @@ -224,7 +224,7 @@ class CaldavManualSortTaskAdapterTest : InjectingTestCase() { private fun checkOrder(dateTime: DateTime, index: Int) = checkOrder(dateTime.toAppleEpoch(), index) private fun checkOrder(order: Long?, index: Int) = runBlocking { - val sortOrder = caldavDao.getTask(adapter.getTask(index).id)!!.order + val sortOrder = taskDao.fetch(adapter.getTask(index).id)!!.order if (order == null) { assertNull(sortOrder) } else { diff --git a/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavTaskAdapterTest.kt b/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavTaskAdapterTest.kt index b7a8033fc..e39ad2d46 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavTaskAdapterTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/adapter/CaldavTaskAdapterTest.kt @@ -202,14 +202,6 @@ class CaldavTaskAdapterTest : InjectingTestCase() { caldavTask.remoteParent = caldavDao.getRemoteIdForTask(task.parent) } caldavTask.id = caldavDao.insert(caldavTask) - t.caldavTask = caldavTask.toSubset() - } - - private fun CaldavTask.toSubset(): SubsetCaldav { - val result = SubsetCaldav() - result.cd_id = id - result.cd_calendar = calendar - result.cd_remote_parent = remoteParent - return result + t.caldavTask = caldavTask } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapterTest.kt b/app/src/androidTest/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapterTest.kt index 227470bc7..0ad667f06 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapterTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapterTest.kt @@ -19,12 +19,11 @@ import org.tasks.data.TaskContainer import org.tasks.data.TaskListQuery.getQuery import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule -import org.tasks.makers.GoogleTaskListMaker.REMOTE_ID -import org.tasks.makers.GoogleTaskListMaker.newGoogleTaskList -import org.tasks.makers.GoogleTaskMaker -import org.tasks.makers.GoogleTaskMaker.LIST -import org.tasks.makers.GoogleTaskMaker.TASK -import org.tasks.makers.GoogleTaskMaker.newGoogleTask +import org.tasks.makers.CaldavCalendarMaker +import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar +import org.tasks.makers.CaldavTaskMaker.CALENDAR +import org.tasks.makers.CaldavTaskMaker.TASK +import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.TaskMaker.PARENT import org.tasks.makers.TaskMaker.newTask import org.tasks.preferences.Preferences @@ -41,7 +40,7 @@ class GoogleTaskManualSortAdapterTest : InjectingTestCase() { private lateinit var adapter: GoogleTaskManualSortAdapter private val tasks = ArrayList() - private val filter = GtasksFilter(newGoogleTaskList(with(REMOTE_ID, "1234"))) + private val filter = GtasksFilter(newCaldavCalendar(with(CaldavCalendarMaker.UUID, "1234"))) private val dataSource = object : TaskAdapterDataSource { override fun getItem(position: Int) = tasks[position] @@ -427,22 +426,22 @@ class GoogleTaskManualSortAdapterTest : InjectingTestCase() { } private fun checkOrder(order: Long, index: Int, parent: Long = 0) = runBlocking { - val googleTask = googleTaskDao.getByTaskId(adapter.getTask(index).id)!! + val googleTask = taskDao.fetch(adapter.getTask(index).id)!! assertEquals(order, googleTask.order) assertEquals(parent, googleTask.parent) } private fun addTask(vararg properties: PropertyValue): Long = runBlocking { val task = newTask(*properties) - val parent = task.parent - task.parent = 0 taskDao.createNew(task) googleTaskDao.insertAndShift( - newGoogleTask( - with(TASK, task.id), - with(LIST, "1234"), - with(GoogleTaskMaker.PARENT, parent)), - false) + task, + newCaldavTask( + with(TASK, task.id), + with(CALENDAR, "1234"), + ), + false + ) task.id } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.kt b/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.kt index 18ed57651..3037f3761 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.kt @@ -11,14 +11,15 @@ import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.tasks.LocalBroadcastManager -import org.tasks.data.GoogleTaskAccount +import org.tasks.data.CaldavAccount +import org.tasks.data.CaldavDao import org.tasks.data.GoogleTaskListDao import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule -import org.tasks.makers.GtaskListMaker.ID -import org.tasks.makers.GtaskListMaker.NAME -import org.tasks.makers.GtaskListMaker.REMOTE_ID -import org.tasks.makers.GtaskListMaker.newGtaskList +import org.tasks.makers.CaldavCalendarMaker.ID +import org.tasks.makers.CaldavCalendarMaker.NAME +import org.tasks.makers.CaldavCalendarMaker.UUID +import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar import org.tasks.makers.RemoteGtaskListMaker import org.tasks.makers.RemoteGtaskListMaker.newRemoteList import javax.inject.Inject @@ -29,6 +30,7 @@ class GtasksListServiceTest : InjectingTestCase() { @Inject lateinit var taskDeleter: TaskDeleter @Inject lateinit var localBroadcastManager: LocalBroadcastManager @Inject lateinit var googleTaskListDao: GoogleTaskListDao + @Inject lateinit var caldavDao: CaldavDao private lateinit var gtasksListService: GtasksListService @@ -44,13 +46,13 @@ class GtasksListServiceTest : InjectingTestCase() { newRemoteList( with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "Default"))) assertEquals( - newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"), with(NAME, "Default")), + newCaldavCalendar(with(ID, 1L), with(UUID, "1"), with(NAME, "Default")), googleTaskListDao.getById(1L)) } @Test fun testGetListByRemoteId() = runBlocking { - val list = newGtaskList(with(REMOTE_ID, "1")) + val list = newCaldavCalendar(with(UUID, "1")) list.id = googleTaskListDao.insertOrReplace(list) assertEquals(list, googleTaskListDao.getByRemoteId("1")) } @@ -62,22 +64,23 @@ class GtasksListServiceTest : InjectingTestCase() { @Test fun testDeleteMissingList() = runBlocking { - googleTaskListDao.insertOrReplace(newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"))) + googleTaskListDao.insertOrReplace(newCaldavCalendar(with(ID, 1L), with(UUID, "1"))) val taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2")) setLists(taskList) assertEquals( - listOf(newGtaskList(with(ID, 2L), with(REMOTE_ID, "2"))), + listOf(newCaldavCalendar(with(ID, 2L), with(UUID, "2"), with(NAME, "Default"))), googleTaskListDao.getLists("account")) } @Test fun testUpdateListName() = runBlocking { googleTaskListDao.insertOrReplace( - newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"), with(NAME, "oldName"))) + newCaldavCalendar(with(ID, 1L), with(UUID, "1"), with(NAME, "oldName")) + ) setLists( newRemoteList( with(RemoteGtaskListMaker.REMOTE_ID, "1"), with(RemoteGtaskListMaker.NAME, "newName"))) - assertEquals("newName", googleTaskListDao.getById(1)!!.title) + assertEquals("newName", googleTaskListDao.getById(1)!!.name) } @Test @@ -87,8 +90,11 @@ class GtasksListServiceTest : InjectingTestCase() { } private suspend fun setLists(vararg list: TaskList) { - val account = GoogleTaskAccount("account") - googleTaskListDao.insert(account) + val account = CaldavAccount().apply { + username = "account" + uuid = "account" + } + caldavDao.insert(account) gtasksListService.updateLists(account, listOf(*list)) } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.kt b/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.kt deleted file mode 100644 index 54aa5d022..000000000 --- a/app/src/androidTest/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2012 Todoroo Inc - * - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.gtasks - -import com.todoroo.astrid.dao.TaskDao -import com.todoroo.astrid.data.Task -import dagger.hilt.android.testing.HiltAndroidTest -import dagger.hilt.android.testing.UninstallModules -import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Test -import org.tasks.data.GoogleTask -import org.tasks.data.GoogleTaskDao -import org.tasks.injection.InjectingTestCase -import org.tasks.injection.ProductionModule -import javax.inject.Inject - -@UninstallModules(ProductionModule::class) -@HiltAndroidTest -class GtasksMetadataServiceTest : InjectingTestCase() { - @Inject lateinit var taskDao: TaskDao - @Inject lateinit var googleTaskDao: GoogleTaskDao - - private var task: Task? = null - private var metadata: GoogleTask? = null - - @Test - fun testMetadataFound() = runBlocking { - givenTask(taskWithMetadata(null)) - whenSearchForMetadata() - thenExpectMetadataFound() - } - - @Test - fun testMetadataDoesntExist() = runBlocking { - givenTask(taskWithoutMetadata()) - whenSearchForMetadata() - thenExpectNoMetadataFound() - } - - private fun thenExpectNoMetadataFound() { - assertNull(metadata) - } - - private fun thenExpectMetadataFound() { - assertNotNull(metadata) - } - - // --- helpers - private suspend fun whenSearchForMetadata() { - metadata = googleTaskDao.getByTaskId(task!!.id) - } - - private suspend fun taskWithMetadata(id: String?): Task { - val task = Task() - task.title = "cats" - taskDao.createNew(task) - val metadata = GoogleTask(task.id, "") - if (id != null) { - metadata.remoteId = id - } - metadata.task = task.id - googleTaskDao.insert(metadata) - return task - } - - private fun givenTask(taskToTest: Task) { - task = taskToTest - } - - private suspend fun taskWithoutMetadata(): Task { - val task = Task() - task.title = "dogs" - taskDao.createNew(task) - return task - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/todoroo/astrid/service/TaskDeleterTest.kt b/app/src/androidTest/java/com/todoroo/astrid/service/TaskDeleterTest.kt index e741df3bb..3ad55d8b3 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/service/TaskDeleterTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/service/TaskDeleterTest.kt @@ -12,9 +12,6 @@ import org.tasks.data.GoogleTaskDao import org.tasks.data.TaskDao import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule -import org.tasks.makers.GoogleTaskMaker -import org.tasks.makers.GoogleTaskMaker.TASK -import org.tasks.makers.GoogleTaskMaker.newGoogleTask import org.tasks.makers.TaskMaker.COMPLETION_TIME import org.tasks.makers.TaskMaker.PARENT import org.tasks.makers.TaskMaker.RECUR @@ -96,42 +93,6 @@ class TaskDeleterTest : InjectingTestCase() { assertTrue(taskDao.fetch(child)!!.isDeleted) } - @Test - fun dontClearCompletedGoogleTaskWithRecurringParent() = runBlocking { - val parent = taskDao.createNew(newTask(with(RECUR, "RRULE:FREQ=DAILY;INTERVAL=1"))) - val child = taskDao.createNew(newTask(with(COMPLETION_TIME, DateTime()))) - googleTaskDao.insert(newGoogleTask(with(TASK, child), with(GoogleTaskMaker.PARENT, parent))) - - clearCompleted() - - assertFalse(taskDao.fetch(child)!!.isDeleted) - } - - @Test - fun clearCompletedGoogleTaskWithNonRecurringParent() = runBlocking { - val parent = taskDao.createNew(newTask()) - val child = taskDao.createNew(newTask(with(COMPLETION_TIME, DateTime()))) - googleTaskDao.insert(newGoogleTask(with(TASK, child), with(GoogleTaskMaker.PARENT, parent))) - - clearCompleted() - - assertTrue(taskDao.fetch(child)!!.isDeleted) - } - - @Test - fun clearCompletedGoogleTaskWithCompletedRecurringParent() = runBlocking { - val parent = taskDao.createNew(newTask( - with(RECUR, "RRULE:FREQ=DAILY;INTERVAL=1"), - with(COMPLETION_TIME, DateTime()) - )) - val child = taskDao.createNew(newTask(with(COMPLETION_TIME, DateTime()))) - googleTaskDao.insert(newGoogleTask(with(TASK, child), with(GoogleTaskMaker.PARENT, parent))) - - clearCompleted() - - assertTrue(taskDao.fetch(child)!!.isDeleted) - } - private suspend fun clearCompleted() = taskDeleter.clearCompleted(getMyTasksFilter(context.resources)) } \ No newline at end of file diff --git a/app/src/androidTest/java/com/todoroo/astrid/service/TaskMoverTest.kt b/app/src/androidTest/java/com/todoroo/astrid/service/TaskMoverTest.kt index 8a43ee777..0d272157b 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/service/TaskMoverTest.kt +++ b/app/src/androidTest/java/com/todoroo/astrid/service/TaskMoverTest.kt @@ -9,28 +9,29 @@ import dagger.hilt.android.testing.UninstallModules import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Test +import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.CaldavCalendar import org.tasks.data.CaldavDao import org.tasks.data.GoogleTaskDao import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule import org.tasks.jobs.WorkManager -import org.tasks.makers.CaldavCalendarMaker.UUID +import org.tasks.makers.CaldavAccountMaker +import org.tasks.makers.CaldavAccountMaker.ACCOUNT_TYPE +import org.tasks.makers.CaldavAccountMaker.newCaldavAccount +import org.tasks.makers.CaldavCalendarMaker +import org.tasks.makers.CaldavCalendarMaker.ACCOUNT import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar -import org.tasks.makers.CaldavTaskMaker import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.REMOTE_ID import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT +import org.tasks.makers.CaldavTaskMaker.TASK import org.tasks.makers.CaldavTaskMaker.newCaldavTask -import org.tasks.makers.GoogleTaskMaker.LIST -import org.tasks.makers.GoogleTaskMaker.PARENT -import org.tasks.makers.GoogleTaskMaker.TASK -import org.tasks.makers.GoogleTaskMaker.newGoogleTask -import org.tasks.makers.GtaskListMaker -import org.tasks.makers.GtaskListMaker.newGtaskList -import org.tasks.makers.TaskMaker import org.tasks.makers.TaskMaker.ID +import org.tasks.makers.TaskMaker.PARENT import org.tasks.makers.TaskMaker.newTask import javax.inject.Inject @@ -44,18 +45,28 @@ class TaskMoverTest : InjectingTestCase() { @Inject lateinit var caldavDao: CaldavDao @Inject lateinit var taskMover: TaskMover + @Before + fun setup() { + runBlocking { + caldavDao.insert(newCaldavCalendar(with(CaldavCalendarMaker.UUID, "1"), with(ACCOUNT, "account1"))) + caldavDao.insert(newCaldavCalendar(with(CaldavCalendarMaker.UUID, "2"), with(ACCOUNT, "account2"))) + } + } + @Test fun moveBetweenGoogleTaskLists() = runBlocking { + setAccountType("account1", TYPE_GOOGLE_TASKS) + setAccountType("account2", TYPE_GOOGLE_TASKS) createTasks(1) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) moveToGoogleTasks("2", 1) - assertEquals("2", googleTaskDao.getByTaskId(1)!!.listId) + assertEquals("2", googleTaskDao.getByTaskId(1)?.calendar) } @Test fun deleteGoogleTaskAfterMove() = runBlocking { createTasks(1) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) moveToGoogleTasks("2", 1) val deleted = googleTaskDao.getDeletedByTaskId(1) assertEquals(1, deleted.size.toLong()) @@ -65,24 +76,25 @@ class TaskMoverTest : InjectingTestCase() { @Test fun moveChildrenBetweenGoogleTaskLists() = runBlocking { - createTasks(1, 2) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) - googleTaskDao.insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L))) + setAccountType("account1", TYPE_GOOGLE_TASKS) + setAccountType("account2", TYPE_GOOGLE_TASKS) + createTasks(1) + createSubtask(2, 1) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 2), with(CALENDAR, "1"))) moveToGoogleTasks("2", 1) val deleted = googleTaskDao.getDeletedByTaskId(2) assertEquals(1, deleted.size.toLong()) assertEquals(2, deleted[0].task) assertTrue(deleted[0].deleted > 0) - val task = googleTaskDao.getByTaskId(2)!! - assertEquals(1, task.parent) - assertEquals("2", task.listId) + assertEquals(1L, taskDao.fetch(2)?.parent) + assertEquals("2", googleTaskDao.getByTaskId(2)?.calendar) } @Test fun moveBetweenCaldavList() = runBlocking { createTasks(1) - caldavDao.insert(newCaldavCalendar(with(UUID, "1"))) - caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"))) + caldavDao.insert(newCaldavTask(with(TASK, 1L), with(CALENDAR, "1"))) moveToCaldavList("2", 1) assertEquals("2", caldavDao.getTask(1)!!.calendar) } @@ -90,8 +102,7 @@ class TaskMoverTest : InjectingTestCase() { @Test fun deleteCaldavTaskAfterMove() = runBlocking { createTasks(1) - caldavDao.insert(newCaldavCalendar(with(UUID, "1"))) - caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"))) + caldavDao.insert(newCaldavTask(with(TASK, 1L), with(CALENDAR, "1"))) moveToCaldavList("2", 1) val deleted = caldavDao.getMoved("1") assertEquals(1, deleted.size.toLong()) @@ -104,18 +115,17 @@ class TaskMoverTest : InjectingTestCase() { createTasks(1) createSubtask(2, 1) createSubtask(3, 2) - caldavDao.insert(newCaldavCalendar(with(UUID, "1"))) caldavDao.insert( listOf( newCaldavTask( - with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), + with(TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), newCaldavTask( - with(CaldavTaskMaker.TASK, 2L), + with(TASK, 2L), with(CALENDAR, "1"), with(REMOTE_ID, "b"), with(REMOTE_PARENT, "a")), newCaldavTask( - with(CaldavTaskMaker.TASK, 3L), + with(TASK, 3L), with(CALENDAR, "1"), with(REMOTE_PARENT, "b")))) moveToCaldavList("2", 1) @@ -128,13 +138,16 @@ class TaskMoverTest : InjectingTestCase() { @Test fun moveGoogleTaskChildrenToCaldav() = runBlocking { - createTasks(1, 2) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) - googleTaskDao.insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L))) + setAccountType("account1", TYPE_GOOGLE_TASKS) + setAccountType("account2", TYPE_CALDAV) + createTasks(1) + createSubtask(2, 1) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 2), with(CALENDAR, "1"))) moveToCaldavList("1", 1) val task = caldavDao.getTask(2) assertEquals("1", task!!.calendar) - assertEquals(1, taskDao.fetch(2)!!.parent) + assertEquals(1L, taskDao.fetch(2)?.parent) } @Test @@ -143,8 +156,7 @@ class TaskMoverTest : InjectingTestCase() { createSubtask(2, 1) createSubtask(3, 2) moveToGoogleTasks("1", 1) - assertEquals(1, googleTaskDao.getByTaskId(3)!!.parent) - assertEquals(0, taskDao.fetch(3)!!.parent) + assertEquals(1L, taskDao.fetch(3)?.parent) } @Test @@ -152,7 +164,7 @@ class TaskMoverTest : InjectingTestCase() { createTasks(1) createSubtask(2, 1) moveToGoogleTasks("1", 2) - assertEquals(0, taskDao.fetch(2)!!.parent) + assertEquals(0L, taskDao.fetch(2)?.parent) } @Test @@ -171,43 +183,43 @@ class TaskMoverTest : InjectingTestCase() { caldavDao.insert( listOf( newCaldavTask( - with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), + with(TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), newCaldavTask( - with(CaldavTaskMaker.TASK, 2L), + with(TASK, 2L), with(CALENDAR, "1"), with(REMOTE_ID, "b"), with(REMOTE_PARENT, "a")), newCaldavTask( - with(CaldavTaskMaker.TASK, 3L), + with(TASK, 3L), with(CALENDAR, "1"), with(REMOTE_PARENT, "b")))) moveToGoogleTasks("1", 1) - val task = googleTaskDao.getByTaskId(3L)!! - assertEquals(1, task.parent) + val task = taskDao.fetch(3L) + assertEquals(1L, task?.parent) } @Test fun moveGoogleTaskChildWithoutParent() = runBlocking { - createTasks(1, 2) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) - googleTaskDao.insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L))) + setAccountType("account2", TYPE_GOOGLE_TASKS) + createTasks(1) + createSubtask(2, 1) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 2), with(CALENDAR, "1"))) moveToGoogleTasks("2", 2) - val task = googleTaskDao.getByTaskId(2)!! - assertEquals(0L, task.parent) - assertEquals("2", task.listId) + assertEquals(0L, taskDao.fetch(2)?.parent) + assertEquals("2", googleTaskDao.getByTaskId(2)?.calendar) } @Test fun moveCaldavChildWithoutParent() = runBlocking { createTasks(1) createSubtask(2, 1) - caldavDao.insert(newCaldavCalendar(with(UUID, "1"))) caldavDao.insert( listOf( newCaldavTask( - with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), + with(TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), newCaldavTask( - with(CaldavTaskMaker.TASK, 2L), + with(TASK, 2L), with(CALENDAR, "1"), with(REMOTE_PARENT, "a")))) moveToCaldavList("2", 2) @@ -218,17 +230,19 @@ class TaskMoverTest : InjectingTestCase() { @Test fun moveGoogleTaskToCaldav() = runBlocking { createTasks(1) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) moveToCaldavList("2", 1) assertEquals("2", caldavDao.getTask(1)!!.calendar) } @Test fun moveCaldavToGoogleTask() = runBlocking { + setAccountType("account1", TYPE_CALDAV) + setAccountType("account2", TYPE_GOOGLE_TASKS) createTasks(1) - caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"))) + caldavDao.insert(newCaldavTask(with(TASK, 1L), with(CALENDAR, "1"))) moveToGoogleTasks("2", 1) - assertEquals("2", googleTaskDao.getByTaskId(1L)!!.listId) + assertEquals("2", googleTaskDao.getByTaskId(1L)?.calendar) } @Test @@ -237,14 +251,15 @@ class TaskMoverTest : InjectingTestCase() { createSubtask(2, 1) createSubtask(3, 2) moveToCaldavList("1", 1) - assertEquals("1", caldavDao.getTask(3)!!.calendar) - assertEquals(2, taskDao.fetch(3)!!.parent) + assertEquals("1", caldavDao.getTask(3)?.calendar) + assertEquals(2L, taskDao.fetch(3)?.parent) } @Test fun moveToSameGoogleTaskListIsNoop() = runBlocking { + setAccountType("account1", TYPE_GOOGLE_TASKS) createTasks(1) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) moveToGoogleTasks("1", 1) assertTrue(googleTaskDao.getDeletedByTaskId(1).isEmpty()) assertEquals(1, googleTaskDao.getAllByTaskId(1).size.toLong()) @@ -253,7 +268,7 @@ class TaskMoverTest : InjectingTestCase() { @Test fun moveToSameCaldavListIsNoop() = runBlocking { createTasks(1) - caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"))) + caldavDao.insert(newCaldavTask(with(TASK, 1L), with(CALENDAR, "1"))) moveToCaldavList("1", 1) assertTrue(caldavDao.getMoved("1").isEmpty()) assertEquals(1, caldavDao.getTasks(1).size.toLong()) @@ -261,9 +276,10 @@ class TaskMoverTest : InjectingTestCase() { @Test fun dontDuplicateWhenParentAndChildGoogleTaskMoved() = runBlocking { - createTasks(1, 2) - googleTaskDao.insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) - googleTaskDao.insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L))) + createTasks(1) + createSubtask(2, 1) + googleTaskDao.insert(newCaldavTask(with(TASK, 1), with(CALENDAR, "1"))) + googleTaskDao.insert(newCaldavTask(with(TASK, 2), with(CALENDAR, "1"))) moveToGoogleTasks("2", 1, 2) assertEquals(1, googleTaskDao.getAllByTaskId(2).filter { it.deleted == 0L }.size) } @@ -272,13 +288,12 @@ class TaskMoverTest : InjectingTestCase() { fun dontDuplicateWhenParentAndChildCaldavMoved() = runBlocking { createTasks(1) createSubtask(2, 1) - caldavDao.insert(newCaldavCalendar(with(UUID, "1"))) caldavDao.insert( listOf( newCaldavTask( - with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), + with(TASK, 1L), with(CALENDAR, "1"), with(REMOTE_ID, "a")), newCaldavTask( - with(CaldavTaskMaker.TASK, 2L), + with(TASK, 2L), with(CALENDAR, "1"), with(REMOTE_PARENT, "a")))) moveToCaldavList("2", 1, 2) @@ -292,14 +307,18 @@ class TaskMoverTest : InjectingTestCase() { } private suspend fun createSubtask(id: Long, parent: Long) { - taskDao.createNew(newTask(with(ID, id), with(TaskMaker.PARENT, parent))) + taskDao.createNew(newTask(with(ID, id), with(PARENT, parent))) } private suspend fun moveToGoogleTasks(list: String, vararg tasks: Long) { - taskMover.move(tasks.toList(), GtasksFilter(newGtaskList(with(GtaskListMaker.REMOTE_ID, list)))) + taskMover.move(tasks.toList(), GtasksFilter(newCaldavCalendar(with(CaldavCalendarMaker.UUID, list)))) } private suspend fun moveToCaldavList(calendar: String, vararg tasks: Long) { taskMover.move(tasks.toList(), CaldavFilter(CaldavCalendar(name = "", uuid = calendar))) } + + private suspend fun setAccountType(account: String, type: Int) { + caldavDao.insert(newCaldavAccount(with(CaldavAccountMaker.UUID, account), with(ACCOUNT_TYPE, type))) + } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/tasks/data/CaldavDaoShiftTests.kt b/app/src/androidTest/java/org/tasks/data/CaldavDaoShiftTests.kt index b66cf0ca7..5ab5b3d59 100644 --- a/app/src/androidTest/java/org/tasks/data/CaldavDaoShiftTests.kt +++ b/app/src/androidTest/java/org/tasks/data/CaldavDaoShiftTests.kt @@ -105,7 +105,7 @@ class CaldavDaoShiftTests : InjectingTestCase() { caldavDao.shiftDown("calendar", 0, created.toAppleEpoch()) - assertNull(caldavDao.getTasks(tasks[0].id)[0].order) + assertNull(taskDao.fetch(tasks[0].id)!!.order) } @Test @@ -116,7 +116,7 @@ class CaldavDaoShiftTests : InjectingTestCase() { caldavDao.shiftDown("calendar", 0, created.toAppleEpoch()) - assertNull(caldavDao.getTasks(tasks[0].id)[0].order) + assertNull(taskDao.fetch(tasks[0].id)!!.order) } @Test @@ -134,10 +134,11 @@ class CaldavDaoShiftTests : InjectingTestCase() { } private suspend fun checkOrder(dateTime: DateTime?, task: TaskContainer) { + val order = taskDao.fetch(task.id)!!.order if (dateTime == null) { - assertNull(caldavDao.getTask(task.id)!!.order) + assertNull(order) } else { - assertEquals(dateTime.toAppleEpoch(), caldavDao.getTask(task.id)!!.order) + assertEquals(dateTime.toAppleEpoch(), order) } } @@ -153,14 +154,6 @@ class CaldavDaoShiftTests : InjectingTestCase() { caldavTask.remoteParent = caldavDao.getRemoteIdForTask(task.parent) } caldavTask.id = caldavDao.insert(caldavTask) - t.caldavTask = caldavTask.toSubset() - } - - private fun CaldavTask.toSubset(): SubsetCaldav { - val result = SubsetCaldav() - result.cd_id = id - result.cd_calendar = calendar - result.cd_remote_parent = remoteParent - return result + t.caldavTask = caldavTask } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/tasks/data/CaldavDaoTests.kt b/app/src/androidTest/java/org/tasks/data/CaldavDaoTests.kt index f0c853a85..6aa8f4e27 100644 --- a/app/src/androidTest/java/org/tasks/data/CaldavDaoTests.kt +++ b/app/src/androidTest/java/org/tasks/data/CaldavDaoTests.kt @@ -99,7 +99,7 @@ class CaldavDaoTests : InjectingTestCase() { private suspend fun checkOrder(dateTime: DateTime, task: Long) = checkOrder(dateTime.toAppleEpoch(), task) private suspend fun checkOrder(order: Long?, task: Long) { - val sortOrder = caldavDao.getTask(task)!!.order + val sortOrder = taskDao.fetch(task)!!.order if (order == null) { assertNull(sortOrder) } else { diff --git a/app/src/androidTest/java/org/tasks/data/GoogleTaskDaoTests.kt b/app/src/androidTest/java/org/tasks/data/GoogleTaskDaoTests.kt index f0e9ce49a..2ef5ca9fa 100644 --- a/app/src/androidTest/java/org/tasks/data/GoogleTaskDaoTests.kt +++ b/app/src/androidTest/java/org/tasks/data/GoogleTaskDaoTests.kt @@ -9,15 +9,17 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule -import org.tasks.makers.GoogleTaskMaker.LIST -import org.tasks.makers.GoogleTaskMaker.PARENT -import org.tasks.makers.GoogleTaskMaker.REMOTE_ID -import org.tasks.makers.GoogleTaskMaker.REMOTE_PARENT -import org.tasks.makers.GoogleTaskMaker.TASK -import org.tasks.makers.GoogleTaskMaker.newGoogleTask -import org.tasks.makers.GtaskListMaker.newGtaskList +import org.tasks.makers.CaldavAccountMaker.ACCOUNT_TYPE +import org.tasks.makers.CaldavAccountMaker.newCaldavAccount +import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar +import org.tasks.makers.CaldavTaskMaker.CALENDAR +import org.tasks.makers.CaldavTaskMaker.REMOTE_ID +import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT +import org.tasks.makers.CaldavTaskMaker.TASK +import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.TaskMaker.newTask import javax.inject.Inject @@ -27,140 +29,135 @@ class GoogleTaskDaoTests : InjectingTestCase() { @Inject lateinit var googleTaskListDao: GoogleTaskListDao @Inject lateinit var googleTaskDao: GoogleTaskDao @Inject lateinit var taskDao: TaskDao + @Inject lateinit var caldavDao: CaldavDao @Before override fun setUp() { super.setUp() runBlocking { - googleTaskListDao.insert(newGtaskList()) + caldavDao.insert(newCaldavAccount(with(ACCOUNT_TYPE, TYPE_GOOGLE_TASKS))) + caldavDao.insert(newCaldavCalendar()) } } @Test fun insertAtTopOfEmptyList() = runBlocking { - insertTop(newGoogleTask(with(REMOTE_ID, "1234"))) - val tasks = googleTaskDao.getByLocalOrder("1") + insertTop(newCaldavTask(with(REMOTE_ID, "1234"))) + val tasks = googleTaskDao.getByLocalOrder("calendar") assertEquals(1, tasks.size.toLong()) val task = tasks[0] - assertEquals("1234", task.remoteId) - assertEquals(0, task.order) + assertEquals("1234", googleTaskDao.getByTaskId(task.id)?.remoteId) + assertEquals(0L, task.order) } @Test fun insertAtBottomOfEmptyList() = runBlocking { - insertBottom(newGoogleTask(with(REMOTE_ID, "1234"))) - val tasks = googleTaskDao.getByLocalOrder("1") + insertBottom(newCaldavTask(with(REMOTE_ID, "1234"))) + val tasks = googleTaskDao.getByLocalOrder("calendar") assertEquals(1, tasks.size.toLong()) val task = tasks[0] - assertEquals("1234", task.remoteId) - assertEquals(0, task.order) + assertEquals("1234", googleTaskDao.getByTaskId(task.id)?.remoteId) + assertEquals(0L, task.order) } @Test fun getPreviousIsNullForTopTask() = runBlocking { - insert(newGoogleTask()) + insert(newCaldavTask()) assertNull(googleTaskDao.getPrevious("1", 0, 0)) } @Test fun getPrevious() = runBlocking { - insertTop(newGoogleTask()) - insertTop(newGoogleTask(with(REMOTE_ID, "1234"))) - assertEquals("1234", googleTaskDao.getPrevious("1", 0, 1)) + insertTop(newCaldavTask()) + insertTop(newCaldavTask(with(REMOTE_ID, "1234"))) + assertEquals("1234", googleTaskDao.getPrevious("calendar", 0, 1)) } @Test fun insertAtTopOfList() = runBlocking { - insertTop(newGoogleTask(with(REMOTE_ID, "1234"))) - insertTop(newGoogleTask(with(REMOTE_ID, "5678"))) - val tasks = googleTaskDao.getByLocalOrder("1") + insertTop(newCaldavTask(with(REMOTE_ID, "1234"))) + insertTop(newCaldavTask(with(REMOTE_ID, "5678"))) + val tasks = googleTaskDao.getByLocalOrder("calendar") assertEquals(2, tasks.size.toLong()) val top = tasks[0] - assertEquals("5678", top.remoteId) - assertEquals(0, top.order) + assertEquals("5678", googleTaskDao.getByTaskId(top.id)?.remoteId) + assertEquals(0L, top.order) } @Test fun insertAtTopOfListShiftsExisting() = runBlocking { - insertTop(newGoogleTask(with(REMOTE_ID, "1234"))) - insertTop(newGoogleTask(with(REMOTE_ID, "5678"))) - val tasks = googleTaskDao.getByLocalOrder("1") + insertTop(newCaldavTask(with(REMOTE_ID, "1234"))) + insertTop(newCaldavTask(with(REMOTE_ID, "5678"))) + val tasks = googleTaskDao.getByLocalOrder("calendar") assertEquals(2, tasks.size.toLong()) val bottom = tasks[1] - assertEquals("1234", bottom.remoteId) - assertEquals(1, bottom.order) + assertEquals("1234", googleTaskDao.getByTaskId(bottom.id)?.remoteId) + assertEquals(1L, bottom.order) } @Test fun getTaskFromRemoteId() = runBlocking { - insert(newGoogleTask(with(REMOTE_ID, "1234"))) + insert(newCaldavTask(with(REMOTE_ID, "1234"))) assertEquals(1L, googleTaskDao.getTask("1234")) } @Test fun getRemoteIdForTask() = runBlocking { - insert(newGoogleTask(with(REMOTE_ID, "1234"))) + insert(newCaldavTask(with(REMOTE_ID, "1234"))) assertEquals("1234", googleTaskDao.getRemoteId(1L)) } @Test fun moveDownInList() = runBlocking { - insert(newGoogleTask(with(REMOTE_ID, "1"))) - insert(newGoogleTask(with(REMOTE_ID, "2"))) - insert(newGoogleTask(with(REMOTE_ID, "3"))) + insert(newCaldavTask(with(REMOTE_ID, "1"))) + insert(newCaldavTask(with(REMOTE_ID, "2"))) + insert(newCaldavTask(with(REMOTE_ID, "3"))) 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) + googleTaskDao.move(taskDao.fetch(two.task)!!, "calendar", 0, 0) + assertEquals(0L, getOrder("2")) + assertEquals(1L, getOrder("1")) + assertEquals(2L, getOrder("3")) } @Test fun moveUpInList() = runBlocking { - insert(newGoogleTask(with(REMOTE_ID, "1"))) - insert(newGoogleTask(with(REMOTE_ID, "2"))) - insert(newGoogleTask(with(REMOTE_ID, "3"))) + insert(newCaldavTask(with(REMOTE_ID, "1"))) + insert(newCaldavTask(with(REMOTE_ID, "2"))) + insert(newCaldavTask(with(REMOTE_ID, "3"))) 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) + googleTaskDao.move(taskDao.fetch(one.task)!!, "calendar", 0, 1) + assertEquals(0L, getOrder("2")) + assertEquals(1L, getOrder("1")) + assertEquals(2L, getOrder("3")) } @Test fun moveToTop() = runBlocking { - insert(newGoogleTask(with(REMOTE_ID, "1"))) - insert(newGoogleTask(with(REMOTE_ID, "2"))) - insert(newGoogleTask(with(REMOTE_ID, "3"))) + insert(newCaldavTask(with(REMOTE_ID, "1"))) + insert(newCaldavTask(with(REMOTE_ID, "2"))) + insert(newCaldavTask(with(REMOTE_ID, "3"))) 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) + googleTaskDao.move(taskDao.fetch(three.task)!!, "calendar", 0, 0) + assertEquals(0L, getOrder("3")) + assertEquals(1L, getOrder("1")) + assertEquals(2L, getOrder("2")) } @Test fun moveToBottom() = runBlocking { - insert(newGoogleTask(with(REMOTE_ID, "1"))) - insert(newGoogleTask(with(REMOTE_ID, "2"))) - insert(newGoogleTask(with(REMOTE_ID, "3"))) + insert(newCaldavTask(with(REMOTE_ID, "1"))) + insert(newCaldavTask(with(REMOTE_ID, "2"))) + insert(newCaldavTask(with(REMOTE_ID, "3"))) 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) - } - - @Test - fun findChildrenInList() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(PARENT, 1L))) - assertEquals(listOf(2L), googleTaskDao.getChildren(listOf(1L, 2L))) + googleTaskDao.move(taskDao.fetch(one.task)!!, "calendar", 0, 2) + assertEquals(0L, getOrder("2")) + assertEquals(1L, getOrder("3")) + assertEquals(2L, getOrder("1")) } @Test fun dontAllowEmptyParent() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "1234"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "1234"))) googleTaskDao.updatePosition("1234", "", "0") @@ -169,7 +166,7 @@ class GoogleTaskDaoTests : InjectingTestCase() { @Test fun updatePositionWithNullParent() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "1234"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "1234"))) googleTaskDao.updatePosition("1234", null, "0") @@ -178,7 +175,7 @@ class GoogleTaskDaoTests : InjectingTestCase() { @Test fun updatePosition() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "1234"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "1234"))) googleTaskDao.updatePosition("1234", "abcd", "0") @@ -187,86 +184,84 @@ class GoogleTaskDaoTests : InjectingTestCase() { @Test fun updateParents() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "123"))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(REMOTE_PARENT, "123"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "123"))) + insert(newCaldavTask(with(TASK, 2), with(REMOTE_PARENT, "123"))) - googleTaskDao.updateParents() + caldavDao.updateParents() - assertEquals(1, googleTaskDao.getByTaskId(2)!!.parent) + assertEquals(1, taskDao.fetch(2)!!.parent) } @Test fun updateParentsByList() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "123"))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(REMOTE_PARENT, "123"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "123"))) + insert(newCaldavTask(with(TASK, 2), with(REMOTE_PARENT, "123"))) - googleTaskDao.updateParents("1") + caldavDao.updateParents("calendar") - assertEquals(1, googleTaskDao.getByTaskId(2)!!.parent) + assertEquals(1, taskDao.fetch(2)!!.parent) } @Test fun updateParentsMustMatchList() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "123"))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "2"), with(REMOTE_PARENT, "123"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "123"))) + insert(newCaldavTask(with(TASK, 2), with(CALENDAR, "2"), with(REMOTE_PARENT, "123"))) - googleTaskDao.updateParents() + caldavDao.updateParents() - assertEquals(0, googleTaskDao.getByTaskId(2)!!.parent) + assertEquals(0, taskDao.fetch(2)!!.parent) } @Test fun updateParentsByListMustMatchList() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, "123"))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "2"), with(REMOTE_PARENT, "123"))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, "123"))) + insert(newCaldavTask(with(TASK, 2), with(CALENDAR, "2"), with(REMOTE_PARENT, "123"))) - googleTaskDao.updateParents("2") + caldavDao.updateParents("2") - assertEquals(0, googleTaskDao.getByTaskId(2)!!.parent) + assertEquals(0, taskDao.fetch(2)!!.parent) } @Test fun ignoreEmptyStringWhenUpdatingParents() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, ""))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(REMOTE_ID, ""), with(REMOTE_PARENT, ""))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, ""))) + insert(newCaldavTask(with(TASK, 2), with(REMOTE_ID, ""), with(REMOTE_PARENT, ""))) - googleTaskDao.updateParents() + caldavDao.updateParents() - assertEquals(0, googleTaskDao.getByTaskId(2)!!.parent) + assertEquals(0, taskDao.fetch(2)!!.parent) } @Test fun ignoreEmptyStringWhenUpdatingParentsForList() = runBlocking { - insert(newGoogleTask(with(TASK, 1), with(LIST, "1"), with(REMOTE_ID, ""))) - insert(newGoogleTask(with(TASK, 2), with(LIST, "1"), with(REMOTE_ID, ""), with(REMOTE_PARENT, ""))) + insert(newCaldavTask(with(TASK, 1), with(REMOTE_ID, ""))) + insert(newCaldavTask(with(TASK, 2), with(REMOTE_ID, ""), with(REMOTE_PARENT, ""))) - googleTaskDao.updateParents("1") + caldavDao.updateParents("1") + + assertEquals(0, taskDao.fetch(2)!!.parent) + } - assertEquals(0, googleTaskDao.getByTaskId(2)!!.parent) + private suspend fun getOrder(remoteId: String): Long? { + return taskDao.fetch(googleTaskDao.getByRemoteId(remoteId)!!.task)?.order } - private suspend fun insertTop(googleTask: GoogleTask) { + private suspend fun insertTop(googleTask: CaldavTask) { insert(googleTask, true) } - private suspend fun insertBottom(googleTask: GoogleTask) { + private suspend fun insertBottom(googleTask: CaldavTask) { insert(googleTask, false) } - private suspend fun insert(googleTask: GoogleTask, top: Boolean = false) { + private suspend fun insert(googleTask: CaldavTask, top: Boolean = false) { val task = newTask() taskDao.createNew(task) googleTask.task = task.id - googleTaskDao.insertAndShift(googleTask, top) + googleTaskDao.insertAndShift(task, googleTask, top) } - private suspend fun getByRemoteId(remoteId: String): SubsetGoogleTask { - val googleTask = googleTaskDao.getByRemoteId(remoteId)!! - val result = SubsetGoogleTask() - result.gt_id = googleTask.id - result.gt_list_id = googleTask.listId - result.gt_order = googleTask.order - result.gt_parent = googleTask.parent - return result + private suspend fun getByRemoteId(remoteId: String): CaldavTask { + return googleTaskDao.getByRemoteId(remoteId)!! } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/tasks/data/GoogleTaskListDaoTest.kt b/app/src/androidTest/java/org/tasks/data/GoogleTaskListDaoTest.kt index dcb717b1b..5b11fa442 100644 --- a/app/src/androidTest/java/org/tasks/data/GoogleTaskListDaoTest.kt +++ b/app/src/androidTest/java/org/tasks/data/GoogleTaskListDaoTest.kt @@ -1,6 +1,5 @@ package org.tasks.data -import com.natpryce.makeiteasy.MakeItEasy.with import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.UninstallModules import kotlinx.coroutines.runBlocking @@ -8,46 +7,22 @@ import org.junit.Assert.* import org.junit.Test import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule -import org.tasks.makers.GoogleTaskListMaker.ACCOUNT -import org.tasks.makers.GoogleTaskListMaker.REMOTE_ID -import org.tasks.makers.GoogleTaskListMaker.newGoogleTaskList import javax.inject.Inject @UninstallModules(ProductionModule::class) @HiltAndroidTest class GoogleTaskListDaoTest : InjectingTestCase() { @Inject lateinit var googleTaskListDao: GoogleTaskListDao + @Inject lateinit var caldavDao: CaldavDao @Test fun noResultsForEmptyAccount() = runBlocking { - val account = GoogleTaskAccount() - account.account = "user@gmail.com" - googleTaskListDao.insert(account) + val account = CaldavAccount().apply { + uuid = "user@gmail.com" + username = "user@gmail.com" + } + caldavDao.insert(account) - assertTrue(googleTaskListDao.getGoogleTaskFilters(account.account!!).isEmpty()) - } - - @Test - fun findListWithNullAccount() = runBlocking { - val list = newGoogleTaskList(with(REMOTE_ID, "1234"), with(ACCOUNT, null as String?)) - list.id = googleTaskListDao.insert(list) - - assertEquals(list, googleTaskListDao.findExistingList("1234")) - } - - @Test - fun findListWithEmptyAccount() = runBlocking { - val list = newGoogleTaskList(with(REMOTE_ID, "1234"), with(ACCOUNT, "")) - list.id = googleTaskListDao.insert(list) - - assertEquals(list, googleTaskListDao.findExistingList("1234")) - } - - @Test - fun ignoreListWithAccount() = runBlocking { - val list = newGoogleTaskList(with(REMOTE_ID, "1234"), with(ACCOUNT, "user@gmail.com")) - googleTaskListDao.insert(list) - - assertNull(googleTaskListDao.findExistingList("1234")) + assertTrue(googleTaskListDao.getGoogleTaskFilters(account.username!!).isEmpty()) } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/tasks/data/ManualGoogleTaskQueryTest.kt b/app/src/androidTest/java/org/tasks/data/ManualGoogleTaskQueryTest.kt index e21581975..63f48dd28 100644 --- a/app/src/androidTest/java/org/tasks/data/ManualGoogleTaskQueryTest.kt +++ b/app/src/androidTest/java/org/tasks/data/ManualGoogleTaskQueryTest.kt @@ -14,32 +14,39 @@ import org.junit.Test import org.tasks.R import org.tasks.injection.InjectingTestCase import org.tasks.injection.ProductionModule -import org.tasks.makers.GoogleTaskListMaker.REMOTE_ID -import org.tasks.makers.GoogleTaskListMaker.newGoogleTaskList -import org.tasks.makers.GoogleTaskMaker.LIST -import org.tasks.makers.GoogleTaskMaker.ORDER -import org.tasks.makers.GoogleTaskMaker.PARENT -import org.tasks.makers.GoogleTaskMaker.TASK -import org.tasks.makers.GoogleTaskMaker.newGoogleTask +import org.tasks.makers.CaldavAccountMaker.newCaldavAccount +import org.tasks.makers.CaldavCalendarMaker.UUID +import org.tasks.makers.CaldavCalendarMaker.newCaldavCalendar +import org.tasks.makers.CaldavTaskMaker.CALENDAR +import org.tasks.makers.CaldavTaskMaker.TASK +import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.TaskMaker import org.tasks.makers.TaskMaker.ID -import org.tasks.makers.TaskMaker.UUID +import org.tasks.makers.TaskMaker.ORDER +import org.tasks.makers.TaskMaker.PARENT import org.tasks.preferences.Preferences import javax.inject.Inject @UninstallModules(ProductionModule::class) @HiltAndroidTest class ManualGoogleTaskQueryTest : InjectingTestCase() { + @Inject lateinit var caldavDao: CaldavDao @Inject lateinit var googleTaskDao: GoogleTaskDao @Inject lateinit var taskDao: TaskDao @Inject lateinit var preferences: Preferences - private val filter: GtasksFilter = GtasksFilter(newGoogleTaskList(with(REMOTE_ID, "1234"))) + private lateinit var filter: GtasksFilter @Before override fun setUp() { super.setUp() preferences.clear() preferences.setBoolean(R.string.p_manual_sort, true) + val calendar = newCaldavCalendar(with(UUID, "1234")) + runBlocking { + caldavDao.insert(newCaldavAccount()) + caldavDao.insert(calendar) + } + filter = GtasksFilter(calendar) } @Test @@ -100,8 +107,13 @@ class ManualGoogleTaskQueryTest : InjectingTestCase() { } private suspend fun newTask(id: Long, order: Long, parent: Long = 0) { - taskDao.insert(TaskMaker.newTask(with(ID, id), with(UUID, UUIDHelper.newUUID()))) - googleTaskDao.insert(newGoogleTask(with(LIST, filter.list.remoteId), with(TASK, id), with(PARENT, parent), with(ORDER, order))) + taskDao.insert(TaskMaker.newTask( + with(ID, id), + with(TaskMaker.UUID, UUIDHelper.newUUID()), + with(ORDER, order), + with(PARENT, parent), + )) + googleTaskDao.insert(newCaldavTask(with(CALENDAR, filter.list.uuid), with(TASK, id))) } private suspend fun query(): List = taskDao.fetchTasks { diff --git a/app/src/androidTest/java/org/tasks/opentasks/OpenTasksPropertiesTests.kt b/app/src/androidTest/java/org/tasks/opentasks/OpenTasksPropertiesTests.kt index 3e0429ce9..d8a8e6926 100644 --- a/app/src/androidTest/java/org/tasks/opentasks/OpenTasksPropertiesTests.kt +++ b/app/src/androidTest/java/org/tasks/opentasks/OpenTasksPropertiesTests.kt @@ -22,7 +22,6 @@ import org.tasks.injection.ProductionModule import org.tasks.makers.CaldavTaskMaker import org.tasks.makers.CaldavTaskMaker.CALENDAR import org.tasks.makers.CaldavTaskMaker.REMOTE_ID -import org.tasks.makers.CaldavTaskMaker.REMOTE_ORDER import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.TagDataMaker.NAME import org.tasks.makers.TagDataMaker.newTagData @@ -31,6 +30,7 @@ import org.tasks.makers.TagMaker.TASK import org.tasks.makers.TagMaker.newTag import org.tasks.makers.TaskMaker import org.tasks.makers.TaskMaker.COLLAPSED +import org.tasks.makers.TaskMaker.ORDER import org.tasks.makers.TaskMaker.newTask import org.tasks.time.DateTime import java.util.* @@ -129,20 +129,18 @@ class OpenTasksPropertiesTests : OpenTasksTest() { synchronizer.sync() - assertEquals( - 633734058L, - caldavDao.getTaskByRemoteId(list.uuid!!, "3076145036806467726")?.order - ) + val task = caldavDao.getTaskByRemoteId(list.uuid!!, "3076145036806467726")!!.task + assertEquals(633734058L, taskDao.fetch(task)?.order) } @Test fun pushOrder() = runBlocking { val (listId, list) = openTaskDao.insertList() - val task = newTask().apply { taskDao.createNew(this) } + val task = newTask(with(ORDER, 5678L)) + taskDao.createNew(task) caldavDao.insert(newCaldavTask( with(CALENDAR, list.uuid), with(REMOTE_ID, "1234"), - with(REMOTE_ORDER, 5678L), with(CaldavTaskMaker.TASK, task.id) )) diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt index cb6f2fa5a..d5972c495 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt @@ -980,7 +980,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL companion object { const val TAGS_METADATA_JOIN = "for_tags" // $NON-NLS-1$ - const val GTASK_METADATA_JOIN = "googletask" // $NON-NLS-1$ const val CALDAV_METADATA_JOIN = "for_caldav" // $NON-NLS-1$ const val ACTION_RELOAD = "action_reload" const val ACTION_DELETED = "action_deleted" diff --git a/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt index 7bd3d6eec..e8be88b56 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt @@ -48,14 +48,14 @@ class CaldavManualSortTaskAdapter internal constructor( private suspend fun changeParent(task: TaskContainer, newParent: Long) { val caldavTask = task.getCaldavTask() if (newParent == 0L) { - caldavTask.cd_remote_parent = "" + caldavTask.remoteParent = "" task.parent = 0 } else { val parentTask = caldavDao.getTask(newParent) ?: return - caldavTask.cd_remote_parent = parentTask.remoteId + caldavTask.remoteParent = parentTask.remoteId task.parent = newParent } - caldavDao.update(caldavTask.cd_id, caldavTask.cd_remote_parent) + caldavDao.update(caldavTask.id, caldavTask.remoteParent) taskDao.save(task.getTask(), null) } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.kt b/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.kt index d228447a2..7f80e8212 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.kt @@ -104,7 +104,7 @@ class FilterViewHolder internal constructor( } return when (filter) { is TagFilter -> R.drawable.ic_outline_label_24px - is GtasksFilter -> R.drawable.ic_list_24px + is GtasksFilter, is CaldavFilter -> R.drawable.ic_list_24px is CustomFilter -> R.drawable.ic_outline_filter_list_24px is PlaceFilter -> R.drawable.ic_outline_place_24px diff --git a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt index 35d03c1cd..4df89c343 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt @@ -15,29 +15,83 @@ class GoogleTaskManualSortAdapter internal constructor( override suspend fun moved(from: Int, to: Int, indent: Int) { val task = getTask(from) - val googleTask = task.googleTask + val googleTask = task.caldavTask val previous = if (to > 0) getTask(to - 1) else null + val list = googleTask.calendar!! if (previous == null) { - googleTaskDao.move(googleTask, 0, 0) + googleTaskDao.move( + task = task.task, + list = list, + newParent = 0, + newPosition = 0, + ) } else if (to == count || to <= from) { when { - indent == 0 -> googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + if (to == count) 0 else 1) - previous.hasParent() && previous.parent == googleTask.parent -> googleTaskDao.move(googleTask, previous.parent, previous.getSecondarySort() + if (to == count) 0 else 1) - previous.hasParent() -> googleTaskDao.move(googleTask, previous.parent, previous.getSecondarySort() + 1) - else -> googleTaskDao.move(googleTask, previous.id, 0) + indent == 0 -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = 0, + newPosition = previous.getPrimarySort() + if (to == count) 0 else 1, + ) + previous.hasParent() && previous.parent == task.parent -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.getSecondarySort() + if (to == count) 0 else 1, + ) + previous.hasParent() -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.getSecondarySort() + 1, + ) + else -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.id, + newPosition = 0, + ) } } else { when { - indent == 0 -> googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + if (task.hasParent()) 1 else 0) - previous.hasParent() && previous.parent == googleTask.parent -> googleTaskDao.move(googleTask, previous.parent, previous.getSecondarySort()) - previous.hasParent() -> googleTaskDao.move(googleTask, previous.parent, previous.getSecondarySort() + 1) - else -> googleTaskDao.move(googleTask, previous.id, 0) + indent == 0 -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = 0, + newPosition = previous.getPrimarySort() + if (task.hasParent()) 1 else 0, + ) + previous.hasParent() && previous.parent == task.parent -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.getSecondarySort(), + ) + previous.hasParent() -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.getSecondarySort() + 1, + ) + else -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.id, + newPosition = 0, + ) } } taskDao.touch(task.id) localBroadcastManager.broadcastRefresh() if (BuildConfig.DEBUG) { - googleTaskDao.validateSorting(task.googleTaskList!!) + googleTaskDao.validateSorting(task.caldav!!) } } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt index f2dd014e9..59f05377e 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import org.tasks.LocalBroadcastManager import org.tasks.data.CaldavDao -import org.tasks.data.GoogleTaskDao import org.tasks.dialogs.NewFilterDialog import org.tasks.filters.NavigationDrawerSubheader import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.* @@ -19,7 +18,6 @@ import javax.inject.Inject class SubheaderClickHandler @Inject constructor( private val activity: Activity, private val preferences: Preferences, - private val googleTaskDao: GoogleTaskDao, private val caldavDao: CaldavDao, private val localBroadcastManager: LocalBroadcastManager, ): SubheaderViewHolder.ClickHandler { @@ -28,8 +26,10 @@ class SubheaderClickHandler @Inject constructor( val collapsed = !subheader.isCollapsed when (subheader.subheaderType) { PREFERENCE -> preferences.setBoolean(subheader.id.toInt(), collapsed) - GOOGLE_TASKS -> googleTaskDao.setCollapsed(subheader.id, collapsed) - CALDAV, TASKS, ETESYNC -> caldavDao.setCollapsed(subheader.id, collapsed) + GOOGLE_TASKS, + CALDAV, + TASKS, + ETESYNC -> caldavDao.setCollapsed(subheader.id, collapsed) } localBroadcastManager.broadcastRefreshList() } diff --git a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt index e9f48a8c2..0676ace39 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt @@ -13,10 +13,7 @@ import org.tasks.BuildConfig import org.tasks.LocalBroadcastManager import org.tasks.data.CaldavDao import org.tasks.data.CaldavTask -import org.tasks.data.GoogleTask import org.tasks.data.GoogleTaskDao -import org.tasks.data.SubsetCaldav -import org.tasks.data.SubsetGoogleTask import org.tasks.data.TaskContainer import org.tasks.date.DateTimeUtils.toAppleEpoch import org.tasks.date.DateTimeUtils.toDateTime @@ -135,21 +132,15 @@ open class TaskAdapter( open suspend fun moved(from: Int, to: Int, indent: Int) { val task = getTask(from) val newParent = findParent(indent, to) - if (newParent?.id ?: 0 == task.parent) { + if ((newParent?.id ?: 0) == task.parent) { if (indent == 0) { changeSortGroup(task, if (from < to) to - 1 else to) } return } else if (newParent != null) { - when { - task.isGoogleTask -> if (task.googleTaskList != newParent.googleTaskList) { - googleTaskDao.markDeleted(task.id) - task.googletask = null - } - task.isCaldavTask -> if (task.caldav != newParent.caldav) { - caldavDao.markDeleted(listOf(task.id)) - task.caldavTask = null - } + if (task.caldav != newParent.caldav) { + caldavDao.markDeleted(listOf(task.id)) + task.caldavTask = null } } when { @@ -250,22 +241,18 @@ open class TaskAdapter( } private suspend fun changeGoogleTaskParent(task: TaskContainer, newParent: TaskContainer?) { - val list = newParent?.googleTaskList ?: task.googleTaskList!! - if (newParent == null || task.googleTaskList == newParent.googleTaskList) { + val list = newParent?.caldav ?: task.caldav!! + if (newParent == null || task.caldav == newParent.caldav) { googleTaskDao.move( - task.googleTask, - newParent?.id ?: 0, - if (newTasksOnTop) 0 else googleTaskDao.getBottom(list, newParent?.id ?: 0)) + task.task, + list, + newParent?.id ?: 0, + if (newTasksOnTop) 0 else googleTaskDao.getBottom(list, newParent?.id ?: 0) + ) } else { - val googleTask = GoogleTask(task.id, list) - googleTask.parent = newParent.id - googleTaskDao.insertAndShift(googleTask, newTasksOnTop) - task.googletask = SubsetGoogleTask().apply { - gt_id = googleTask.id - gt_list_id = googleTask.listId - gt_order = googleTask.order - gt_parent = googleTask.parent - } + task.parent = newParent.id + task.caldavTask = CaldavTask(task.id, list, remoteId = null) + googleTaskDao.insertAndShift(task.task, task.caldavTask, newTasksOnTop) } taskDao.touch(task.id) if (BuildConfig.DEBUG) { @@ -275,16 +262,19 @@ open class TaskAdapter( private suspend fun changeCaldavParent(task: TaskContainer, newParent: TaskContainer?) { val list = newParent?.caldav ?: task.caldav!! - val caldavTask = task.getCaldavTask() ?: SubsetCaldav() + val caldavTask = task.getCaldavTask() ?: CaldavTask( + task.id, + list, + ) val newParentId = newParent?.id ?: 0 if (newParentId == 0L) { - caldavTask.cd_remote_parent = "" + caldavTask.remoteParent = "" } else { val parentTask = caldavDao.getTask(newParentId) ?: return - caldavTask.cd_calendar = list - caldavTask.cd_remote_parent = parentTask.remoteId + caldavTask.calendar = list + caldavTask.remoteParent = parentTask.remoteId } - caldavTask.cd_order = if (newTasksOnTop) { + task.task.order = if (newTasksOnTop) { caldavDao.findFirstTask(list, newParentId) ?.takeIf { task.creationDate.toAppleEpoch() >= it} ?.minus(1) @@ -293,15 +283,15 @@ open class TaskAdapter( ?.takeIf { task.creationDate.toAppleEpoch() <= it } ?.plus(1) } - if (caldavTask.cd_id == 0L) { + if (caldavTask.id == 0L) { val newTask = CaldavTask(task.id, list) - newTask.order = caldavTask.cd_order - newTask.remoteParent = caldavTask.cd_remote_parent - caldavTask.cd_id = caldavDao.insert(newTask) + newTask.remoteParent = caldavTask.remoteParent + caldavTask.id = caldavDao.insert(newTask) task.caldavTask = caldavTask } else { caldavDao.update(caldavTask) } + taskDao.setOrder(task.id, task.task.order) taskDao.setParent(newParentId, listOf(task.id)) taskDao.touch(task.id) localBroadcastManager.broadcastRefresh() diff --git a/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java b/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java index 5a9f780ad..96c7227e2 100644 --- a/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java +++ b/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java @@ -2,19 +2,24 @@ package com.todoroo.astrid.api; import android.os.Parcel; import android.os.Parcelable; + import androidx.annotation.NonNull; + import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.astrid.data.Task; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; + import org.tasks.R; +import org.tasks.data.CaldavCalendar; +import org.tasks.data.CaldavTask; import org.tasks.data.GoogleTask; -import org.tasks.data.GoogleTaskList; import org.tasks.data.TaskDao; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + public class GtasksFilter extends Filter { /** Parcelable Creator Object */ @@ -36,14 +41,14 @@ public class GtasksFilter extends Filter { } }; - private GoogleTaskList list; + private CaldavCalendar list; private GtasksFilter() { super(); } - public GtasksFilter(GoogleTaskList list) { - super(list.getTitle(), getQueryTemplate(list), getValuesForNewTasks(list)); + public GtasksFilter(CaldavCalendar list) { + super(list.getName(), getQueryTemplate(list), getValuesForNewTasks(list)); this.list = list; id = list.getId(); tint = list.getColor(); @@ -51,31 +56,27 @@ public class GtasksFilter extends Filter { order = list.getOrder(); } - private static QueryTemplate getQueryTemplate(GoogleTaskList list) { + private static QueryTemplate getQueryTemplate(CaldavCalendar list) { return new QueryTemplate() - .join(Join.left(GoogleTask.TABLE, Task.ID.eq(GoogleTask.TASK))) + .join(Join.left(CaldavTask.TABLE, Task.ID.eq(CaldavTask.TASK))) .where( Criterion.and( TaskDao.TaskCriteria.activeAndVisible(), - GoogleTask.DELETED.eq(0), - GoogleTask.LIST.eq(list.getRemoteId()))); + CaldavTask.DELETED.eq(0), + CaldavTask.CALENDAR.eq(list.getUuid()))); } - private static Map getValuesForNewTasks(GoogleTaskList list) { + private static Map getValuesForNewTasks(CaldavCalendar list) { Map values = new HashMap<>(); - values.put(GoogleTask.KEY, list.getRemoteId()); + values.put(GoogleTask.KEY, list.getUuid()); return values; } - public long getStoreId() { - return list.getId(); - } - public String getAccount() { return list.getAccount(); } - public GoogleTaskList getList() { + public CaldavCalendar getList() { return list; } @@ -98,7 +99,7 @@ public class GtasksFilter extends Filter { } public String getRemoteId() { - return list.getRemoteId(); + return list.getUuid(); } @Override diff --git a/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt b/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt index 9dc4b4469..b7c7ea2ba 100644 --- a/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt @@ -61,11 +61,6 @@ class SearchFilter : Filter { .from(CaldavTask.TABLE) .join(Join.inner(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR))) .where(CaldavCalendar.NAME.like(matcher))), - Task.ID.`in`( - Query.select(GoogleTask.TASK) - .from(GoogleTask.TABLE) - .join(Join.inner(GoogleTaskList.TABLE, GoogleTaskList.REMOTE_ID.eq(GoogleTask.LIST))) - .where(GoogleTaskList.NAME.like(matcher))) ))) } } diff --git a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt index 8ab12a976..81bca780c 100644 --- a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt +++ b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt @@ -101,9 +101,8 @@ class BuiltInFilterExposer @Inject constructor( Filter( "No list", QueryTemplate() - .join(Join.left(GoogleTask.TABLE, GoogleTask.TASK.eq(Task.ID))) .join(Join.left(CaldavTask.TABLE, CaldavTask.TASK.eq(Task.ID))) - .where(and(GoogleTask.ID.eq(null), CaldavTask.ID.eq(null))) + .where(CaldavTask.ID.eq(null)) ).apply { icon = R.drawable.ic_outline_cloud_off_24px } @@ -116,13 +115,9 @@ class BuiltInFilterExposer @Inject constructor( Filter( "Missing list", QueryTemplate() - .join(Join.left(GoogleTask.TABLE, GoogleTask.TASK.eq(Task.ID))) .join(Join.left(CaldavTask.TABLE, CaldavTask.TASK.eq(Task.ID))) - .join(Join.left(GoogleTaskList.TABLE, GoogleTaskList.REMOTE_ID.eq(GoogleTask.LIST))) .join(Join.left(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR))) - .where(or( - and(GoogleTask.ID.gt(0), GoogleTaskList.REMOTE_ID.eq(null)), - and(CaldavTask.ID.gt(0), CaldavCalendar.UUID.eq(null)))) + .where(and(CaldavTask.ID.gt(0), CaldavCalendar.UUID.eq(null))) ).apply { icon = R.drawable.ic_outline_cloud_off_24px } @@ -131,15 +126,10 @@ class BuiltInFilterExposer @Inject constructor( Filter( "Missing account", QueryTemplate() - .join(Join.left(GoogleTask.TABLE, and(GoogleTask.TASK.eq(Task.ID)))) .join(Join.left(CaldavTask.TABLE, and(CaldavTask.TASK.eq(Task.ID)))) - .join(Join.left(GoogleTaskList.TABLE, GoogleTaskList.REMOTE_ID.eq(GoogleTask.LIST))) .join(Join.left(CaldavCalendar.TABLE, CaldavCalendar.UUID.eq(CaldavTask.CALENDAR))) - .join(Join.left(GoogleTaskAccount.TABLE, GoogleTaskAccount.ACCOUNT.eq(GoogleTaskList.ACCOUNT))) .join(Join.left(CaldavAccount.TABLE, CaldavAccount.UUID.eq(CaldavCalendar.ACCOUNT))) - .where(or( - and(GoogleTask.ID.gt(0), GoogleTaskAccount.ACCOUNT.eq(null)), - and(CaldavTask.ID.gt(0), CaldavAccount.UUID.eq(null)))) + .where(and(CaldavTask.ID.gt(0), CaldavAccount.UUID.eq(null))) ).apply { icon = R.drawable.ic_outline_cloud_off_24px } diff --git a/app/src/main/java/com/todoroo/astrid/core/SortHelper.java b/app/src/main/java/com/todoroo/astrid/core/SortHelper.java index c298076e1..5017db4fd 100644 --- a/app/src/main/java/com/todoroo/astrid/core/SortHelper.java +++ b/app/src/main/java/com/todoroo/astrid/core/SortHelper.java @@ -39,7 +39,7 @@ public class SortHelper { public static final long APPLE_EPOCH = 978307200000L; // 1/1/2001 GMT @SuppressLint("DefaultLocale") public static final String CALDAV_ORDER_COLUMN = - String.format("IFNULL(caldav_tasks.cd_order, (tasks.created - %d) / 1000)", APPLE_EPOCH); + String.format("IFNULL(tasks.`order`, (tasks.created - %d) / 1000)", APPLE_EPOCH); private static final String ADJUSTED_DUE_DATE = "(CASE WHEN (dueDate / 1000) % 60 > 0 THEN dueDate ELSE (dueDate + 43140000) END)"; @@ -176,7 +176,7 @@ public class SortHelper { select = "tasks.created AS sort_created"; break; case SORT_GTASKS: - select = "google_tasks.gt_order AS sort_manual"; + select = "tasks.`order` AS sort_manual"; break; case SORT_CALDAV: select = CALDAV_ORDER_COLUMN + " AS sort_manual"; diff --git a/app/src/main/java/com/todoroo/astrid/dao/Database.kt b/app/src/main/java/com/todoroo/astrid/dao/Database.kt index cd20c7ae9..5333940be 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/Database.kt +++ b/app/src/main/java/com/todoroo/astrid/dao/Database.kt @@ -22,13 +22,10 @@ import org.tasks.notifications.NotificationDao Place::class, Geofence::class, Tag::class, - GoogleTask::class, Filter::class, - GoogleTaskList::class, CaldavCalendar::class, CaldavTask::class, CaldavAccount::class, - GoogleTaskAccount::class, Principal::class, PrincipalAccess::class, Attachment::class, @@ -36,7 +33,7 @@ import org.tasks.notifications.NotificationDao autoMigrations = [ AutoMigration(from = 83, to = 84, spec = Migrations.AutoMigrate83to84::class), ], - version = 87 + version = 88 ) abstract class Database : RoomDatabase() { abstract fun notificationDao(): NotificationDao diff --git a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt index 3d11c28c2..5390fdad8 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt +++ b/app/src/main/java/com/todoroo/astrid/dao/TaskDao.kt @@ -69,6 +69,8 @@ class TaskDao @Inject constructor( syncAdapters.sync() } + suspend fun setOrder(taskId: Long, order: Long?) = taskDao.setOrder(taskId, order) + suspend fun setParent(parent: Long, tasks: List) = taskDao.setParent(parent, tasks) suspend fun getChildren(ids: List) = taskDao.getChildren(ids) diff --git a/app/src/main/java/com/todoroo/astrid/data/Task.kt b/app/src/main/java/com/todoroo/astrid/data/Task.kt index 13de3323c..5a67dcd81 100644 --- a/app/src/main/java/com/todoroo/astrid/data/Task.kt +++ b/app/src/main/java/com/todoroo/astrid/data/Task.kt @@ -103,6 +103,9 @@ class Task : Parcelable { @Transient var parent = 0L + @ColumnInfo(name = "order") + var order: Long? = null + @ColumnInfo(name = "read_only", defaultValue = "0") var readOnly: Boolean = false @@ -136,6 +139,7 @@ class Task : Parcelable { isCollapsed = ParcelCompat.readBoolean(parcel) parent = parcel.readLong() readOnly = ParcelCompat.readBoolean(parcel) + order = parcel.readLong() } var uuid: String @@ -270,6 +274,7 @@ class Task : Parcelable { ParcelCompat.writeBoolean(dest, isCollapsed) dest.writeLong(parent) ParcelCompat.writeBoolean(dest, readOnly) + dest.writeLong(order ?: 0) } fun insignificantChange(task: Task?): Boolean { @@ -295,6 +300,7 @@ class Task : Parcelable { && calendarURI == task.calendarURI && parent == task.parent && remoteId == task.remoteId + && order == task.order } fun googleTaskUpToDate(original: Task?): Boolean { @@ -309,6 +315,7 @@ class Task : Parcelable { && deletionDate == original.deletionDate && parent == original.parent && notes == original.notes + && order == original.order } fun caldavUpToDate(original: Task?): Boolean { @@ -327,6 +334,7 @@ class Task : Parcelable { && recurrence == original.recurrence && parent == original.parent && isCollapsed == original.isCollapsed + && order == original.order } val isSaved: Boolean @@ -414,6 +422,7 @@ class Task : Parcelable { if (parent != other.parent) return false if (transitoryData != other.transitoryData) return false if (readOnly != other.readOnly) return false + if (order != other.order) return false return true } @@ -441,11 +450,12 @@ class Task : Parcelable { result = 31 * result + parent.hashCode() result = 31 * result + (transitoryData?.hashCode() ?: 0) result = 31 * result + readOnly.hashCode() + result = 31 * result + order.hashCode() return result } override fun toString(): String { - return "Task(id=$id, title=$title, priority=$priority, dueDate=$dueDate, hideUntil=$hideUntil, creationDate=$creationDate, modificationDate=$modificationDate, completionDate=$completionDate, deletionDate=$deletionDate, notes=$notes, estimatedSeconds=$estimatedSeconds, elapsedSeconds=$elapsedSeconds, timerStart=$timerStart, ringFlags=$ringFlags, reminderLast=$reminderLast, recurrence=$recurrence, calendarURI=$calendarURI, remoteId='$remoteId', isCollapsed=$isCollapsed, parent=$parent, transitoryData=$transitoryData, readOnly=$readOnly)" + return "Task(id=$id, title=$title, priority=$priority, dueDate=$dueDate, hideUntil=$hideUntil, creationDate=$creationDate, modificationDate=$modificationDate, completionDate=$completionDate, deletionDate=$deletionDate, notes=$notes, estimatedSeconds=$estimatedSeconds, elapsedSeconds=$elapsedSeconds, timerStart=$timerStart, ringFlags=$ringFlags, reminderLast=$reminderLast, recurrence=$recurrence, calendarURI=$calendarURI, remoteId='$remoteId', isCollapsed=$isCollapsed, parent=$parent, transitoryData=$transitoryData, readOnly=$readOnly, order=$order)" } @Retention(AnnotationRetention.SOURCE) diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/GtasksListService.kt b/app/src/main/java/com/todoroo/astrid/gtasks/GtasksListService.kt index ab6d288da..1a8260104 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/GtasksListService.kt +++ b/app/src/main/java/com/todoroo/astrid/gtasks/GtasksListService.kt @@ -8,25 +8,24 @@ package com.todoroo.astrid.gtasks import com.google.api.services.tasks.model.TaskList import com.todoroo.astrid.service.TaskDeleter import org.tasks.LocalBroadcastManager -import org.tasks.data.GoogleTaskAccount -import org.tasks.data.GoogleTaskList +import org.tasks.data.CaldavAccount +import org.tasks.data.CaldavCalendar import org.tasks.data.GoogleTaskListDao -import timber.log.Timber -import java.util.* import javax.inject.Inject class GtasksListService @Inject constructor( private val googleTaskListDao: GoogleTaskListDao, private val taskDeleter: TaskDeleter, - private val localBroadcastManager: LocalBroadcastManager) { + private val localBroadcastManager: LocalBroadcastManager, +) { /** * Reads in remote list information and updates local list objects. * * @param remoteLists remote information about your lists */ - suspend fun updateLists(account: GoogleTaskAccount, remoteLists: List) { - val lists = googleTaskListDao.getLists(account.account!!) + suspend fun updateLists(account: CaldavAccount, remoteLists: List) { + val lists = googleTaskListDao.getLists(account.uuid!!) val previousLists: MutableSet = HashSet() for (list in lists) { previousLists.add(list.id) @@ -34,27 +33,20 @@ class GtasksListService @Inject constructor( for (i in remoteLists.indices) { val remote = remoteLists[i] val id = remote.id - var local: GoogleTaskList? = null + var local: CaldavCalendar? = null for (list in lists) { - if (list.remoteId == id) { + if (list.uuid == id) { local = list break } } - val title = remote.title if (local == null) { - val byRemoteId = googleTaskListDao.findExistingList(id) - if (byRemoteId != null) { - byRemoteId.account = account.account - local = byRemoteId - } else { - Timber.d("Adding new gtask list %s", title) - local = GoogleTaskList() - local.account = account.account - local.remoteId = id - } + local = CaldavCalendar( + account = account.uuid, + uuid = id, + ) } - local.title = title + local.name = remote.title googleTaskListDao.insertOrReplace(local) previousLists.remove(local.id) } diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.kt b/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.kt index fe3e74d48..2ac9fdba2 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.kt +++ b/app/src/main/java/com/todoroo/astrid/gtasks/auth/GtasksLoginActivity.kt @@ -20,7 +20,9 @@ import org.tasks.PermissionUtil.verifyPermissions import org.tasks.R import org.tasks.analytics.Constants import org.tasks.analytics.Firebase -import org.tasks.data.GoogleTaskAccount +import org.tasks.data.CaldavAccount +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS +import org.tasks.data.CaldavDao import org.tasks.data.GoogleTaskListDao import org.tasks.dialogs.DialogBuilder import org.tasks.gtasks.GoogleAccountManager @@ -39,6 +41,7 @@ import javax.inject.Inject class GtasksLoginActivity : InjectingAppCompatActivity() { @Inject lateinit var dialogBuilder: DialogBuilder @Inject lateinit var googleAccountManager: GoogleAccountManager + @Inject lateinit var caldavDao: CaldavDao @Inject lateinit var googleTaskListDao: GoogleTaskListDao @Inject lateinit var permissionRequestor: ActivityPermissionRequestor @Inject lateinit var firebase: Firebase @@ -71,18 +74,21 @@ class GtasksLoginActivity : InjectingAppCompatActivity() { startActivity(intent) } else { withContext(NonCancellable) { - var account = googleTaskListDao.getAccount(accountName) + var account = caldavDao.getAccount(TYPE_GOOGLE_TASKS, accountName) if (account == null) { - account = GoogleTaskAccount() - account.account = accountName - googleTaskListDao.insert(account) + account = CaldavAccount() + account.accountType = TYPE_GOOGLE_TASKS + account.uuid = accountName + account.name = accountName + account.username = accountName + caldavDao.insert(account) firebase.logEvent( R.string.event_sync_add_account, R.string.param_type to Constants.SYNC_TYPE_GOOGLE_TASKS ) } else { account.error = "" - googleTaskListDao.update(account) + caldavDao.update(account) googleTaskListDao.resetLastSync(accountName) } } diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt b/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt index d52fab2a1..db7b62247 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskCompleter.kt @@ -35,11 +35,9 @@ class TaskCompleter @Inject internal constructor( ArrayList() .apply { if (includeChildren) { - addAll(googleTaskDao.getChildTasks(item.id)) addAll(taskDao.getChildren(item.id).let { taskDao.fetch(it) }) } if (!completed) { - add(googleTaskDao.getParentTask(item.id)) addAll(taskDao.getParents(item.id).let { taskDao.fetch(it) }) } add(item) diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt b/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt index 2abd10ab6..b0712bdf0 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt @@ -55,7 +55,10 @@ class TaskCreator @Inject constructor( val addToTop = preferences.addTasksToTop() if (task.hasTransitory(GoogleTask.KEY)) { googleTaskDao.insertAndShift( - GoogleTask(task.id, task.getTransitory(GoogleTask.KEY)!!), addToTop) + task, + CaldavTask(task.id, task.getTransitory(GoogleTask.KEY)!!, remoteId = null), + addToTop + ) } else if (task.hasTransitory(CaldavTask.KEY)) { caldavDao.insert( task, CaldavTask(task.id, task.getTransitory(CaldavTask.KEY)), addToTop) @@ -63,7 +66,10 @@ class TaskCreator @Inject constructor( val remoteList = defaultFilterProvider.getDefaultList() if (remoteList is GtasksFilter) { googleTaskDao.insertAndShift( - GoogleTask(task.id, remoteList.remoteId), addToTop) + task, + CaldavTask(task.id, remoteList.remoteId, remoteId = null), + addToTop + ) } else if (remoteList is CaldavFilter) { caldavDao.insert( task, CaldavTask(task.id, remoteList.uuid), addToTop) diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt index 9e04be686..93ac31014 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.kt @@ -2,7 +2,6 @@ package com.todoroo.astrid.service import com.todoroo.astrid.api.Filter import com.todoroo.astrid.data.Task -import kotlinx.coroutines.runBlocking import org.tasks.LocalBroadcastManager import org.tasks.caldav.VtodoCache import org.tasks.data.* @@ -29,7 +28,6 @@ class TaskDeleter @Inject constructor( suspend fun markDeleted(taskIds: List): List { val ids = taskIds .toSet() - .plus(taskIds.chunkedMap(googleTaskDao::getChildren)) .plus(taskIds.chunkedMap(taskDao::getChildren)) .let { taskDao.fetch(it.toList()) } .filterNot { it.readOnly } @@ -51,7 +49,6 @@ class TaskDeleter @Inject constructor( .map(TaskContainer::getId) .toMutableList() completed.removeAll(deletionDao.hasRecurringAncestors(completed)) - completed.removeAll(googleTaskDao.hasRecurringParent(completed)) markDeleted(completed) return completed.size } @@ -66,18 +63,6 @@ class TaskDeleter @Inject constructor( localBroadcastManager.broadcastRefresh() } - fun delete(list: GoogleTaskList) = runBlocking { - val tasks = deletionDao.delete(list) - delete(tasks) - localBroadcastManager.broadcastRefreshList() - } - - suspend fun delete(list: GoogleTaskAccount) { - val tasks = deletionDao.delete(list) - delete(tasks) - localBroadcastManager.broadcastRefreshList() - } - suspend fun delete(list: CaldavCalendar) { vtodoCache.delete(list) val tasks = deletionDao.delete(list) diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskDuplicator.kt b/app/src/main/java/com/todoroo/astrid/service/TaskDuplicator.kt index a112c1c23..31e5cd166 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskDuplicator.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskDuplicator.kt @@ -28,8 +28,7 @@ class TaskDuplicator @Inject constructor( return taskIds .dbchunk() .flatMap { - it.minus(googleTaskDao.getChildren(it).toSet()) - .minus(taskDao.getChildren(it).toSet()) + it.minus(taskDao.getChildren(it).toSet()) } .let { taskDao.fetch(it) } .filterNot { it.readOnly } @@ -58,7 +57,11 @@ class TaskDuplicator @Inject constructor( val googleTask = googleTaskDao.getByTaskId(originalId) val addToTop = preferences.addTasksToTop() if (googleTask != null) { - googleTaskDao.insertAndShift(GoogleTask(clone.id, googleTask.listId!!), addToTop) + googleTaskDao.insertAndShift( + clone, + CaldavTask(clone.id, googleTask.calendar!!, remoteId = null), + addToTop + ) } val caldavTask = caldavDao.getTask(originalId) if (caldavTask != null) { diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskMover.kt b/app/src/main/java/com/todoroo/astrid/service/TaskMover.kt index 3c012b1f7..da8a20252 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskMover.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskMover.kt @@ -46,11 +46,8 @@ class TaskMover @Inject constructor( suspend fun move(ids: List, selectedList: Filter) { val tasks = ids .dbchunk() - .flatMap { - it.minus(googleTaskDao.getChildren(it).toSet()) - .minus(taskDao.getChildren(it).toSet()) - } - .let { taskDao.fetch(it) } + .flatMap { taskDao.getChildren(it) } + .let { taskDao.fetch(ids.minus(it.toSet())) } .filterNot { it.readOnly } val taskIds = tasks.map { it.id } taskDao.setParent(0, ids.intersect(taskIds.toSet()).toList()) @@ -82,32 +79,30 @@ class TaskMover @Inject constructor( moveLocalTask(task, selectedList) } - private suspend fun moveGoogleTask(task: Task, googleTask: GoogleTask, selected: Filter) { - if (selected is GtasksFilter && googleTask.listId == selected.remoteId) { + private suspend fun moveGoogleTask(task: Task, googleTask: CaldavTask, selected: Filter) { + if (selected is GtasksFilter && googleTask.calendar == selected.remoteId) { return } - val id = googleTask.task - val children = googleTaskDao.getChildren(id) - val childIds = children.map(GoogleTask::task) - googleTaskDao.markDeleted(id, DateUtilities.now()) + val id = task.id + val children = taskDao.getChildren(id) + caldavDao.markDeleted(children + id, DateUtilities.now()) when(selected) { is GtasksFilter -> { val listId = selected.remoteId - googleTaskDao.insertAndShift(GoogleTask(id, listId), preferences.addTasksToTop()) + googleTaskDao.insertAndShift( + task = task, + caldavTask = CaldavTask(id, listId, remoteId = null), + top = preferences.addTasksToTop() + ) children.takeIf { it.isNotEmpty() } - ?.map { - val newChild = GoogleTask(it.task, listId) - newChild.order = it.order - newChild.parent = id - newChild - } + ?.map { CaldavTask(task = it, calendar = listId, remoteId = null) } ?.let { googleTaskDao.insert(it) } } is CaldavFilter -> { val listId = selected.uuid val newParent = CaldavTask(id, listId) caldavDao.insert(task, newParent, preferences.addTasksToTop()) - childIds.map { + children.map { val newChild = CaldavTask(it, listId) newChild.remoteParent = newParent.remoteId newChild @@ -176,16 +171,16 @@ class TaskMover @Inject constructor( } private suspend fun moveToGoogleTasks(id: Long, children: List, filter: GtasksFilter) { - taskDao.setParent(0, children) + val task = taskDao.fetch(id) ?: return + taskDao.setParent(id, children) val listId = filter.remoteId - googleTaskDao.insertAndShift(GoogleTask(id, listId), preferences.addTasksToTop()) + googleTaskDao.insertAndShift( + task, + CaldavTask(id, listId, remoteId = null), + preferences.addTasksToTop() + ) children.takeIf { it.isNotEmpty() } - ?.mapIndexed { index, task -> - val newChild = GoogleTask(task, listId) - newChild.order = index.toLong() - newChild.parent = id - newChild - } + ?.map { CaldavTask(it, listId, remoteId = null) } ?.let { googleTaskDao.insert(it) } } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt b/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt index 737dff0ba..86c187a16 100644 --- a/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt +++ b/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt @@ -35,7 +35,6 @@ class Upgrader @Inject constructor( private val filterDao: FilterDao, private val defaultFilterProvider: DefaultFilterProvider, private val googleTaskListDao: GoogleTaskListDao, - private val googleTaskDao: GoogleTaskDao, private val userActivityDao: UserActivityDao, private val taskAttachmentDao: TaskAttachmentDao, private val caldavDao: CaldavDao, @@ -56,7 +55,6 @@ class Upgrader @Inject constructor( run(from, V4_9_5) { removeDuplicateTags() } run(from, V5_3_0) { migrateFilters() } run(from, V6_0_beta_1) { migrateDefaultSyncList() } - run(from, V6_0_beta_2) { migrateGoogleTaskAccount() } run(from, V6_4) { migrateUris() } run(from, V6_7) { this.migrateGoogleTaskFilters() } run(from, V6_8_1) { this.migrateCaldavFilters() } @@ -71,8 +69,8 @@ class Upgrader @Inject constructor( preferences.setBoolean(R.string.p_astrid_sort_enabled, true) taskMover.migrateLocalTasks() } - run(from, V9_7) { googleTaskListDao.resetOrders() } - run(from, V9_7_3) { googleTaskDao.updateParents() } + run(from, V9_7) { caldavDao.resetOrders() } + run(from, V9_7_3) { caldavDao.updateParents() } run(from, V10_0_2) { filterDao.getFilters() .filter { it.getSql().trim() == "WHERE" } @@ -96,6 +94,9 @@ class Upgrader @Inject constructor( run(from, V12_6) { setInstallDetails(from) } + run(from, V13_2) { + caldavDao.updateParents() + } preferences.setBoolean(R.string.p_just_updated, true) } else { setInstallDetails(to) @@ -133,10 +134,6 @@ class Upgrader @Inject constructor( calendar.copy(color = getAndroidColor(calendar.color)) ) } - for (list in googleTaskListDao.getAllLists()) { - list.setColor(getAndroidColor(list.getColor()!!)) - googleTaskListDao.update(list) - } for (tagData in tagDataDao.getAll()) { tagData.setColor(getAndroidColor(tagData.getColor()!!)) tagDataDao.update(tagData) @@ -156,8 +153,7 @@ class Upgrader @Inject constructor( val remoteTask = vtodoCache.getVtodo(task)?.let { fromVtodo(it) } ?: continue val order: Long? = remoteTask.order if (order != null) { - task.order = order - caldavDao.update(task) + taskDao.setOrder(task.task, order) } } } @@ -247,19 +243,6 @@ class Upgrader @Inject constructor( } } - private suspend fun migrateGoogleTaskAccount() { - val account = preferences.getStringValue("gtasks_user") - if (!isNullOrEmpty(account)) { - val googleTaskAccount = GoogleTaskAccount() - googleTaskAccount.account = account - googleTaskListDao.insert(googleTaskAccount) - for (list in googleTaskListDao.getAllLists()) { - list.account = account - googleTaskListDao.insertOrReplace(list) - } - } - } - private suspend fun migrateUris() { migrateUriPreference(R.string.p_backup_dir) migrateUriPreference(R.string.p_attachment_dir) @@ -339,7 +322,6 @@ class Upgrader @Inject constructor( private const val V4_9_5 = 434 private const val V5_3_0 = 491 private const val V6_0_beta_1 = 522 - private const val V6_0_beta_2 = 523 const val V6_4 = 546 private const val V6_7 = 585 private const val V6_8_1 = 607 @@ -358,6 +340,7 @@ class Upgrader @Inject constructor( const val V12_4 = 120400 const val V12_6 = 120601 const val V12_8 = 120800 + const val V13_2 = 130200 @JvmStatic fun getAndroidColor(context: Context, index: Int): Int { diff --git a/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt b/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt index ef48ca1a0..b55b0c90d 100644 --- a/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt @@ -1,7 +1,7 @@ package org.tasks.activities import dagger.hilt.android.lifecycle.HiltViewModel -import org.tasks.data.GoogleTaskList +import org.tasks.data.CaldavCalendar import org.tasks.googleapis.InvokerFactory import org.tasks.ui.ActionViewModel import javax.inject.Inject @@ -9,7 +9,7 @@ import javax.inject.Inject @HiltViewModel class DeleteListViewModel @Inject constructor( private val invoker: InvokerFactory) : ActionViewModel() { - suspend fun deleteList(list: GoogleTaskList) { - run { invoker.getGtasksInvoker(list.account!!).deleteGtaskList(list.remoteId) } + suspend fun deleteList(list: CaldavCalendar) { + run { invoker.getGtasksInvoker(list.account!!).deleteGtaskList(list.uuid) } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt index 0b741b0d0..e288b5d3b 100644 --- a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt +++ b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.ProgressBar import androidx.activity.viewModels +import androidx.lifecycle.lifecycleScope import com.google.android.material.textfield.TextInputEditText import com.google.api.services.tasks.model.TaskList import com.todoroo.astrid.activity.MainActivity @@ -15,10 +16,13 @@ import com.todoroo.astrid.activity.TaskListFragment import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.service.TaskDeleter import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.tasks.R import org.tasks.Strings.isNullOrEmpty -import org.tasks.data.GoogleTaskAccount -import org.tasks.data.GoogleTaskList +import org.tasks.data.CaldavAccount +import org.tasks.data.CaldavCalendar import org.tasks.data.GoogleTaskListDao import org.tasks.databinding.ActivityGoogleTaskListSettingsBinding import org.tasks.extensions.Context.toast @@ -34,20 +38,21 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { private lateinit var progressView: ProgressBar private var isNewList = false - private lateinit var gtasksList: GoogleTaskList + private lateinit var gtasksList: CaldavCalendar private val createListViewModel: CreateListViewModel by viewModels() private val renameListViewModel: RenameListViewModel by viewModels() private val deleteListViewModel: DeleteListViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { gtasksList = intent.getParcelableExtra(EXTRA_STORE_DATA) - ?: GoogleTaskList().apply { + ?: CaldavCalendar( + account = intent.getParcelableExtra(EXTRA_ACCOUNT)!!.username + ).apply { isNewList = true - account = intent.getParcelableExtra(EXTRA_ACCOUNT)!!.account } super.onCreate(savedInstanceState) if (savedInstanceState == null) { - selectedColor = gtasksList.getColor()!! + selectedColor = gtasksList.color selectedIcon = gtasksList.getIcon()!! } if (isNewList) { @@ -55,7 +60,7 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT) } else { - name.setText(gtasksList.title) + name.setText(gtasksList.name) } if (createListViewModel.inProgress || renameListViewModel.inProgress @@ -72,7 +77,7 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { get() = isNewList override val toolbarTitle: String? - get() = if (isNew) getString(R.string.new_list) else gtasksList.title!! + get() = if (isNew) getString(R.string.new_list) else gtasksList.name!! private fun showProgressIndicator() { progressView.visibility = View.VISIBLE @@ -104,7 +109,7 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { } else -> { if (colorChanged() || iconChanged()) { - gtasksList.setColor(selectedColor) + gtasksList.color = selectedColor gtasksList.setIcon(selectedIcon) googleTaskListDao.insertOrReplace(gtasksList) setResult( @@ -154,17 +159,17 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { selectedColor >= 0 || !isNullOrEmpty(newName) } else colorChanged() || nameChanged() || iconChanged() - private fun colorChanged() = selectedColor != gtasksList.getColor() + private fun colorChanged() = selectedColor != gtasksList.color private fun iconChanged() = selectedIcon != gtasksList.getIcon() - private fun nameChanged() = newName != gtasksList.title + private fun nameChanged() = newName != gtasksList.name private suspend fun onListCreated(taskList: TaskList) { with(gtasksList) { - remoteId = taskList.id - title = taskList.title - setColor(selectedColor) + uuid = taskList.id + name = taskList.title + color = selectedColor setIcon(selectedIcon) id = googleTaskListDao.insertOrReplace(this) } @@ -176,16 +181,20 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { private fun onListDeleted(deleted: Boolean) { if (deleted) { - taskDeleter.delete(gtasksList) - setResult(Activity.RESULT_OK, Intent(TaskListFragment.ACTION_DELETED)) - finish() + lifecycleScope.launch { + withContext(NonCancellable) { + taskDeleter.delete(gtasksList) + } + setResult(Activity.RESULT_OK, Intent(TaskListFragment.ACTION_DELETED)) + finish() + } } } private suspend fun onListRenamed(taskList: TaskList) { with(gtasksList) { - title = taskList.title - setColor(selectedColor) + name = taskList.title + color = selectedColor setIcon(selectedIcon) googleTaskListDao.insertOrReplace(this) } diff --git a/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt b/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt index 90b9fafa3..149993d70 100644 --- a/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt +++ b/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt @@ -126,7 +126,6 @@ class NavigationDrawerCustomization : ThemedInjectingAppCompatActivity(), Toolba lifecycleScope.launch { filterDao.resetOrders() caldavDao.resetOrders() - googleTaskListDao.resetOrders() tagDataDao.resetOrders() locationDao.resetOrders() updateFilters() @@ -226,7 +225,7 @@ class NavigationDrawerCustomization : ThemedInjectingAppCompatActivity(), Toolba private suspend fun setOrder(order: Int, filter: FilterListItem) { when (filter) { - is GtasksFilter -> googleTaskListDao.setOrder(filter.list.id, order) + is GtasksFilter -> caldavDao.setOrder(filter.list.id, order) is CaldavFilter -> caldavDao.setOrder(filter.calendar.id, order) is TagFilter -> tagDataDao.setOrder(filter.tagData.id!!, order) is CustomFilter -> filterDao.setOrder(filter.id, order) diff --git a/app/src/main/java/org/tasks/activities/RenameListViewModel.kt b/app/src/main/java/org/tasks/activities/RenameListViewModel.kt index 89f813405..81167ffa4 100644 --- a/app/src/main/java/org/tasks/activities/RenameListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/RenameListViewModel.kt @@ -2,7 +2,7 @@ package org.tasks.activities import com.google.api.services.tasks.model.TaskList import dagger.hilt.android.lifecycle.HiltViewModel -import org.tasks.data.GoogleTaskList +import org.tasks.data.CaldavCalendar import org.tasks.googleapis.InvokerFactory import org.tasks.ui.CompletableViewModel import javax.inject.Inject @@ -10,7 +10,7 @@ import javax.inject.Inject @HiltViewModel class RenameListViewModel @Inject constructor( private val invoker: InvokerFactory) : CompletableViewModel() { - suspend fun renameList(list: GoogleTaskList, name: String) { - run { invoker.getGtasksInvoker(list.account!!).renameGtaskList(list.remoteId, name)!! } + suspend fun renameList(list: CaldavCalendar, name: String) { + run { invoker.getGtasksInvoker(list.account!!).renameGtaskList(list.uuid, name)!! } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/backup/BackupContainer.kt b/app/src/main/java/org/tasks/backup/BackupContainer.kt index b1bbe6889..f9b05d98a 100644 --- a/app/src/main/java/org/tasks/backup/BackupContainer.kt +++ b/app/src/main/java/org/tasks/backup/BackupContainer.kt @@ -9,8 +9,6 @@ class BackupContainer( val places: List?, val tags: List?, val filters: List?, - val googleTaskAccounts: List?, - val googleTaskLists: List?, val caldavAccounts: List?, val caldavCalendars: List?, val taskListMetadata: List?, @@ -20,17 +18,19 @@ class BackupContainer( val stringPrefs: Map?, val boolPrefs: Map?, val setPrefs: Map>?, + val googleTaskAccounts: List? = emptyList(), + val googleTaskLists: List? = emptyList(), ) { class TaskBackup( val task: Task, val alarms: List, val geofences: List?, val tags: List, - val google: List, val comments: List, val attachments: List?, val caldavTasks: List?, val vtodo: String?, + val google: List = emptyList(), ) { val locations: List = emptyList() } diff --git a/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt b/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt index e0dc173cc..9cf2637e5 100755 --- a/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt +++ b/app/src/main/java/org/tasks/backup/TasksJsonExporter.kt @@ -37,9 +37,7 @@ class TasksJsonExporter @Inject constructor( private val alarmDao: AlarmDao, private val locationDao: LocationDao, private val tagDao: TagDao, - private val googleTaskDao: GoogleTaskDao, private val filterDao: FilterDao, - private val googleTaskListDao: GoogleTaskListDao, private val taskAttachmentDao: TaskAttachmentDao, private val caldavDao: CaldavDao, private val workManager: WorkManager, @@ -117,7 +115,6 @@ class TasksJsonExporter @Inject constructor( alarmDao.getAlarms(taskId), locationDao.getGeofencesForTask(taskId), tagDao.getTagsForTask(taskId), - googleTaskDao.getAllByTaskId(taskId), userActivityDao.getComments(taskId), taskAttachmentDao.getAttachmentsForTask(taskId), caldavTasks, @@ -132,8 +129,6 @@ class TasksJsonExporter @Inject constructor( locationDao.getPlaces(), tagDataDao.getAll(), filterDao.getFilters(), - googleTaskListDao.getAccounts(), - googleTaskListDao.getAllLists(), caldavDao.getAccounts(), caldavDao.getCalendars(), taskListMetadataDao.getAll(), diff --git a/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt b/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt index f5f597005..13b263e20 100644 --- a/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt +++ b/app/src/main/java/org/tasks/backup/TasksJsonImporter.kt @@ -19,6 +19,7 @@ import org.tasks.LocalBroadcastManager import org.tasks.R import org.tasks.caldav.VtodoCache import org.tasks.data.* +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.Place.Companion.newPlace import org.tasks.db.Migrations.repeatFrom import org.tasks.db.Migrations.withoutFrom @@ -38,8 +39,6 @@ class TasksJsonImporter @Inject constructor( private val localBroadcastManager: LocalBroadcastManager, private val alarmDao: AlarmDao, private val tagDao: TagDao, - private val googleTaskDao: GoogleTaskDao, - private val googleTaskListDao: GoogleTaskListDao, private val filterDao: FilterDao, private val taskAttachmentDao: TaskAttachmentDao, private val caldavDao: CaldavDao, @@ -81,8 +80,15 @@ class TasksJsonImporter @Inject constructor( tagDataDao.createNew(tagData) } backupContainer.googleTaskAccounts?.forEach { googleTaskAccount -> - if (googleTaskListDao.getAccount(googleTaskAccount.account!!) == null) { - googleTaskListDao.insert(googleTaskAccount) + if (caldavDao.getAccount(TYPE_GOOGLE_TASKS, googleTaskAccount.account!!) == null) { + caldavDao.insert( + CaldavAccount().apply { + accountType = TYPE_GOOGLE_TASKS + uuid = googleTaskAccount.account + name = googleTaskAccount.account + username = googleTaskAccount.account + } + ) } } backupContainer.places?.forEach { place -> @@ -91,9 +97,15 @@ class TasksJsonImporter @Inject constructor( } } backupContainer.googleTaskLists?.forEach { googleTaskList -> - googleTaskList.setColor(themeToColor(context, version, googleTaskList.getColor()!!)) - if (googleTaskListDao.getByRemoteId(googleTaskList.remoteId!!) == null) { - googleTaskListDao.insert(googleTaskList) + if (caldavDao.getCalendar(googleTaskList.remoteId!!) == null) { + caldavDao.insert( + CaldavCalendar( + account = googleTaskList.account, + uuid = googleTaskList.remoteId, + color = themeToColor(context, version, googleTaskList.color ?: 0), + + ) + ) } } backupContainer.filters?.forEach { filter -> @@ -174,8 +186,17 @@ class TasksJsonImporter @Inject constructor( userActivityDao.createNew(comment) } for (googleTask in backup.google) { - googleTask.task = taskId - googleTaskDao.insert(googleTask) + caldavDao.insert( + CaldavTask( + task = taskId, + calendar = googleTask.listId, + remoteId = googleTask.remoteId, + ).apply { + remoteOrder = googleTask.remoteOrder + remoteParent = googleTask.remoteParent + lastSync = googleTask.lastSync + } + ) } for (location in backup.locations) { val place = newPlace() @@ -226,7 +247,6 @@ class TasksJsonImporter @Inject constructor( } result.importCount++ } - googleTaskDao.updateParents() caldavDao.updateParents() val ignoreKeys = ignorePrefs.map { context.getString(it) } backupContainer diff --git a/app/src/main/java/org/tasks/caldav/iCalendar.kt b/app/src/main/java/org/tasks/caldav/iCalendar.kt index 53b83f2f3..1d61ff38f 100644 --- a/app/src/main/java/org/tasks/caldav/iCalendar.kt +++ b/app/src/main/java/org/tasks/caldav/iCalendar.kt @@ -438,7 +438,7 @@ class iCalendar @Inject constructor( else -> if (priority > 5) min(9, priority) else 9 } parent = if (task.parent == 0L) null else caldavTask.remoteParent - order = caldavTask.order + order = task.order collapsed = task.isCollapsed } diff --git a/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt b/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt index 1d4130075..b59498d86 100644 --- a/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt +++ b/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt @@ -30,12 +30,12 @@ fun com.todoroo.astrid.data.Task.applyRemote( applyDue(remote, local) applyStart(remote, local) applyCollapsed(remote, local) + applyOrder(remote, local) return this } fun CaldavTask.applyRemote(remote: Task, local: Task?): CaldavTask { applyParent(remote, local) - applyOrder(remote, local) return this } @@ -108,7 +108,7 @@ private fun com.todoroo.astrid.data.Task.applyCollapsed(remote: Task, local: Tas } } -private fun CaldavTask.applyOrder(remote: Task, local: Task?) { +private fun com.todoroo.astrid.data.Task.applyOrder(remote: Task, local: Task?) { if (local == null || local.order == order) { order = remote.order } diff --git a/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt b/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt index 9b7424edd..c01d68523 100644 --- a/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt +++ b/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt @@ -28,13 +28,13 @@ import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.data.Task import org.tasks.compose.* -import org.tasks.data.GoogleTask import org.tasks.data.TaskContainer @Composable fun SubtaskRow( + originalFilter: Filter?, filter: Filter?, - googleTask: GoogleTask?, + hasParent: Boolean, desaturate: Boolean, existingSubtasks: List, newSubtasks: List, @@ -61,9 +61,11 @@ fun SubtaskRow( }, content = { Column { - val isGoogleTaskChild = - filter is GtasksFilter && googleTask != null && googleTask.parent > 0 && googleTask.listId == filter.remoteId + hasParent && + filter is GtasksFilter && + originalFilter is GtasksFilter && + originalFilter.remoteId == filter.remoteId if (isGoogleTaskChild) { DisabledText( text = stringResource(id = org.tasks.R.string.subtasks_multilevel_google_task), @@ -207,8 +209,9 @@ fun ExistingSubtaskRow( fun NoSubtasks() { MdcTheme { SubtaskRow( + originalFilter = null, filter = null, - googleTask = null, + hasParent = false, desaturate = true, existingSubtasks = emptyList(), newSubtasks = emptyList(), @@ -228,8 +231,9 @@ fun NoSubtasks() { fun SubtasksPreview() { MdcTheme { SubtaskRow( + originalFilter = null, filter = null, - googleTask = null, + hasParent = false, desaturate = true, existingSubtasks = listOf( TaskContainer().apply { diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.kt b/app/src/main/java/org/tasks/data/CaldavAccount.kt index 8e24083d7..8b622face 100644 --- a/app/src/main/java/org/tasks/data/CaldavAccount.kt +++ b/app/src/main/java/org/tasks/data/CaldavAccount.kt @@ -103,6 +103,9 @@ class CaldavAccount : Parcelable { val isMicrosoft: Boolean get() = accountType == TYPE_MICROSOFT + val isGoogleTasks: Boolean + get() = accountType == TYPE_GOOGLE_TASKS + fun listSettingsClass(): Class = when(accountType) { TYPE_LOCAL -> LocalListSettingsActivity::class.java TYPE_ETESYNC, TYPE_OPENTASKS -> OpenTasksListSettingsActivity::class.java @@ -208,6 +211,7 @@ class CaldavAccount : Parcelable { uuid.isDavx5() -> R.string.davx5 uuid.isDecSync() -> R.string.decsync isMicrosoft -> R.string.microsoft + isGoogleTasks -> R.string.gtasks_GPr_header else -> 0 } @@ -219,12 +223,14 @@ class CaldavAccount : Parcelable { uuid.isDavx5() -> R.drawable.ic_davx5_icon_green_bg uuid.isDecSync() -> R.drawable.ic_decsync isMicrosoft -> R.drawable.ic_microsoft_tasks + isGoogleTasks -> R.drawable.ic_google else -> 0 } companion object { val TABLE = Table("caldav_accounts") val UUID = TABLE.column("cda_uuid") + val ACCOUNT_TYPE = TABLE.column("cda_account_type") const val TYPE_CALDAV = 0 @Deprecated("use etebase") const val TYPE_ETESYNC = 1 @@ -233,6 +239,7 @@ class CaldavAccount : Parcelable { const val TYPE_TASKS = 4 const val TYPE_ETEBASE = 5 const val TYPE_MICROSOFT = 6 + const val TYPE_GOOGLE_TASKS = 7 const val SERVER_UNKNOWN = -1 const val SERVER_TASKS = 0 diff --git a/app/src/main/java/org/tasks/data/CaldavCalendar.kt b/app/src/main/java/org/tasks/data/CaldavCalendar.kt index 16a59a60e..3763f314a 100644 --- a/app/src/main/java/org/tasks/data/CaldavCalendar.kt +++ b/app/src/main/java/org/tasks/data/CaldavCalendar.kt @@ -25,6 +25,7 @@ data class CaldavCalendar( @ColumnInfo(name = "cdl_icon") private var icon: Int? = -1, @ColumnInfo(name = "cdl_order") val order: Int = NO_ORDER, @ColumnInfo(name = "cdl_access") var access: Int = ACCESS_OWNER, + @ColumnInfo(name = "cdl_last_sync") val lastSync: Long = 0, ) : Parcelable { @Ignore constructor(source: Parcel): this( @@ -38,6 +39,7 @@ data class CaldavCalendar( icon = source.readInt(), order = source.readInt(), access = source.readInt(), + lastSync = source.readLong(), ) @Suppress("RedundantNullableReturnType") @@ -63,6 +65,7 @@ data class CaldavCalendar( writeInt(getIcon()!!) writeInt(order) writeInt(access) + writeLong(lastSync) } } diff --git a/app/src/main/java/org/tasks/data/CaldavDao.kt b/app/src/main/java/org/tasks/data/CaldavDao.kt index 49b6cdf35..efb75d5bc 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.kt +++ b/app/src/main/java/org/tasks/data/CaldavDao.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.tasks.R import org.tasks.data.CaldavAccount.Companion.TYPE_ETESYNC +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.CaldavAccount.Companion.TYPE_LOCAL import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS import org.tasks.data.CaldavAccount.Companion.TYPE_TASKS @@ -92,25 +93,43 @@ ORDER BY CASE cda_account_type @Transaction open suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long { - if (caldavTask.order != null) { + if (task.order != null) { return insert(caldavTask) } if (addToTop) { - caldavTask.order = findFirstTask(caldavTask.calendar!!, task.parent) + task.order = findFirstTask(caldavTask.calendar!!, task.parent) ?.takeIf { task.creationDate.toAppleEpoch() >= it } ?.minus(1) } else { - caldavTask.order = findLastTask(caldavTask.calendar!!, task.parent) + task.order = findLastTask(caldavTask.calendar!!, task.parent) ?.takeIf { task.creationDate.toAppleEpoch() <= it } ?.plus(1) } - return insert(caldavTask) + val id = insert(caldavTask) + update(task) + return id } - @Query("SELECT MIN(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent") + @Query(""" +SELECT MIN(IFNULL(`order`, (created - $APPLE_EPOCH) / 1000)) +FROM caldav_tasks + INNER JOIN tasks ON _id = cd_task +WHERE cd_calendar = :calendar + AND cd_deleted = 0 + AND deleted = 0 + AND parent = :parent + """) internal abstract suspend fun findFirstTask(calendar: String, parent: Long): Long? - @Query("SELECT MAX(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent") + @Query(""" +SELECT MAX(IFNULL(`order`, (created - $APPLE_EPOCH) / 1000)) +FROM caldav_tasks + INNER JOIN tasks ON _id = cd_task +WHERE cd_calendar = :calendar + AND cd_deleted = 0 + AND deleted = 0 + AND parent = :parent + """) internal abstract suspend fun findLastTask(calendar: String, parent: Long): Long? @Insert @@ -122,15 +141,11 @@ ORDER BY CASE cda_account_type @Update abstract suspend fun update(caldavTask: CaldavTask) - suspend fun update(caldavTask: SubsetCaldav) { - update(caldavTask.cd_id, caldavTask.cd_order, caldavTask.cd_remote_parent) - } - - @Query("UPDATE caldav_tasks SET cd_order = :position, cd_remote_parent = :parent WHERE cd_id = :id") - internal abstract suspend fun update(id: Long, position: Long?, parent: String?) + @Update + abstract suspend fun update(task: Task) - @Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id") - internal abstract suspend fun update(id: Long, position: Long?) + @Update + abstract suspend fun updateTasks(tasks: Iterable) @Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id") internal abstract suspend fun update(id: Long, remoteParent: String?) @@ -188,7 +203,16 @@ SELECT EXISTS(SELECT 1 + "AND cd_deleted = 0") abstract suspend fun getCaldavTasksToPush(calendar: String): List - @Query("SELECT * FROM caldav_lists ORDER BY cdl_name COLLATE NOCASE") + @Query("SELECT * FROM caldav_lists " + + "INNER JOIN caldav_accounts ON caldav_lists.cdl_account = caldav_accounts.cda_uuid " + + "WHERE caldav_accounts.cda_account_type = $TYPE_GOOGLE_TASKS " + + "ORDER BY cdl_name COLLATE NOCASE") + abstract suspend fun getGoogleTaskLists(): List + + @Query("SELECT * FROM caldav_lists " + + "INNER JOIN caldav_accounts ON caldav_lists.cdl_account = caldav_accounts.cda_uuid " + + "WHERE caldav_accounts.cda_account_type != $TYPE_GOOGLE_TASKS " + + "ORDER BY cdl_name COLLATE NOCASE") abstract suspend fun getCalendars(): List @Query(""" @@ -240,12 +264,16 @@ SELECT EXISTS(SELECT 1 @Query(""" SELECT caldav_lists.*, COUNT(DISTINCT(tasks._id)) AS count, COUNT(DISTINCT(principal_access.id)) AS principals FROM caldav_lists - LEFT JOIN caldav_tasks - ON caldav_tasks.cd_calendar = caldav_lists.cdl_uuid - LEFT JOIN tasks ON caldav_tasks.cd_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND - tasks.hideUntil < :now AND cd_deleted = 0 + LEFT JOIN caldav_tasks ON caldav_tasks.cd_calendar = caldav_lists.cdl_uuid + LEFT JOIN tasks ON caldav_tasks.cd_task = tasks._id AND + tasks.deleted = 0 AND + tasks.completed = 0 AND + tasks.hideUntil < :now AND + cd_deleted = 0 LEFT JOIN principal_access ON caldav_lists.cdl_id = principal_access.list + LEFT JOIN caldav_accounts ON caldav_accounts.cda_uuid = caldav_lists.cdl_account WHERE caldav_lists.cdl_account = :uuid +AND caldav_accounts.cda_account_type != $TYPE_GOOGLE_TASKS GROUP BY caldav_lists.cdl_uuid """) abstract suspend fun getCaldavFilters(uuid: String, now: Long = currentTimeMillis()): List @@ -255,7 +283,10 @@ GROUP BY caldav_lists.cdl_uuid + " INNER JOIN caldav_tasks ON caldav_tasks.cd_task = tasks._id" + " WHERE p.cd_remote_id = caldav_tasks.cd_remote_parent" + " AND p.cd_calendar = caldav_tasks.cd_calendar" - + " AND p.cd_deleted = 0), 0)" + + " AND p.cd_deleted = 0" + + " AND caldav_tasks.cd_remote_parent IS NOT NULL" + + " AND caldav_tasks.cd_remote_parent != ''" + + "), 0)" + "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0)") abstract suspend fun updateParents() @@ -266,14 +297,16 @@ GROUP BY caldav_lists.cdl_uuid + " AND caldav_tasks.cd_calendar = :calendar" + " WHERE p.cd_remote_id = caldav_tasks.cd_remote_parent" + " AND p.cd_calendar = caldav_tasks.cd_calendar" - + " AND caldav_tasks.cd_deleted = 0), 0)" + + " AND p.cd_deleted = 0" + + " AND caldav_tasks.cd_remote_parent IS NOT NULL" + + " AND caldav_tasks.cd_remote_parent != ''" + + "), 0)" + "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)") abstract suspend fun updateParents(calendar: String) @Transaction open suspend fun move(task: TaskContainer, newParent: Long, newPosition: Long?) { val previousParent = task.parent - val caldavTask = task.caldavTask val previousPosition = task.caldavSortOrder if (newPosition != null) { if (newParent == previousParent && newPosition < previousPosition) { @@ -282,28 +315,28 @@ GROUP BY caldav_lists.cdl_uuid shiftDown(task.caldav!!, newParent, newPosition) } } - caldavTask.cd_order = newPosition - update(caldavTask.cd_id, caldavTask.cd_order) + task.task.order = newPosition + setTaskOrder(task.id, newPosition) } @Transaction open suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) { - val updated = ArrayList() + val updated = ArrayList() val tasks = getTasksToShift(calendar, parent, from, to) for (i in tasks.indices) { val task = tasks[i] val current = from + i if (task.sortOrder == current) { - val caldavTask = task.caldavTask - caldavTask.order = current + 1 - updated.add(caldavTask) + val task = task.task + task.order = current + 1 + updated.add(task) } else if (task.sortOrder > current) { break } } - update(updated) + updateTasks(updated) updated - .map(CaldavTask::task) + .map(Task::id) .dbchunk() .forEach { touchInternal(it) } } @@ -311,7 +344,18 @@ GROUP BY caldav_lists.cdl_uuid @Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)") internal abstract suspend fun touchInternal(ids: List, modificationTime: Long = now()) - @Query("SELECT task.*, caldav_task.*, IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000) AS primary_sort FROM caldav_tasks AS caldav_task INNER JOIN tasks AS task ON _id = cd_task WHERE cd_calendar = :calendar AND parent = :parent AND cd_deleted = 0 AND deleted = 0 AND primary_sort >= :from AND primary_sort < IFNULL(:to, ${Long.MAX_VALUE}) ORDER BY primary_sort") + @Query(""" +SELECT task.*, caldav_task.*, IFNULL(`order`, (created - $APPLE_EPOCH) / 1000) AS primary_sort +FROM caldav_tasks AS caldav_task + INNER JOIN tasks AS task ON _id = cd_task +WHERE cd_calendar = :calendar + AND parent = :parent + AND cd_deleted = 0 + AND deleted = 0 + AND primary_sort >= :from + AND primary_sort < IFNULL(:to, ${Long.MAX_VALUE}) +ORDER BY primary_sort + """) internal abstract suspend fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List @Query("UPDATE caldav_lists SET cdl_order = $NO_ORDER") @@ -320,6 +364,9 @@ GROUP BY caldav_lists.cdl_uuid @Query("UPDATE caldav_lists SET cdl_order = :order WHERE cdl_id = :id") abstract suspend fun setOrder(id: Long, order: Int) + @Query("UPDATE tasks SET `order` = :order WHERE _id = :id") + abstract suspend fun setTaskOrder(id: Long, order: Long?) + suspend fun setupLocalAccount(context: Context): CaldavAccount = mutex.withLock { val account = getLocalAccount() getLocalList(context, account) diff --git a/app/src/main/java/org/tasks/data/CaldavTask.kt b/app/src/main/java/org/tasks/data/CaldavTask.kt index ccdf6ecfe..c4a23f2c8 100644 --- a/app/src/main/java/org/tasks/data/CaldavTask.kt +++ b/app/src/main/java/org/tasks/data/CaldavTask.kt @@ -1,10 +1,6 @@ package org.tasks.data -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.Ignore -import androidx.room.PrimaryKey +import androidx.room.* import com.todoroo.andlib.data.Table import com.todoroo.astrid.data.Task import com.todoroo.astrid.helper.UUIDHelper @@ -52,9 +48,11 @@ class CaldavTask { @ColumnInfo(name = "cd_remote_parent") var remoteParent: String? = null - @ColumnInfo(name = "cd_order") - @Transient - var order: Long? = null + @ColumnInfo(name = "gt_moved") + var isMoved: Boolean = false + + @ColumnInfo(name = "gt_remote_order") + var remoteOrder: Long = 0 constructor() @@ -67,7 +65,7 @@ class CaldavTask { } @Ignore - constructor(task: Long, calendar: String?, remoteId: String?, `object`: String?) { + constructor(task: Long, calendar: String?, remoteId: String?, `object`: String? = null) { this.task = task this.calendar = calendar this.remoteId = remoteId @@ -77,7 +75,7 @@ class CaldavTask { fun isDeleted() = deleted > 0 override fun toString(): String = - "CaldavTask(id=$id, task=$task, calendar=$calendar, `object`=$`object`, remoteId=$remoteId, etag=$etag, lastSync=$lastSync, deleted=$deleted, remoteParent=$remoteParent, order=$order)" + "CaldavTask(id=$id, task=$task, calendar=$calendar, `object`=$`object`, remoteId=$remoteId, etag=$etag, lastSync=$lastSync, deleted=$deleted, remoteParent=$remoteParent, isMoved=$isMoved, remoteOrder=$remoteOrder)" companion object { const val KEY = "caldav" @@ -87,4 +85,4 @@ class CaldavTask { @JvmField val DELETED = TABLE.column("cd_deleted") @JvmField val CALENDAR = TABLE.column("cd_calendar") } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/tasks/data/CaldavTaskContainer.kt b/app/src/main/java/org/tasks/data/CaldavTaskContainer.kt index 9d40f5d64..8c41ddb84 100644 --- a/app/src/main/java/org/tasks/data/CaldavTaskContainer.kt +++ b/app/src/main/java/org/tasks/data/CaldavTaskContainer.kt @@ -18,7 +18,7 @@ class CaldavTaskContainer { get() = task.isDeleted val sortOrder: Long - get() = caldavTask.order ?: DateTime(task.creationDate).toAppleEpoch() + get() = task.order ?: DateTime(task.creationDate).toAppleEpoch() val startDate: Long get() = task.hideUntil diff --git a/app/src/main/java/org/tasks/data/ContentProviderDao.kt b/app/src/main/java/org/tasks/data/ContentProviderDao.kt index 8a0b68fed..be0ade47a 100644 --- a/app/src/main/java/org/tasks/data/ContentProviderDao.kt +++ b/app/src/main/java/org/tasks/data/ContentProviderDao.kt @@ -39,9 +39,6 @@ interface ContentProviderDao { INNER JOIN caldav_accounts ON cdl_account = cda_uuid""") fun getLists(): Cursor - @Query("SELECT * FROM google_task_lists") - fun getGoogleTaskLists(): Cursor - @RawQuery fun rawQuery(query: SupportSQLiteQuery): Cursor } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/ContentProviderDaoBlocking.kt b/app/src/main/java/org/tasks/data/ContentProviderDaoBlocking.kt index 40061843e..6027e4561 100644 --- a/app/src/main/java/org/tasks/data/ContentProviderDaoBlocking.kt +++ b/app/src/main/java/org/tasks/data/ContentProviderDaoBlocking.kt @@ -24,7 +24,5 @@ class ContentProviderDaoBlocking @Inject constructor(private val dao: ContentPro fun getLists(): Cursor = dao.getLists() - fun getGoogleTaskLists(): Cursor = dao.getGoogleTaskLists() - fun rawQuery(query: SupportSQLiteQuery): Cursor = dao.rawQuery(query) } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/DeletionDao.kt b/app/src/main/java/org/tasks/data/DeletionDao.kt index 393f66ac4..7627885f9 100644 --- a/app/src/main/java/org/tasks/data/DeletionDao.kt +++ b/app/src/main/java/org/tasks/data/DeletionDao.kt @@ -55,36 +55,6 @@ WHERE recurring = 1 ids.eachChunk(this::markDeletedInternal) } - @Query("SELECT gt_task FROM google_tasks WHERE gt_deleted = 0 AND gt_list_id = :listId") - internal abstract suspend fun getActiveGoogleTasks(listId: String): List - - @Delete - internal abstract suspend fun deleteGoogleTaskList(googleTaskList: GoogleTaskList) - - @Transaction - open suspend fun delete(googleTaskList: GoogleTaskList): List { - val tasks = getActiveGoogleTasks(googleTaskList.remoteId!!) - delete(tasks) - deleteGoogleTaskList(googleTaskList) - return tasks - } - - @Delete - internal abstract suspend fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount) - - @Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC") - abstract suspend fun getLists(account: String): List - - @Transaction - open suspend fun delete(googleTaskAccount: GoogleTaskAccount): List { - val deleted = ArrayList() - for (list in getLists(googleTaskAccount.account!!)) { - deleted.addAll(delete(list)) - } - deleteGoogleTaskAccount(googleTaskAccount) - return deleted - } - @Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0") internal abstract suspend fun getActiveCaldavTasks(calendar: String): List diff --git a/app/src/main/java/org/tasks/data/GoogleTask.kt b/app/src/main/java/org/tasks/data/GoogleTask.kt index 2645870a6..60a0b68da 100644 --- a/app/src/main/java/org/tasks/data/GoogleTask.kt +++ b/app/src/main/java/org/tasks/data/GoogleTask.kt @@ -1,126 +1,15 @@ package org.tasks.data -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.Ignore -import androidx.room.Index -import androidx.room.PrimaryKey -import com.todoroo.andlib.data.Table -import com.todoroo.astrid.data.Task - -@Entity( - tableName = "google_tasks", - indices = [ - Index(name = "gt_list_parent", value = ["gt_list_id", "gt_parent"]) - ], - foreignKeys = [ - ForeignKey( - entity = Task::class, - parentColumns = ["_id"], - childColumns = ["gt_task"], - onDelete = ForeignKey.CASCADE, - ), - ] -) -class GoogleTask { - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "gt_id") - @Transient - var id: Long = 0 - - @ColumnInfo(name = "gt_task", index = true) - @Transient - var task: Long = 0 - - @ColumnInfo(name = "gt_remote_id") - var remoteId: String? = "" - - @ColumnInfo(name = "gt_list_id") - var listId: String? = "" - - @ColumnInfo(name = "gt_parent") - @Transient - var parent: Long = 0 - - @ColumnInfo(name = "gt_remote_parent") - var remoteParent: String? = null - set(value) { - field = if (value?.isNotBlank() == true) value else null - } - - @ColumnInfo(name = "gt_moved") - @Transient - var isMoved = false - - @ColumnInfo(name = "gt_order") - @Transient - var order: Long = 0 - - @ColumnInfo(name = "gt_remote_order") - var remoteOrder: Long = 0 - - @ColumnInfo(name = "gt_last_sync") - var lastSync: Long = 0 - - @ColumnInfo(name = "gt_deleted") - var deleted: Long = 0 - - constructor() - - @Ignore - constructor(task: Long, listId: String) { - this.task = task - this.listId = listId - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is GoogleTask) return false - - if (id != other.id) return false - if (task != other.task) return false - if (remoteId != other.remoteId) return false - if (listId != other.listId) return false - if (parent != other.parent) return false - if (remoteParent != other.remoteParent) return false - if (isMoved != other.isMoved) return false - if (order != other.order) return false - if (remoteOrder != other.remoteOrder) return false - if (lastSync != other.lastSync) return false - if (deleted != other.deleted) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + task.hashCode() - result = 31 * result + remoteId.hashCode() - result = 31 * result + listId.hashCode() - result = 31 * result + parent.hashCode() - result = 31 * result + (remoteParent?.hashCode() ?: 0) - result = 31 * result + isMoved.hashCode() - result = 31 * result + order.hashCode() - result = 31 * result + remoteOrder.hashCode() - result = 31 * result + lastSync.hashCode() - result = 31 * result + deleted.hashCode() - return result - } - - override fun toString(): String = - "GoogleTask(id=$id, task=$task, remoteId='$remoteId', listId='$listId', parent=$parent, remoteParent=$remoteParent, isMoved=$isMoved, order=$order, remoteOrder=$remoteOrder, lastSync=$lastSync, deleted=$deleted)" - - val isNew: Boolean - get() = id == 0L - +@Deprecated("For backup use only") +data class GoogleTask( + var remoteId: String? = "", + var listId: String? = "", + var remoteParent: String? = null, + var remoteOrder: Long = 0, + var lastSync: Long = 0, + var deleted: Long = 0, +) { companion object { const val KEY = "gtasks" - @JvmField val TABLE = Table("google_tasks") - val ID = TABLE.column("gt_id") - @JvmField val PARENT = TABLE.column("gt_parent") - @JvmField val TASK = TABLE.column("gt_task") - @JvmField val DELETED = TABLE.column("gt_deleted") - @JvmField val LIST = TABLE.column("gt_list_id") } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/GoogleTaskAccount.kt b/app/src/main/java/org/tasks/data/GoogleTaskAccount.kt index 3527ce8ce..1946961a9 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskAccount.kt +++ b/app/src/main/java/org/tasks/data/GoogleTaskAccount.kt @@ -1,98 +1,8 @@ package org.tasks.data -import android.os.Parcel -import android.os.Parcelable -import androidx.core.os.ParcelCompat -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Ignore -import androidx.room.PrimaryKey -import com.todoroo.andlib.data.Table - -@Entity(tableName = "google_task_accounts") -class GoogleTaskAccount : Parcelable { - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "gta_id") - @Transient - var id: Long = 0 - - @ColumnInfo(name = "gta_account") - var account: String? = null - - @ColumnInfo(name = "gta_error") - @Transient - var error: String? = "" - - @ColumnInfo(name = "gta_etag") - var etag: String? = null - - @ColumnInfo(name = "gta_collapsed") - var isCollapsed = false - - constructor() - - @Ignore - constructor(source: Parcel) { - id = source.readLong() - account = source.readString() - error = source.readString() - etag = source.readString() - isCollapsed = ParcelCompat.readBoolean(source) - } - - @Ignore - constructor(account: String?) { - this.account = account - } - - override fun describeContents() = 0 - - override fun writeToParcel(dest: Parcel, flags: Int) { - with(dest) { - writeLong(id) - writeString(account) - writeString(error) - writeString(etag) - ParcelCompat.writeBoolean(this, isCollapsed) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is GoogleTaskAccount) return false - - if (id != other.id) return false - if (account != other.account) return false - if (error != other.error) return false - if (etag != other.etag) return false - if (isCollapsed != other.isCollapsed) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + (account?.hashCode() ?: 0) - result = 31 * result + (error?.hashCode() ?: 0) - result = 31 * result + (etag?.hashCode() ?: 0) - result = 31 * result + isCollapsed.hashCode() - return result - } - - override fun toString(): String = - "GoogleTaskAccount(id=$id, account=$account, error=$error, etag=$etag, isCollapsed=$isCollapsed)" - - val hasError: Boolean - get() = !error.isNullOrBlank() - - companion object { - val TABLE = Table("google_task_accounts") - val ACCOUNT = TABLE.column("gta_account") - - @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): GoogleTaskAccount = GoogleTaskAccount(source) - - override fun newArray(size: Int): Array = arrayOfNulls(size) - } - } -} \ No newline at end of file +@Deprecated("Only used for backup migration") +data class GoogleTaskAccount( + var account: String? = null, + var etag: String? = null, + var isCollapsed: Boolean = false, +) diff --git a/app/src/main/java/org/tasks/data/GoogleTaskDao.kt b/app/src/main/java/org/tasks/data/GoogleTaskDao.kt index 66a6fb78f..fe857456a 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskDao.kt +++ b/app/src/main/java/org/tasks/data/GoogleTaskDao.kt @@ -2,195 +2,179 @@ package org.tasks.data import androidx.room.* import com.todoroo.astrid.data.Task -import kotlinx.coroutines.flow.Flow -import org.tasks.db.SuspendDbUtils.chunkedMap -import org.tasks.time.DateTimeUtils.currentTimeMillis +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS @Dao abstract class GoogleTaskDao { @Insert - abstract suspend fun insert(task: GoogleTask): Long + abstract suspend fun insert(task: CaldavTask): Long @Insert - abstract suspend fun insert(tasks: Iterable) + abstract suspend fun insert(tasks: Iterable) @Transaction - open suspend fun insertAndShift(task: GoogleTask, top: Boolean) { + open suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) { if (top) { task.order = 0 - shiftDown(task.listId!!, task.parent, 0) + shiftDown(caldavTask.calendar!!, task.parent, 0) } else { - task.order = getBottom(task.listId!!, task.parent) + task.order = getBottom(caldavTask.calendar!!, task.parent) } - task.id = insert(task) + caldavTask.id = insert(caldavTask) + update(task) } - @Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position") + @Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") internal abstract suspend 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") + @Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` > :from AND `order` <= :to AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") internal abstract suspend 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") + @Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` < :from AND `order` >= :to AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") internal abstract suspend 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") + @Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") internal abstract suspend fun shiftUp(listId: String, parent: Long, position: Long) @Transaction - open suspend fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) { + open suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) { val previousParent = task.parent - val previousPosition = task.order + val previousPosition = task.order!! if (newParent == previousParent) { if (previousPosition < newPosition) { - shiftUp(task.listId, newParent, previousPosition, newPosition) + shiftUp(list, newParent, previousPosition, newPosition) } else { - shiftDown(task.listId, newParent, previousPosition, newPosition) + shiftDown(list, newParent, previousPosition, newPosition) } } else { - shiftUp(task.listId, previousParent, previousPosition) - shiftDown(task.listId, newParent, newPosition) + shiftUp(list, previousParent, previousPosition) + shiftDown(list, newParent, newPosition) } task.parent = newParent task.order = newPosition update(task) + setMoved(task.id, list) } - @Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id") - abstract suspend fun setCollapsed(id: Long, collapsed: Boolean) - - @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1") - abstract suspend fun getByTaskId(taskId: Long): GoogleTask? + @Query("UPDATE caldav_tasks SET gt_moved = 1 WHERE cd_task = :task and cd_calendar = :list") + internal abstract suspend fun setMoved(task: Long, list: String) - @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1") - abstract fun watchGoogleTask(taskId: Long): Flow + @Query("SELECT caldav_tasks.* FROM caldav_tasks INNER JOIN caldav_lists ON cdl_uuid = cd_calendar INNER JOIN caldav_accounts ON cda_uuid = cdl_account WHERE cd_task = :taskId AND cd_deleted = 0 AND cda_account_type = $TYPE_GOOGLE_TASKS LIMIT 1") + abstract suspend fun getByTaskId(taskId: Long): CaldavTask? @Update - abstract suspend fun update(googleTask: GoogleTask) - - private suspend 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 suspend fun update(id: Long, parent: Long, order: Long) + abstract suspend fun update(googleTask: CaldavTask) - @Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task") - abstract suspend fun markDeleted(task: Long, now: Long = currentTimeMillis()) + @Update + abstract suspend fun update(task: Task) @Delete - abstract suspend fun delete(deleted: GoogleTask) + abstract suspend fun delete(deleted: CaldavTask) - @Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1") - abstract suspend fun getByRemoteId(remoteId: String): GoogleTask? + @Query("SELECT * FROM caldav_tasks WHERE cd_remote_id = :remoteId LIMIT 1") + abstract suspend fun getByRemoteId(remoteId: String): CaldavTask? - @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0") - abstract suspend fun getDeletedByTaskId(taskId: Long): List + @Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted > 0") + abstract suspend fun getDeletedByTaskId(taskId: Long): List - @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId") - abstract suspend fun getAllByTaskId(taskId: Long): List + @Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId") + abstract suspend fun getAllByTaskId(taskId: Long): List - @Query("SELECT DISTINCT gt_list_id FROM google_tasks WHERE gt_deleted = 0 AND gt_task IN (:tasks)") + @Query("SELECT DISTINCT cd_calendar FROM caldav_tasks WHERE cd_deleted = 0 AND cd_task IN (:tasks)") abstract suspend fun getLists(tasks: List): List - @Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids) AND gt_deleted = 0") - abstract suspend fun getChildren(ids: List): List - - suspend fun hasRecurringParent(ids: List): List = - ids.chunkedMap { internalHasRecurringParent(it) } - - @Query(""" -SELECT gt_task -FROM google_tasks - INNER JOIN tasks ON gt_parent = _id -WHERE gt_task IN (:ids) - AND gt_deleted = 0 - AND tasks.recurrence IS NOT NULL - AND tasks.recurrence != '' - AND tasks.completed = 0 - """) - abstract suspend fun internalHasRecurringParent(ids: List): List - - @Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId") - abstract suspend fun getChildTasks(taskId: Long): List - - @Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_parent WHERE gt_task = :taskId") - abstract suspend fun getParentTask(taskId: Long): Task? - - @Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0") - abstract suspend fun getChildren(id: Long): List - - @Query("SELECT IFNULL(MAX(gt_order), -1) + 1 FROM google_tasks WHERE gt_list_id = :listId AND gt_parent = :parent") + @Query("SELECT IFNULL(MAX(`order`), -1) + 1 FROM tasks INNER JOIN caldav_tasks ON cd_task = tasks._id WHERE cd_calendar = :listId AND parent = :parent") abstract suspend 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") + @Query( + """ +SELECT cd_remote_id +FROM caldav_tasks + INNER JOIN tasks ON tasks._id = cd_task +WHERE deleted = 0 + AND cd_calendar = :listId + AND parent = :parent + AND `order` < :order + AND cd_remote_id IS NOT NULL + AND cd_remote_id != '' +ORDER BY `order` DESC + """ + ) abstract suspend fun getPrevious(listId: String, parent: Long, order: Long): String? - @Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task") + @Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_task = :task") abstract suspend fun getRemoteId(task: Long): String? - @Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId") + @Query("SELECT cd_task FROM caldav_tasks WHERE cd_remote_id = :remoteId") abstract suspend 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 suspend fun getByLocalOrder(listId: String): List - - @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") - internal abstract suspend fun getByRemoteOrder(listId: String): List + @Query( + """ +SELECT tasks.*, `order` AS primary_sort, NULL AS secondary_sort +FROM tasks + INNER JOIN caldav_tasks ON tasks._id = cd_task +WHERE parent = 0 + AND cd_calendar = :listId + AND tasks.deleted = 0 +UNION +SELECT c.*, p.`order` AS primary_sort, c.`order` AS secondary_sort +FROM tasks AS c + INNER JOIN tasks AS p ON c.parent = p._id + INNER JOIN caldav_tasks ON c._id = cd_task +WHERE c.parent > 0 + AND cd_calendar = :listId + AND c.deleted = 0 +ORDER BY primary_sort ASC, secondary_sort ASC + """ + ) + internal abstract suspend fun getByLocalOrder(listId: String): List + + @Query( + """ +SELECT tasks.*, gt_remote_order AS primary_sort, NULL AS secondary_sort +FROM tasks + JOIN caldav_tasks ON tasks._id = cd_task +WHERE parent = 0 + AND cd_calendar = :listId + AND tasks.deleted = 0 +UNION +SELECT c.*, parent.gt_remote_order AS primary_sort, child.gt_remote_order AS secondary_sort +FROM tasks AS c + INNER JOIN tasks AS p ON c.parent = p._id + INNER JOIN caldav_tasks AS child ON c._id = child.cd_task + INNER JOIN caldav_tasks AS parent ON p._id = parent.cd_task +WHERE c.parent > 0 + AND child.cd_calendar = :listId + AND c.deleted = 0 +ORDER BY primary_sort ASC, secondary_sort ASC + """ + ) + internal abstract suspend fun getByRemoteOrder(listId: String): List @Query(""" -UPDATE google_tasks -SET gt_parent = IFNULL((SELECT gt_task - FROM google_tasks AS p - WHERE google_tasks.gt_remote_parent IS NOT NULL - AND google_tasks.gt_remote_parent != '' - AND 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 suspend fun updateParents() - - @Query(""" -UPDATE google_tasks -SET gt_parent = IFNULL((SELECT gt_task - FROM google_tasks AS p - WHERE google_tasks.gt_remote_parent IS NOT NULL - AND google_tasks.gt_remote_parent != '' - AND 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_list_id = :listId - AND gt_moved = 0 - """) - abstract suspend fun updateParents(listId: String) - - @Query(""" -UPDATE google_tasks -SET gt_remote_parent = CASE WHEN :parent == '' THEN NULL ELSE :parent END, +UPDATE caldav_tasks +SET cd_remote_parent = CASE WHEN :parent == '' THEN NULL ELSE :parent END, gt_remote_order = :position -WHERE gt_remote_id = :id +WHERE cd_remote_id = :id """) abstract suspend fun updatePosition(id: String, parent: String?, position: String) @Transaction - open suspend fun reposition(listId: String) { - updateParents(listId) + open suspend fun reposition(caldavDao: CaldavDao, listId: String) { + caldavDao.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) { + if (task.order != subtasks) { task.order = subtasks update(task) } subtasks++ } else { subtasks = 0 - if (task.order != parent && !task.isMoved) { + if (task.order != parent) { task.order = parent update(task) } diff --git a/app/src/main/java/org/tasks/data/GoogleTaskList.kt b/app/src/main/java/org/tasks/data/GoogleTaskList.kt index 0e990336e..1f4b36dfd 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskList.kt +++ b/app/src/main/java/org/tasks/data/GoogleTaskList.kt @@ -1,126 +1,14 @@ package org.tasks.data -import android.os.Parcel -import android.os.Parcelable -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Ignore -import androidx.room.PrimaryKey -import com.todoroo.andlib.data.Table import com.todoroo.astrid.api.FilterListItem.NO_ORDER -import org.tasks.themes.CustomIcons.LIST -@Entity(tableName = "google_task_lists") -class GoogleTaskList : Parcelable { - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "gtl_id") - @Transient - var id: Long = 0 - - @ColumnInfo(name = "gtl_account") - var account: String? = null - - @ColumnInfo(name = "gtl_remote_id") - var remoteId: String? = null - - @ColumnInfo(name = "gtl_title") - var title: String? = null - - @ColumnInfo(name = "gtl_remote_order") - var order = NO_ORDER - - @ColumnInfo(name = "gtl_last_sync") - var lastSync: Long = 0 - - @ColumnInfo(name = "gtl_color") - private var color: Int? = null - - @ColumnInfo(name = "gtl_icon") - private var icon: Int? = -1 - - constructor() - - @Ignore - constructor(parcel: Parcel) { - id = parcel.readLong() - account = parcel.readString() - remoteId = parcel.readString() - title = parcel.readString() - order = parcel.readInt() - lastSync = parcel.readLong() - color = parcel.readInt() - icon = parcel.readInt() - } - - @Suppress("RedundantNullableReturnType") - fun getColor(): Int? = color ?: 0 - - fun setColor(color: Int?) { - this.color = color - } - - @Suppress("RedundantNullableReturnType") - fun getIcon(): Int? = icon ?: LIST - - fun setIcon(icon: Int?) { - this.icon = icon - } - - override fun describeContents() = 0 - - override fun writeToParcel(parcel: Parcel, i: Int) { - with(parcel) { - writeLong(id) - writeString(account) - writeString(remoteId) - writeString(title) - writeInt(order) - writeLong(lastSync) - writeInt(getColor()!!) - writeInt(getIcon()!!) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is GoogleTaskList) return false - - if (id != other.id) return false - if (account != other.account) return false - if (remoteId != other.remoteId) return false - if (title != other.title) return false - if (order != other.order) return false - if (lastSync != other.lastSync) return false - if (color != other.color) return false - if (icon != other.icon) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + (account?.hashCode() ?: 0) - result = 31 * result + (remoteId?.hashCode() ?: 0) - result = 31 * result + (title?.hashCode() ?: 0) - result = 31 * result + order - result = 31 * result + lastSync.hashCode() - result = 31 * result + (color ?: 0) - result = 31 * result + (icon ?: 0) - return result - } - - override fun toString(): String = - "GoogleTaskList(id=$id, account=$account, remoteId=$remoteId, title=$title, remoteOrder=$order, lastSync=$lastSync, color=$color, icon=$icon)" - - companion object { - @JvmField val TABLE = Table("google_task_lists") - val ACCOUNT = TABLE.column("gtl_account") - @JvmField val REMOTE_ID = TABLE.column("gtl_remote_id") - @JvmField val NAME = TABLE.column("gtl_title") - @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): GoogleTaskList = GoogleTaskList(parcel) - - override fun newArray(size: Int): Array = arrayOfNulls(size) - } - } -} \ No newline at end of file +@Deprecated("Only used for backup migration") +data class GoogleTaskList( + var account: String? = null, + var remoteId: String? = null, + var title: String? = null, + var order: Int = NO_ORDER, + var lastSync: Long = 0, + var color: Int? = null, + var icon: Int? = -1, +) \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/GoogleTaskListDao.kt b/app/src/main/java/org/tasks/data/GoogleTaskListDao.kt index a238c5cf7..4f82cbd30 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskListDao.kt +++ b/app/src/main/java/org/tasks/data/GoogleTaskListDao.kt @@ -1,78 +1,38 @@ package org.tasks.data -import androidx.lifecycle.LiveData import androidx.room.* -import com.todoroo.astrid.api.FilterListItem.NO_ORDER +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.filters.GoogleTaskFilters import org.tasks.time.DateTimeUtils.currentTimeMillis @Dao interface GoogleTaskListDao { - @Query("SELECT * FROM google_task_accounts WHERE gta_id = :id") - fun watchAccount(id: Long): LiveData + @Query("SELECT * FROM caldav_accounts WHERE cda_account_type = $TYPE_GOOGLE_TASKS") + suspend fun getAccounts(): List - @Query("SELECT COUNT(*) FROM google_task_accounts") - suspend fun accountCount(): Int + @Query("SELECT * FROM caldav_lists WHERE cdl_id = :id") + suspend fun getById(id: Long): CaldavCalendar? - @Query("SELECT * FROM google_task_accounts") - suspend fun getAccounts(): List + @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account ORDER BY cdl_name ASC") + suspend fun getLists(account: String): List - @Query("SELECT * FROM google_task_accounts") - fun watchAccounts(): LiveData> + @Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :remoteId LIMIT 1") + suspend fun getByRemoteId(remoteId: String): CaldavCalendar? - @Query("SELECT * FROM google_task_accounts WHERE gta_account = :account COLLATE NOCASE LIMIT 1") - suspend fun getAccount(account: String): GoogleTaskAccount? + @Query("SELECT * FROM caldav_lists WHERE cdl_uuid IN (:remoteIds)") + suspend fun getByRemoteId(remoteIds: List): List - @Query("SELECT * FROM google_task_lists WHERE gtl_id = :id") - suspend fun getById(id: Long): GoogleTaskList? - - @Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC") - suspend fun getLists(account: String): List - - @Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId LIMIT 1") - suspend fun getByRemoteId(remoteId: String): GoogleTaskList? - - @Query("SELECT * FROM google_task_lists WHERE gtl_remote_id IN (:remoteIds)") - suspend fun getByRemoteId(remoteIds: List): List - - @Query("SELECT * FROM google_task_lists") - fun subscribeToLists(): LiveData> - - @Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId AND IFNULL(gtl_account, '') = ''") - suspend fun findExistingList(remoteId: String): GoogleTaskList? - - @Query("SELECT * FROM google_task_lists") - suspend fun getAllLists(): List - - @Query("UPDATE google_task_lists SET gtl_last_sync = 0 WHERE gtl_account = :account") + @Query("UPDATE caldav_lists SET cdl_last_sync = 0 WHERE cdl_account = :account") suspend fun resetLastSync(account: String) @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertOrReplace(googleTaskList: GoogleTaskList): Long - - @Insert - suspend fun insert(googleTaskList: GoogleTaskList): Long - - @Insert - suspend fun insert(googleTaskAccount: GoogleTaskAccount) - - @Update - suspend fun update(account: GoogleTaskAccount) - - @Update - suspend fun update(list: GoogleTaskList) - - @Query("SELECT google_task_lists.*, COUNT(tasks._id) AS count" - + " FROM google_task_lists " - + " LEFT JOIN google_tasks ON google_tasks.gt_list_id = google_task_lists.gtl_remote_id" - + " LEFT JOIN tasks ON google_tasks.gt_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now AND gt_deleted = 0" - + " WHERE google_task_lists.gtl_account = :account" - + " GROUP BY google_task_lists.gtl_remote_id") + suspend fun insertOrReplace(googleTaskList: CaldavCalendar): Long + + @Query("SELECT caldav_lists.*, COUNT(tasks._id) AS count" + + " FROM caldav_lists " + + " LEFT JOIN caldav_tasks ON caldav_tasks.cd_calendar = caldav_lists.cdl_uuid" + + " LEFT JOIN tasks ON caldav_tasks.cd_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now AND cd_deleted = 0" + + " WHERE caldav_lists.cdl_account = :account" + + " GROUP BY caldav_lists.cdl_uuid") suspend fun getGoogleTaskFilters(account: String, now: Long = currentTimeMillis()): List - - @Query("UPDATE google_task_lists SET gtl_remote_order = $NO_ORDER") - suspend fun resetOrders() - - @Query("UPDATE google_task_lists SET gtl_remote_order = :order WHERE gtl_id = :id") - suspend fun setOrder(id: Long, order: Int) } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/SubsetCaldav.kt b/app/src/main/java/org/tasks/data/SubsetCaldav.kt deleted file mode 100644 index 985bacc50..000000000 --- a/app/src/main/java/org/tasks/data/SubsetCaldav.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.tasks.data - -class SubsetCaldav { - var cd_id: Long = 0 - var cd_calendar: String? = null - var cd_remote_parent: String? = null - var cd_order: Long? = null - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is SubsetCaldav) return false - - if (cd_id != other.cd_id) return false - if (cd_calendar != other.cd_calendar) return false - if (cd_remote_parent != other.cd_remote_parent) return false - if (cd_order != other.cd_order) return false - - return true - } - - override fun hashCode(): Int { - var result = cd_id.hashCode() - result = 31 * result + (cd_calendar?.hashCode() ?: 0) - result = 31 * result + (cd_remote_parent?.hashCode() ?: 0) - result = 31 * result + cd_order.hashCode() - return result - } - - override fun toString(): String = - "SubsetCaldav(cd_id=$cd_id, cd_calendar=$cd_calendar, cd_remote_parent=$cd_remote_parent, cd_order=$cd_order)" -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/SubsetGoogleTask.java b/app/src/main/java/org/tasks/data/SubsetGoogleTask.java deleted file mode 100644 index ed6cb79d9..000000000 --- a/app/src/main/java/org/tasks/data/SubsetGoogleTask.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.tasks.data; - -import java.util.Objects; - -public class SubsetGoogleTask { - - public long gt_id; - public long gt_parent; - public String gt_list_id; - public long gt_order; - - public long getId() { - return gt_id; - } - - public String getListId() { - return gt_list_id; - } - - public long getParent() { - return gt_parent; - } - - public void setParent(long parent) { - gt_parent = parent; - } - - public long getOrder() { - return gt_order; - } - - public void setOrder(long order) { - gt_order = order; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SubsetGoogleTask)) { - return false; - } - SubsetGoogleTask that = (SubsetGoogleTask) o; - return gt_id == that.gt_id - && gt_parent == that.gt_parent - && gt_order == that.gt_order - && Objects.equals(gt_list_id, that.gt_list_id); - } - - @Override - public int hashCode() { - return Objects.hash(gt_id, gt_parent, gt_list_id, gt_order); - } - - @Override - public String toString() { - return "SubsetGoogleTask{" - + "gt_id=" - + gt_id - + ", gt_parent=" - + gt_parent - + ", gt_list_id='" - + gt_list_id - + '\'' - + ", gt_order=" - + gt_order - + '}'; - } -} diff --git a/app/src/main/java/org/tasks/data/SubtaskInfo.java b/app/src/main/java/org/tasks/data/SubtaskInfo.java index 69dfaf31a..0397ecabe 100644 --- a/app/src/main/java/org/tasks/data/SubtaskInfo.java +++ b/app/src/main/java/org/tasks/data/SubtaskInfo.java @@ -2,9 +2,7 @@ package org.tasks.data; public class SubtaskInfo { public boolean hasSubtasks; - public boolean hasGoogleSubtasks; - public boolean usesSubtasks() { - return hasSubtasks || hasGoogleSubtasks; + return hasSubtasks; } } diff --git a/app/src/main/java/org/tasks/data/TaskContainer.java b/app/src/main/java/org/tasks/data/TaskContainer.java index 4ce0a47e6..a82dfbfd0 100644 --- a/app/src/main/java/org/tasks/data/TaskContainer.java +++ b/app/src/main/java/org/tasks/data/TaskContainer.java @@ -7,9 +7,9 @@ import java.util.Objects; public class TaskContainer { @Embedded public Task task; - @Embedded public SubsetGoogleTask googletask; - @Embedded public SubsetCaldav caldavTask; + @Embedded public CaldavTask caldavTask; @Embedded public Location location; + public boolean isGoogleTask; public boolean parentComplete; public String tags; public int children; @@ -23,16 +23,11 @@ public class TaskContainer { return tags; } - public @Nullable String getGoogleTaskList() { - return isGoogleTask() ? googletask.getListId() : null; - } - - public boolean isGoogleTask() { - return googletask != null; - } - public @Nullable String getCaldav() { - return isCaldavTask() ? caldavTask.getCd_calendar() : null; + if (caldavTask != null) { + return caldavTask.getCalendar(); + } + return null; } public boolean isCaldavTask() { @@ -127,7 +122,6 @@ public class TaskContainer { && indent == that.indent && targetIndent == that.targetIndent && Objects.equals(task, that.task) - && Objects.equals(googletask, that.googletask) && Objects.equals(caldavTask, that.caldavTask) && Objects.equals(location, that.location) && Objects.equals(tags, that.tags) @@ -138,7 +132,6 @@ public class TaskContainer { public int hashCode() { return Objects.hash( task, - googletask, caldavTask, location, tags, @@ -155,8 +148,6 @@ public class TaskContainer { return "TaskContainer{" + "task=" + task - + ", googletask=" - + googletask + ", caldavTask=" + caldavTask + ", location=" @@ -184,20 +175,11 @@ public class TaskContainer { } public long getParent() { - if (googletask != null) { - return googletask.getParent(); - } else { - return task.getParent(); - } + return task.getParent(); } public void setParent(long parent) { - if (googletask != null) { - task.setParent(0); - googletask.setParent(parent); - } else { - task.setParent(parent); - } + task.setParent(parent); } public boolean hasParent() { @@ -208,11 +190,7 @@ public class TaskContainer { return children > 0; } - public SubsetGoogleTask getGoogleTask() { - return googletask; - } - - public SubsetCaldav getCaldavTask() { + public CaldavTask getCaldavTask() { return caldavTask; } diff --git a/app/src/main/java/org/tasks/data/TaskDao.kt b/app/src/main/java/org/tasks/data/TaskDao.kt index 562190375..53d6eceb6 100644 --- a/app/src/main/java/org/tasks/data/TaskDao.kt +++ b/app/src/main/java/org/tasks/data/TaskDao.kt @@ -1,12 +1,7 @@ package org.tasks.data import androidx.paging.DataSource -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.Query -import androidx.room.RawQuery -import androidx.room.Update -import androidx.room.withTransaction +import androidx.room.* import androidx.sqlite.db.SimpleSQLiteQuery import com.todoroo.andlib.sql.Criterion import com.todoroo.andlib.sql.Field @@ -66,10 +61,11 @@ abstract class TaskDao(private val database: Database) { abstract suspend fun setCompletionDate(remoteIds: List, completionDate: Long, updateTime: Long = now()) @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") + + "LEFT JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task " + + "LEFT JOIN caldav_lists ON caldav_tasks.cd_calendar = caldav_lists.cdl_uuid " + + "WHERE cdl_account = :account " + + "AND (tasks.modified > caldav_tasks.cd_last_sync OR caldav_tasks.cd_remote_id = '' OR caldav_tasks.cd_remote_id IS NULL OR caldav_tasks.cd_deleted > 0) " + + "ORDER BY CASE WHEN parent = 0 THEN 0 ELSE 1 END, `order` ASC") abstract suspend fun getGoogleTasksToPush(account: String): List @Query(""" @@ -131,15 +127,7 @@ abstract class TaskDao(private val database: Database) { @RawQuery abstract suspend 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 - """) + @Query("SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks") abstract suspend fun getSubtaskInfo(): SubtaskInfo @RawQuery(observedEntities = [Place::class]) @@ -151,6 +139,9 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas @Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)") internal abstract suspend fun internalTouch(ids: List, now: Long = currentTimeMillis()) + @Query("UPDATE tasks SET `order` = :order WHERE _id = :id") + internal abstract suspend fun setOrder(id: Long, order: Long?) + suspend fun setParent(parent: Long, tasks: List) = tasks.eachChunk { setParentInternal(parent, it) } @@ -250,10 +241,8 @@ FROM recursive_tasks @Query(""" SELECT _id FROM tasks - LEFT JOIN google_tasks ON _id = gt_task AND gt_deleted = 0 LEFT JOIN caldav_tasks ON _id = cd_task AND cd_deleted = 0 -WHERE gt_id IS NULL - AND cd_id IS NULL +WHERE cd_id IS NULL AND parent = 0 """) abstract suspend fun getLocalTasks(): List diff --git a/app/src/main/java/org/tasks/data/TaskListQuery.kt b/app/src/main/java/org/tasks/data/TaskListQuery.kt index ad1016eac..ed5ddf67c 100644 --- a/app/src/main/java/org/tasks/data/TaskListQuery.kt +++ b/app/src/main/java/org/tasks/data/TaskListQuery.kt @@ -6,27 +6,26 @@ import com.todoroo.andlib.sql.Join import com.todoroo.astrid.activity.TaskListFragment import com.todoroo.astrid.api.Filter import com.todoroo.astrid.data.Task +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.TaskListQueryNonRecursive.getNonRecursiveQuery import org.tasks.data.TaskListQueryRecursive.getRecursiveQuery import org.tasks.preferences.QueryPreferences object TaskListQuery { - private val JOIN_GTASK = Criterion.and( - Task.ID.eq(field("${TaskListFragment.GTASK_METADATA_JOIN}.gt_task")), - field("${TaskListFragment.GTASK_METADATA_JOIN}.gt_deleted").eq(0)) private val JOIN_CALDAV = Criterion.and( Task.ID.eq(field("${TaskListFragment.CALDAV_METADATA_JOIN}.cd_task")), field("${TaskListFragment.CALDAV_METADATA_JOIN}.cd_deleted").eq(0)) val JOINS = """ - ${Join.left(GoogleTask.TABLE.`as`(TaskListFragment.GTASK_METADATA_JOIN), JOIN_GTASK)} ${Join.left(CaldavTask.TABLE.`as`(TaskListFragment.CALDAV_METADATA_JOIN), JOIN_CALDAV)} + ${Join.left(CaldavCalendar.TABLE, field("${TaskListFragment.CALDAV_METADATA_JOIN}.cd_calendar").eq(CaldavCalendar.UUID))} + ${Join.left(CaldavAccount.TABLE, CaldavCalendar.ACCOUNT.eq(CaldavAccount.UUID))} ${Join.left(Geofence.TABLE, Geofence.TASK.eq(Task.ID))} ${Join.left(Place.TABLE, Place.UID.eq(Geofence.PLACE))} """.trimIndent() val FIELDS = listOf( field("tasks.*"), - field("${TaskListFragment.GTASK_METADATA_JOIN}.*"), field("${TaskListFragment.CALDAV_METADATA_JOIN}.*"), + field("CASE ${CaldavAccount.ACCOUNT_TYPE} WHEN $TYPE_GOOGLE_TASKS THEN 1 ELSE 0 END").`as`("isGoogleTask"), field("geofences.*"), field("places.*")) @@ -37,11 +36,11 @@ object TaskListQuery { subtasks: SubtaskInfo ): MutableList = when { filter.supportsManualSort() && preferences.isManualSort -> - getRecursiveQuery(filter, preferences, subtasks) + getRecursiveQuery(filter, preferences) filter.supportsAstridSorting() && preferences.isAstridSort -> getNonRecursiveQuery(filter, preferences) filter.supportsSubtasks() && subtasks.usesSubtasks() && !preferences.usePagedQueries() -> - getRecursiveQuery(filter, preferences, subtasks) + getRecursiveQuery(filter, preferences) else -> getNonRecursiveQuery(filter, preferences) } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt b/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt index 521c199cd..2b7955744 100644 --- a/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt +++ b/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt @@ -33,47 +33,27 @@ internal object TaskListQueryRecursive { LEFT JOIN (SELECT parent, count(distinct recursive_tasks.task) AS children FROM recursive_tasks GROUP BY parent) AS recursive_children ON recursive_children.parent = tasks._id ${TaskListQuery.JOINS} """.trimIndent() - private val GOOGLE_SUBTASKS = + private val SUBTASK_QUERY = QueryTemplate() - .join(Join.inner(RECURSIVE, GoogleTask.PARENT.eq(RECURSIVE_TASK))) - .join(Join.inner(GoogleTask.TABLE, Criterion.and(GoogleTask.TASK.eq(Task.ID), GoogleTask.DELETED.eq(0)))) - .where(activeAndVisible()) - private val ALL_SUBTASKS = - QueryTemplate() - .join(Join.inner(RECURSIVE, Criterion.or(GoogleTask.PARENT.eq(RECURSIVE_TASK), Task.PARENT.eq(RECURSIVE_TASK)))) - .join(Join.left(GoogleTask.TABLE, Criterion.and(GoogleTask.TASK.eq(Task.ID), GoogleTask.DELETED.eq(0)))) + .join(Join.inner(RECURSIVE, Task.PARENT.eq(RECURSIVE_TASK))) .where(activeAndVisible()) fun getRecursiveQuery( filter: Filter, preferences: QueryPreferences, - subtasks: SubtaskInfo ): MutableList { var joinedQuery = JOINS var where = " WHERE recursive_tasks.hidden = 0" val parentQuery: String - val subtaskQuery: QueryTemplate when (filter) { is CaldavFilter -> { - parentQuery = newCaldavQuery(filter) - subtaskQuery = QueryTemplate() - .join(Join.inner(RECURSIVE, Task.PARENT.eq(RECURSIVE_TASK))) - .join(Join.inner(CaldavTask.TABLE, Criterion.and(CaldavTask.TASK.eq(Task.ID), CaldavTask.DELETED.eq(0)))) - .where(activeAndVisible()) + parentQuery = newCaldavQuery(filter.uuid) } is GtasksFilter -> { - parentQuery = newGoogleTaskQuery(filter) - subtaskQuery = GOOGLE_SUBTASKS + parentQuery = newCaldavQuery(filter.list.uuid!!) } else -> { parentQuery = PermaSql.replacePlaceholdersForQuery(filter.getSqlQuery()) - subtaskQuery = when { - subtasks.hasGoogleSubtasks && subtasks.hasSubtasks -> ALL_SUBTASKS - subtasks.hasGoogleSubtasks -> GOOGLE_SUBTASKS - else -> QueryTemplate() - .join(Join.inner(RECURSIVE, Task.PARENT.eq(RECURSIVE_TASK))) - .where(activeAndVisible()) - } joinedQuery += " LEFT JOIN (SELECT task, max(indent) AS max_indent FROM recursive_tasks GROUP BY task) AS recursive_indents ON recursive_indents.task = tasks._id " where += " AND indent = max_indent " } @@ -85,7 +65,7 @@ internal object TaskListQueryRecursive { when { manualSort && filter is GtasksFilter -> { sortMode = SortHelper.SORT_GTASKS - sortField = "google_tasks.gt_order" + sortField = "tasks.`order`" } manualSort && filter is CaldavFilter -> { sortMode = SortHelper.SORT_CALDAV @@ -111,7 +91,7 @@ internal object TaskListQueryRecursive { SELECT tasks._id, $parentCompleted as parent_complete, 0 as subtask_complete, $completionSort as completion_sort, 0 as parent, tasks.collapsed as collapsed, 0 as hidden, 0 AS sort_indent, UPPER(tasks.title) AS sort_title, $sortSelect, $sortField as primary_sort, NULL as secondarySort, ${SortHelper.getSortGroup(sortMode)} FROM tasks $parentQuery UNION ALL SELECT tasks._id, recursive_tasks.parent_complete, $parentCompleted as subtask_complete, $completionSort as completion_sort, recursive_tasks.task as parent, tasks.collapsed as collapsed, CASE WHEN recursive_tasks.collapsed > 0 OR recursive_tasks.hidden > 0 THEN 1 ELSE 0 END as hidden, recursive_tasks.indent+1 AS sort_indent, UPPER(tasks.title) AS sort_title, $sortSelect, recursive_tasks.primary_sort as primary_sort, $sortField as secondary_sort, recursive_tasks.sort_group FROM tasks - $subtaskQuery + $SUBTASK_QUERY ORDER BY parent_complete ASC, sort_indent DESC, subtask_complete ASC, completion_sort DESC, ${SortHelper.orderForSortTypeRecursive(sortMode, reverseSort)} ) SELECT * FROM recursive_tasks """.trimIndent() @@ -127,26 +107,14 @@ internal object TaskListQueryRecursive { .toString()) } - private fun newCaldavQuery(filter: CaldavFilter) = + private fun newCaldavQuery(list: String) = QueryTemplate() .join(Join.inner( CaldavTask.TABLE, Criterion.and( - CaldavTask.CALENDAR.eq(filter.uuid), + CaldavTask.CALENDAR.eq(list), CaldavTask.TASK.eq(Task.ID), CaldavTask.DELETED.eq(0)))) .where(Criterion.and(activeAndVisible(), Task.PARENT.eq(0))) .toString() - - private fun newGoogleTaskQuery(filter: GtasksFilter) = - QueryTemplate() - .join(Join.inner( - GoogleTask.TABLE, - Criterion.and( - GoogleTask.LIST.eq(filter.remoteId), - GoogleTask.PARENT.eq(0), - GoogleTask.TASK.eq(Task.ID), - GoogleTask.DELETED.eq(0)))) - .where(activeAndVisible()) - .toString() } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/db/Migrations.kt b/app/src/main/java/org/tasks/db/Migrations.kt index 99e48302a..53e76b7f8 100644 --- a/app/src/main/java/org/tasks/db/Migrations.kt +++ b/app/src/main/java/org/tasks/db/Migrations.kt @@ -16,6 +16,8 @@ import org.tasks.data.Alarm.Companion.TYPE_REL_END import org.tasks.data.Alarm.Companion.TYPE_REL_START import org.tasks.data.Alarm.Companion.TYPE_SNOOZE import org.tasks.data.CaldavAccount.Companion.SERVER_UNKNOWN +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS +import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_ONLY import org.tasks.data.OpenTaskDao.Companion.getLong import org.tasks.extensions.getLongOrNull @@ -588,6 +590,27 @@ object Migrations { } } + private val MIGRATION_87_88 = object : Migration(87, 88) { + override fun migrate(database: SupportSQLiteDatabase) { + // migrate google task accounts and lists to caldav table + database.execSQL("ALTER TABLE `caldav_lists` ADD COLUMN `cdl_last_sync` INTEGER NOT NULL DEFAULT 0") + database.execSQL("INSERT INTO `caldav_accounts` (`cda_account_type`, `cda_server_type`, `cda_uuid`, `cda_name`, `cda_username`, `cda_collapsed`) SELECT $TYPE_GOOGLE_TASKS, $SERVER_UNKNOWN, `gta_account`, `gta_account`, `gta_account`, `gta_collapsed` FROM `google_task_accounts`") + database.execSQL("INSERT INTO `caldav_lists` (`cdl_account`, `cdl_uuid`, `cdl_name`, `cdl_color`, `cdl_icon`, `cdl_order`, `cdl_access`, `cdl_last_sync`) SELECT `gtl_account`, `gtl_remote_id`, `gtl_title`, `gtl_color`, `gtl_icon`, `gtl_remote_order`, $ACCESS_OWNER, `gtl_last_sync` FROM `google_task_lists`") + database.execSQL("DROP TABLE `google_task_accounts`") + database.execSQL("DROP TABLE `google_task_lists`") + // move cd_order to task table + database.execSQL("ALTER TABLE `tasks` ADD COLUMN `order` INTEGER") + database.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav-temp`") + database.execSQL("CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, FOREIGN KEY(`cd_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE)") + database.execSQL("DROP INDEX `index_caldav_tasks_cd_task`") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_caldav_tasks_cd_task` ON `caldav_tasks` (`cd_task`)") + database.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `gt_moved`, `gt_remote_order`) SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, 0, 0 FROM `caldav-temp`") + database.execSQL("DROP TABLE `caldav-temp`") + database.execSQL("INSERT INTO `caldav_tasks` (`cd_task`, `cd_calendar`, `cd_remote_id`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `gt_moved`, `gt_remote_order`) SELECT `gt_task`, `gt_list_id`, `gt_remote_id`, `gt_last_sync`, `gt_deleted`, `gt_remote_parent`, 0, 0 FROM google_tasks") + database.execSQL("DROP TABLE `google_tasks`") + } + } + fun migrations(fileStorage: FileStorage) = arrayOf( MIGRATION_35_36, MIGRATION_36_37, @@ -631,6 +654,7 @@ object Migrations { MIGRATION_84_85, MIGRATION_85_86, MIGRATION_86_87, + MIGRATION_87_88, ) private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) { diff --git a/app/src/main/java/org/tasks/dialogs/FilterPickerViewModel.kt b/app/src/main/java/org/tasks/dialogs/FilterPickerViewModel.kt index 3997b0d79..b838977dc 100644 --- a/app/src/main/java/org/tasks/dialogs/FilterPickerViewModel.kt +++ b/app/src/main/java/org/tasks/dialogs/FilterPickerViewModel.kt @@ -19,7 +19,6 @@ import org.tasks.LocalBroadcastManager import org.tasks.R import org.tasks.billing.Inventory import org.tasks.data.CaldavDao -import org.tasks.data.GoogleTaskDao import org.tasks.dialogs.FilterPicker.Companion.EXTRA_LISTS_ONLY import org.tasks.filters.FilterProvider import org.tasks.filters.NavigationDrawerSubheader @@ -37,7 +36,6 @@ class FilterPickerViewModel @Inject constructor( private val inventory: Inventory, private val colorProvider: ColorProvider, private val preferences: Preferences, - private val googleTaskDao: GoogleTaskDao, private val caldavDao: CaldavDao, ) : ViewModel() { private val listsOnly = savedStateHandle[EXTRA_LISTS_ONLY] ?: false @@ -70,8 +68,7 @@ class FilterPickerViewModel @Inject constructor( when (subheader.subheaderType) { NavigationDrawerSubheader.SubheaderType.PREFERENCE -> preferences.setBoolean(subheader.id.toInt(), collapsed) - NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS -> - googleTaskDao.setCollapsed(subheader.id, collapsed) + NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS, NavigationDrawerSubheader.SubheaderType.CALDAV, NavigationDrawerSubheader.SubheaderType.TASKS, NavigationDrawerSubheader.SubheaderType.ETESYNC -> diff --git a/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt b/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt index 00b1e7d9b..fe7156efd 100644 --- a/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt +++ b/app/src/main/java/org/tasks/filters/FilterCriteriaProvider.kt @@ -6,25 +6,13 @@ import com.todoroo.andlib.sql.Criterion.Companion.exists import com.todoroo.andlib.sql.Criterion.Companion.or import com.todoroo.andlib.sql.Field.Companion.field import com.todoroo.andlib.sql.Join.Companion.inner -import com.todoroo.andlib.sql.Join.Companion.left import com.todoroo.andlib.sql.Query.Companion.select import com.todoroo.andlib.sql.UnaryCriterion.Companion.isNotNull -import com.todoroo.astrid.api.BooleanCriterion -import com.todoroo.astrid.api.CustomFilterCriterion -import com.todoroo.astrid.api.MultipleSelectCriterion -import com.todoroo.astrid.api.PermaSql -import com.todoroo.astrid.api.TextInputCriterion +import com.todoroo.astrid.api.* import com.todoroo.astrid.data.Task import dagger.hilt.android.qualifiers.ApplicationContext import org.tasks.R -import org.tasks.data.Alarm -import org.tasks.data.CaldavDao -import org.tasks.data.CaldavTask -import org.tasks.data.GoogleTask -import org.tasks.data.GoogleTaskListDao -import org.tasks.data.Tag -import org.tasks.data.TagData -import org.tasks.data.TagDataDao +import org.tasks.data.* import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import javax.inject.Inject @@ -147,12 +135,8 @@ class FilterCriteriaProvider @Inject constructor( context.getString(R.string.custom_filter_has_subtask), select(Task.ID) .from(Task.TABLE) - .join(left(Task.TABLE.`as`("children"), Task.ID.eq(field("children.parent")))) - .join(left(GoogleTask.TABLE, GoogleTask.PARENT.eq(Task.ID))) - .where(or( - isNotNull(field("children._id")), - isNotNull(GoogleTask.ID) - )) + .join(inner(Task.TABLE.`as`("children"), Task.ID.eq(field("children.parent")))) + .where(isNotNull(field("children._id"))) .toString() ) @@ -162,11 +146,7 @@ class FilterCriteriaProvider @Inject constructor( context.getString(R.string.custom_filter_is_subtask), select(Task.ID) .from(Task.TABLE) - .join(left(GoogleTask.TABLE, GoogleTask.TASK.eq(Task.ID))) - .where(or( - field("${Task.PARENT}>0").eq(1), - field("${GoogleTask.PARENT}>0").eq(1) - )) + .where(field("${Task.PARENT}>0").eq(1)) .toString() ) @@ -297,26 +277,26 @@ class FilterCriteriaProvider @Inject constructor( r.getString(R.string.CFC_title_contains_name)) private suspend fun gtasksFilterCriteria(): CustomFilterCriterion { - val lists = googleTaskListDao.getAllLists() + val lists = caldavDao.getGoogleTaskLists() val listNames = arrayOfNulls(lists.size) val listIds = arrayOfNulls(lists.size) for (i in lists.indices) { - listNames[i] = lists[i].title - listIds[i] = lists[i].remoteId + listNames[i] = lists[i].name + listIds[i] = lists[i].uuid } val values: MutableMap = HashMap() values[GoogleTask.KEY] = "?" return MultipleSelectCriterion( IDENTIFIER_GTASKS, context.getString(R.string.CFC_gtasks_list_text), - select(GoogleTask.TASK) - .from(GoogleTask.TABLE) - .join(inner(Task.TABLE, GoogleTask.TASK.eq(Task.ID))) + select(CaldavTask.TASK) + .from(CaldavTask.TABLE) + .join(inner(Task.TABLE, CaldavTask.TASK.eq(Task.ID))) .where( and( activeAndVisible(), - GoogleTask.DELETED.eq(0), - GoogleTask.LIST.eq("?"))) + CaldavTask.DELETED.eq(0), + CaldavTask.CALENDAR.eq("?"))) .toString(), values, listNames, diff --git a/app/src/main/java/org/tasks/filters/FilterProvider.kt b/app/src/main/java/org/tasks/filters/FilterProvider.kt index 4d531a74e..5e485f4f5 100644 --- a/app/src/main/java/org/tasks/filters/FilterProvider.kt +++ b/app/src/main/java/org/tasks/filters/FilterProvider.kt @@ -39,7 +39,7 @@ class FilterProvider @Inject constructor( private val locationDao: LocationDao) { suspend fun listPickerItems(): List = - googleTaskFilters(false).plus(caldavFilters(false)) + caldavFilters(false) suspend fun navDrawerItems(): List = getAllFilters(hideUnused = true).plus(navDrawerFooter) @@ -166,7 +166,6 @@ class FilterProvider @Inject constructor( .plus(addFilters(showCreate, showBuiltIn)) .plus(addTags(showCreate, hideUnused)) .plus(addPlaces(showCreate, hideUnused)) - .plus(googleTaskFilters(showCreate)) .plus(caldavFilters(showCreate)) .toList() .plusAllIf(BuildConfig.DEBUG) { getDebugFilters() } @@ -201,13 +200,10 @@ class FilterProvider @Inject constructor( Intent(context, HelpAndFeedback::class.java), 0)) - private suspend fun googleTaskFilters(showCreate: Boolean = true): List = - googleTaskListDao.getAccounts().flatMap { googleTaskFilter(it, showCreate) } - - private suspend fun googleTaskFilter(account: GoogleTaskAccount, showCreate: Boolean): List = + private suspend fun googleTaskFilter(account: CaldavAccount, showCreate: Boolean): List = listOf( NavigationDrawerSubheader( - account.account, + account.username, account.error?.isNotBlank() ?: false, account.isCollapsed, SubheaderType.GOOGLE_TASKS, @@ -221,7 +217,7 @@ class FilterProvider @Inject constructor( })) .apply { if (account.isCollapsed) return this } .plus(googleTaskListDao - .getGoogleTaskFilters(account.account!!) + .getGoogleTaskFilters(account.username!!) .map(GoogleTaskFilters::toGtasksFilter) .sort()) @@ -229,7 +225,16 @@ class FilterProvider @Inject constructor( caldavDao.getAccounts() .ifEmpty { listOf(caldavDao.setupLocalAccount(context)) } .filter { it.accountType != TYPE_LOCAL || preferences.getBoolean(R.string.p_lists_enabled, true) } - .flatMap { caldavFilter(it, showCreate && it.accountType != TYPE_OPENTASKS && it.accountType != TYPE_ETESYNC) } + .flatMap { + if (it.isGoogleTasks) { + googleTaskFilter(it, showCreate) + } else { + caldavFilter( + it, + showCreate && it.accountType != TYPE_OPENTASKS && it.accountType != TYPE_ETESYNC + ) + } + } private suspend fun caldavFilter(account: CaldavAccount, showCreate: Boolean): List = listOf( diff --git a/app/src/main/java/org/tasks/filters/GoogleTaskFilters.java b/app/src/main/java/org/tasks/filters/GoogleTaskFilters.java index f7078b8cf..4f5e17512 100644 --- a/app/src/main/java/org/tasks/filters/GoogleTaskFilters.java +++ b/app/src/main/java/org/tasks/filters/GoogleTaskFilters.java @@ -1,12 +1,15 @@ package org.tasks.filters; import androidx.room.Embedded; + import com.todoroo.astrid.api.GtasksFilter; + +import org.tasks.data.CaldavCalendar; + import java.util.Objects; -import org.tasks.data.GoogleTaskList; public class GoogleTaskFilters { - @Embedded public GoogleTaskList googleTaskList; + @Embedded public CaldavCalendar googleTaskList; public int count; GtasksFilter toGtasksFilter() { diff --git a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt index a73fbf7ab..d96a0dc14 100644 --- a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt +++ b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt @@ -43,6 +43,7 @@ import kotlin.math.max class GoogleTaskSynchronizer @Inject constructor( @param:ApplicationContext private val context: Context, private val googleTaskListDao: GoogleTaskListDao, + private val caldavDao: CaldavDao, private val gtasksListService: GtasksListService, private val preferences: Preferences, private val taskDao: TaskDao, @@ -58,7 +59,7 @@ class GoogleTaskSynchronizer @Inject constructor( private val invokers: InvokerFactory, private val alarmDao: AlarmDao, ) { - suspend fun sync(account: GoogleTaskAccount, i: Int) { + suspend fun sync(account: CaldavAccount, i: Int) { Timber.d("%s: start sync", account) try { if (i == 0 || inventory.hasPro) { @@ -94,20 +95,20 @@ class GoogleTaskSynchronizer @Inject constructor( account.error = e.message firebase.reportException(e) } finally { - googleTaskListDao.update(account) + caldavDao.update(account) localBroadcastManager.broadcastRefreshList() Timber.d("%s: end sync", account) } } @Throws(IOException::class) - private suspend fun synchronize(account: GoogleTaskAccount) { + private suspend fun synchronize(account: CaldavAccount) { if (!permissionChecker.canAccessAccounts() - || googleAccountManager.getAccount(account.account) == null) { + || googleAccountManager.getAccount(account.username) == null) { account.error = context.getString(R.string.cannot_access_account) return } - val gtasksInvoker = invokers.getGtasksInvoker(account.account!!) + val gtasksInvoker = invokers.getGtasksInvoker(account.username!!) pushLocalChanges(account, gtasksInvoker) val gtaskLists: MutableList = ArrayList() var nextPageToken: String? = null @@ -130,13 +131,13 @@ class GoogleTaskSynchronizer @Inject constructor( } } for (list in googleTaskListDao.getByRemoteId(gtaskLists.map { it.id })) { - if (isNullOrEmpty(list.remoteId)) { + if (isNullOrEmpty(list.uuid)) { firebase.reportException(RuntimeException("Empty remote id")) continue } fetchAndApplyRemoteChanges(gtasksInvoker, list) if (!preferences.isPositionHackEnabled) { - googleTaskDao.reposition(list.remoteId!!) + googleTaskDao.reposition(caldavDao, list.uuid!!) } } if (preferences.isPositionHackEnabled) { @@ -145,10 +146,10 @@ class GoogleTaskSynchronizer @Inject constructor( for (task in tasks) { googleTaskDao.updatePosition(task.id, task.parent, task.position) } - googleTaskDao.reposition(list.id) + googleTaskDao.reposition(caldavDao, list.id) } } - account.etag = eTag +// account.etag = eTag account.error = "" } @@ -168,8 +169,8 @@ class GoogleTaskSynchronizer @Inject constructor( } @Throws(IOException::class) - private suspend fun pushLocalChanges(account: GoogleTaskAccount, gtasksInvoker: GtasksInvoker) { - val tasks = taskDao.getGoogleTasksToPush(account.account!!) + private suspend fun pushLocalChanges(account: CaldavAccount, gtasksInvoker: GtasksInvoker) { + val tasks = taskDao.getGoogleTasksToPush(account.uuid!!) for (task in tasks) { pushTask(task, gtasksInvoker) } @@ -178,7 +179,7 @@ class GoogleTaskSynchronizer @Inject constructor( @Throws(IOException::class) private suspend fun pushTask(task: com.todoroo.astrid.data.Task, gtasksInvoker: GtasksInvoker) { for (deleted in googleTaskDao.getDeletedByTaskId(task.id)) { - gtasksInvoker.deleteGtask(deleted.listId, deleted.remoteId) + gtasksInvoker.deleteGtask(deleted.calendar, deleted.remoteId) googleTaskDao.delete(deleted) } val gtasksMetadata = googleTaskDao.getByTaskId(task.id) ?: return @@ -188,14 +189,14 @@ class GoogleTaskSynchronizer @Inject constructor( val defaultRemoteList = defaultFilterProvider.defaultList var listId = if (defaultRemoteList is GtasksFilter) defaultRemoteList.remoteId else DEFAULT_LIST if (isNullOrEmpty(gtasksMetadata.remoteId)) { // Create case - val selectedList = gtasksMetadata.listId + val selectedList = gtasksMetadata.calendar if (!isNullOrEmpty(selectedList)) { listId = selectedList } newlyCreated = true } else { // update case remoteId = gtasksMetadata.remoteId - listId = gtasksMetadata.listId + listId = gtasksMetadata.calendar remoteModel.id = remoteId } @@ -223,12 +224,11 @@ class GoogleTaskSynchronizer @Inject constructor( remoteModel.status = "needsAction" // $NON-NLS-1$ } if (newlyCreated) { - val parent = gtasksMetadata.parent + val parent = task.parent val localParent = if (parent > 0) googleTaskDao.getRemoteId(parent) else null val previous = googleTaskDao.getPrevious( - listId!!, if (isNullOrEmpty(localParent)) 0 else parent, gtasksMetadata.order) - val created: Task? - created = try { + listId!!, if (isNullOrEmpty(localParent)) 0 else parent, task.order ?: 0) + val created: Task? = try { gtasksInvoker.createGtask(listId, remoteModel, localParent, previous) } catch (e: HttpNotFoundException) { gtasksInvoker.createGtask(listId, remoteModel, null, null) @@ -236,8 +236,8 @@ class GoogleTaskSynchronizer @Inject constructor( if (created != null) { // Update the metadata for the newly created task gtasksMetadata.remoteId = created.id - gtasksMetadata.listId = listId - setOrderAndParent(gtasksMetadata, created) + gtasksMetadata.calendar = listId + setOrderAndParent(gtasksMetadata, created, task) } else { return } @@ -245,15 +245,15 @@ class GoogleTaskSynchronizer @Inject constructor( try { if (!task.isDeleted && gtasksMetadata.isMoved) { try { - val parent = gtasksMetadata.parent + val parent = task.parent val localParent = if (parent > 0) googleTaskDao.getRemoteId(parent) else null val previous = googleTaskDao.getPrevious( listId!!, - if (isNullOrEmpty(localParent)) 0 else parent, - gtasksMetadata.order) + if (localParent.isNullOrBlank()) 0 else parent, + task.order ?: 0) gtasksInvoker .moveGtask(listId, remoteModel.id, localParent, previous) - ?.let { setOrderAndParent(gtasksMetadata, it) } + ?.let { setOrderAndParent(gtasksMetadata, it, task) } } catch (e: GoogleJsonResponseException) { if (e.statusCode == 400) { Timber.e(e) @@ -275,8 +275,10 @@ class GoogleTaskSynchronizer @Inject constructor( @Throws(IOException::class) private suspend fun fetchAndApplyRemoteChanges( - gtasksInvoker: GtasksInvoker, list: GoogleTaskList) { - val listId = list.remoteId + gtasksInvoker: GtasksInvoker, + list: CaldavCalendar + ) { + val listId = list.uuid var lastSyncDate = list.lastSync val tasks: MutableList = ArrayList() var nextPageToken: String? = null @@ -300,7 +302,7 @@ class GoogleTaskSynchronizer @Inject constructor( var googleTask = googleTaskDao.getByRemoteId(remoteId) var task: com.todoroo.astrid.data.Task? = null if (googleTask == null) { - googleTask = GoogleTask(0, "") + googleTask = CaldavTask(0, "", remoteId = null) } else if (googleTask.task > 0) { task = taskDao.fetch(googleTask.task) } @@ -325,33 +327,36 @@ class GoogleTaskSynchronizer @Inject constructor( continue } } else { - setOrderAndParent(googleTask, gtask) + if (task == null) { + task = taskCreator.createWithValues("") + } + setOrderAndParent(googleTask, gtask, task) googleTask.remoteId = gtask.id } - if (task == null) { - task = taskCreator.createWithValues("") - } task.title = getTruncatedValue(task.title, gtask.title, MAX_TITLE_LENGTH) task.completionDate = GtasksApiUtilities.gtasksCompletedTimeToUnixTime(gtask.completed?.let(::DateTime)) val dueDate = GtasksApiUtilities.gtasksDueTimeToUnixTime(gtask.due?.let(::DateTime)) mergeDates(createDueDate(com.todoroo.astrid.data.Task.URGENCY_SPECIFIC_DAY, dueDate), task) task.notes = getTruncatedValue(task.notes, gtask.notes, MAX_DESCRIPTION_LENGTH) - googleTask.listId = listId + googleTask.calendar = listId if (task.title?.isNotBlank() == true || task.notes?.isNotBlank() == true) { write(task, googleTask) } } - list.lastSync = lastSyncDate - googleTaskListDao.insertOrReplace(list) + googleTaskListDao.insertOrReplace( + list.copy( + lastSync = lastSyncDate + ) + ) } - private suspend fun setOrderAndParent(googleTask: GoogleTask, task: Task) { + private suspend fun setOrderAndParent(googleTask: CaldavTask, task: Task, local: com.todoroo.astrid.data.Task) { task.position?.toLongOrNull()?.let { googleTask.remoteOrder = it } googleTask.remoteParent = task.parent?.takeIf { it.isNotBlank() } - googleTask.parent = googleTask.remoteParent?.let { googleTaskDao.getTask(it) } ?: 0L + local.parent = googleTask.remoteParent?.let { googleTaskDao.getTask(it) } ?: 0L } - private suspend fun write(task: com.todoroo.astrid.data.Task, googleTask: GoogleTask) { + private suspend fun write(task: com.todoroo.astrid.data.Task, googleTask: CaldavTask) { task.suppressSync() task.suppressRefresh() if (task.isNew) { @@ -361,7 +366,7 @@ class GoogleTaskSynchronizer @Inject constructor( taskDao.save(task) googleTask.lastSync = task.modificationDate googleTask.task = task.id - if (googleTask.isNew) { + if (googleTask.id == 0L) { googleTaskDao.insert(googleTask) } else { googleTaskDao.update(googleTask) diff --git a/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt b/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt index 87e22ca1b..bbbf439d9 100644 --- a/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt +++ b/app/src/main/java/org/tasks/jobs/WorkManagerImpl.kt @@ -19,6 +19,7 @@ import org.tasks.R import org.tasks.data.* import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV import org.tasks.data.CaldavAccount.Companion.TYPE_ETEBASE +import org.tasks.data.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.CaldavAccount.Companion.TYPE_TASKS import org.tasks.date.DateTimeUtils.midnight import org.tasks.date.DateTimeUtils.newDateTime @@ -122,8 +123,8 @@ class WorkManagerImpl constructor( override fun updateBackgroundSync() { throttle.run { - val enabled = googleTaskListDao.accountCount() > 0 || - caldavDao.getAccounts(TYPE_CALDAV, TYPE_TASKS, TYPE_ETEBASE).isNotEmpty() || + val enabled = + caldavDao.getAccounts(TYPE_GOOGLE_TASKS, TYPE_CALDAV, TYPE_TASKS, TYPE_ETEBASE).isNotEmpty() || openTaskDao.shouldSync() if (enabled) { Timber.d("Enabling background sync") diff --git a/app/src/main/java/org/tasks/preferences/DefaultFilterProvider.kt b/app/src/main/java/org/tasks/preferences/DefaultFilterProvider.kt index 282dd5d47..19fb1b540 100644 --- a/app/src/main/java/org/tasks/preferences/DefaultFilterProvider.kt +++ b/app/src/main/java/org/tasks/preferences/DefaultFilterProvider.kt @@ -78,7 +78,7 @@ class DefaultFilterProvider @Inject constructor( getFilterFromPreference(prefString, getMyTasksFilter(context.resources))!! private suspend fun getAnyList(): Filter { - val filter = googleTaskListDao.getAllLists().getOrNull(0)?.let(::GtasksFilter) + val filter = caldavDao.getGoogleTaskLists().getOrNull(0)?.let(::GtasksFilter) ?: caldavDao.getCalendars().filterNot { it.access == ACCESS_READ_ONLY }.getOrElse(0) { caldavDao.getLocalList(context) }.let(::CaldavFilter) defaultList = filter return filter @@ -100,7 +100,8 @@ class DefaultFilterProvider @Inject constructor( val tag = tagDataDao.getByUuid(split[1]) if (tag == null || isNullOrEmpty(tag.name)) null else TagFilter(tag) } - TYPE_GOOGLE_TASKS -> googleTaskListDao.getById(split[1].toLong())?.let { GtasksFilter(it) } + // TODO: convert filters from old ID to uuid + TYPE_GOOGLE_TASKS -> caldavDao.getCalendarByUuid(split[1])?.let { GtasksFilter(it) } TYPE_CALDAV -> caldavDao.getCalendarByUuid(split[1])?.let { CaldavFilter(it) } TYPE_LOCATION -> locationDao.getPlace(split[1])?.let { PlaceFilter(it) } else -> null @@ -114,7 +115,7 @@ class DefaultFilterProvider @Inject constructor( TYPE_FILTER -> getFilterPreference(filterType, getBuiltInFilterId(filter)) TYPE_CUSTOM_FILTER -> getFilterPreference(filterType, (filter as CustomFilter).id) TYPE_TAG -> getFilterPreference(filterType, (filter as TagFilter).uuid) - TYPE_GOOGLE_TASKS -> getFilterPreference(filterType, (filter as GtasksFilter).storeId) + TYPE_GOOGLE_TASKS -> getFilterPreference(filterType, (filter as GtasksFilter).remoteId) TYPE_CALDAV -> getFilterPreference(filterType, (filter as CaldavFilter).uuid) TYPE_LOCATION -> getFilterPreference(filterType, (filter as PlaceFilter).uid) else -> null @@ -169,7 +170,7 @@ class DefaultFilterProvider @Inject constructor( val googleTask = googleTaskDao.getByTaskId(task.id) val caldavTask = caldavDao.getTask(task.id) if (googleTask != null) { - val googleTaskList = googleTaskListDao.getByRemoteId(googleTask.listId!!) + val googleTaskList = googleTaskListDao.getByRemoteId(googleTask.calendar!!) if (googleTaskList != null) { originalList = GtasksFilter(googleTaskList) } diff --git a/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt b/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt index 3b3b1024f..a00d38c82 100644 --- a/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt +++ b/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt @@ -14,7 +14,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.tasks.R import org.tasks.backup.BackupConstants -import org.tasks.data.* +import org.tasks.data.CaldavAccount +import org.tasks.data.CaldavDao import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.googleapis.InvokerFactory import org.tasks.gtasks.GoogleAccountManager @@ -29,13 +30,11 @@ class PreferencesViewModel @Inject constructor( invokers: InvokerFactory, private val googleAccountManager: GoogleAccountManager, caldavDao: CaldavDao, - googleTaskListDao: GoogleTaskListDao, ) : ViewModel() { private val driveInvoker = invokers.getDriveInvoker() val lastBackup = MutableLiveData() val lastDriveBackup = MutableLiveData() val lastAndroidBackup = MutableLiveData() - var googleTaskAccounts = googleTaskListDao.watchAccounts() var caldavAccounts = caldavDao.watchAccounts() private fun isStale(timestamp: Long?) = diff --git a/app/src/main/java/org/tasks/preferences/fragments/GoogleTasksAccount.kt b/app/src/main/java/org/tasks/preferences/fragments/GoogleTasksAccount.kt index 6d58f077b..520de4998 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/GoogleTasksAccount.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/GoogleTasksAccount.kt @@ -1,6 +1,8 @@ package org.tasks.preferences.fragments -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent import android.os.Bundle import androidx.lifecycle.LiveData import androidx.lifecycle.lifecycleScope @@ -11,9 +13,9 @@ import kotlinx.coroutines.launch import org.tasks.LocalBroadcastManager import org.tasks.R import org.tasks.billing.Inventory +import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount.Companion.isPaymentRequired -import org.tasks.data.GoogleTaskAccount -import org.tasks.data.GoogleTaskListDao +import org.tasks.data.CaldavDao import org.tasks.preferences.IconPreference import javax.inject.Inject @@ -23,11 +25,11 @@ class GoogleTasksAccount : BaseAccountPreference() { @Inject lateinit var taskDeleter: TaskDeleter @Inject lateinit var inventory: Inventory @Inject lateinit var localBroadcastManager: LocalBroadcastManager - @Inject lateinit var googleTaskListDao: GoogleTaskListDao + @Inject lateinit var caldavDao: CaldavDao - private lateinit var googleTaskAccountLiveData: LiveData + private lateinit var googleTaskAccountLiveData: LiveData - val googleTaskAccount: GoogleTaskAccount + val googleTaskAccount: CaldavAccount get() = googleTaskAccountLiveData.value ?: requireArguments().getParcelable(EXTRA_ACCOUNT)!! private val purchaseReceiver = object : BroadcastReceiver() { @@ -36,7 +38,7 @@ class GoogleTasksAccount : BaseAccountPreference() { googleTaskAccount.let { if (inventory.subscription.value != null && it.error.isPaymentRequired()) { it.error = null - googleTaskListDao.update(it) + caldavDao.update(it) } refreshUi(it) } @@ -49,8 +51,8 @@ class GoogleTasksAccount : BaseAccountPreference() { override suspend fun setupPreferences(savedInstanceState: Bundle?) { super.setupPreferences(savedInstanceState) - googleTaskAccountLiveData = googleTaskListDao.watchAccount( - arguments?.getParcelable(EXTRA_ACCOUNT)?.id ?: 0 + googleTaskAccountLiveData = caldavDao.watchAccount( + arguments?.getParcelable(EXTRA_ACCOUNT)?.id ?: 0 ) googleTaskAccountLiveData.observe(this) { refreshUi(it) } @@ -74,7 +76,7 @@ class GoogleTasksAccount : BaseAccountPreference() { localBroadcastManager.unregisterReceiver(purchaseReceiver) } - private fun refreshUi(account: GoogleTaskAccount?) { + private fun refreshUi(account: CaldavAccount?) { if (account == null) { return } @@ -119,7 +121,7 @@ class GoogleTasksAccount : BaseAccountPreference() { fun String?.isUnauthorized(): Boolean = this?.startsWith("401 Unauthorized", ignoreCase = true) == true - fun newGoogleTasksAccountPreference(account: GoogleTaskAccount) = + fun newGoogleTasksAccountPreference(account: CaldavAccount) = GoogleTasksAccount().apply { arguments = Bundle().apply { putParcelable(EXTRA_ACCOUNT, account) diff --git a/app/src/main/java/org/tasks/preferences/fragments/MainSettingsFragment.kt b/app/src/main/java/org/tasks/preferences/fragments/MainSettingsFragment.kt index addb6cd23..f0d941e7e 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/MainSettingsFragment.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/MainSettingsFragment.kt @@ -22,7 +22,6 @@ import org.tasks.billing.PurchaseActivity import org.tasks.caldav.BaseCaldavAccountSettingsActivity import org.tasks.caldav.CaldavAccountSettingsActivity import org.tasks.data.CaldavAccount -import org.tasks.data.GoogleTaskAccount import org.tasks.etebase.EtebaseAccountSettingsActivity import org.tasks.extensions.Context.openUri import org.tasks.extensions.Context.toast @@ -87,7 +86,6 @@ class MainSettingsFragment : InjectingPreferenceFragment() { viewModel.lastBackup.observe(this) { updateBackupWarning() } viewModel.lastAndroidBackup.observe(this) { updateBackupWarning() } viewModel.lastDriveBackup.observe(this) { updateBackupWarning() } - viewModel.googleTaskAccounts.observe(this) { refreshAccounts() } viewModel.caldavAccounts.observe(this) { refreshAccounts() } if (BuildConfig.FLAVOR == "generic") { remove(R.string.upgrade_to_pro) @@ -142,7 +140,6 @@ class MainSettingsFragment : InjectingPreferenceFragment() { private fun refreshAccounts() { val caldavAccounts = viewModel.caldavAccounts.value ?: emptyList() - val googleTaskAccounts = viewModel.googleTaskAccounts.value ?: emptyList() val addAccount = findPreference(R.string.add_account) val index = preferenceScreen.indexOf(addAccount) var current = 0 @@ -153,15 +150,8 @@ class MainSettingsFragment : InjectingPreferenceFragment() { preferenceScreen.insertAt(current++) }) } - googleTaskAccounts.forEach { - setup(it, if (current < index) { - preferenceScreen.getPreference(current++) as IconPreference - } else { - preferenceScreen.insertAt(current++) - }) - } preferenceScreen.removeAt(current, index - current) - if (caldavAccounts.isEmpty() && googleTaskAccounts.isEmpty()) { + if (caldavAccounts.isEmpty()) { addAccount.setTitle(R.string.not_signed_in) addAccount.setIcon(R.drawable.ic_outline_cloud_off_24px) } else { @@ -216,6 +206,12 @@ class MainSettingsFragment : InjectingPreferenceFragment() { newMicrosoftAccountPreference(account), getString(R.string.microsoft) ) + } else if (account.isGoogleTasks) { + (activity as MainPreferences).startPreference( + this, + newGoogleTasksAccountPreference(account), + getString(R.string.gtasks_GPr_header) + ) } else { val intent = Intent(context, account.accountSettingsClass).apply { putExtra(BaseCaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, account) @@ -239,21 +235,6 @@ class MainSettingsFragment : InjectingPreferenceFragment() { setupErrorIcon(pref, account.hasError, account.isEteSyncAccount) } - private fun setup(account: GoogleTaskAccount, pref: IconPreference) { - pref.setTitle(R.string.gtasks_GPr_header) - pref.setIcon(R.drawable.ic_google) - pref.summary = account.account - setupErrorIcon(pref, account.hasError) - pref.setOnPreferenceClickListener { - (activity as MainPreferences).startPreference( - this, - newGoogleTasksAccountPreference(account), - account.account!! - ) - false - } - } - private fun setupErrorIcon( pref: IconPreference, hasError: Boolean, diff --git a/app/src/main/java/org/tasks/provider/TasksContentProvider.kt b/app/src/main/java/org/tasks/provider/TasksContentProvider.kt index d5fb6bc18..296be16eb 100644 --- a/app/src/main/java/org/tasks/provider/TasksContentProvider.kt +++ b/app/src/main/java/org/tasks/provider/TasksContentProvider.kt @@ -45,7 +45,7 @@ class TasksContentProvider : ContentProvider() { } URI_TASKS -> hilt.contentProviderDao.getTasks() URI_LISTS -> hilt.contentProviderDao.getLists() - URI_GOOGLE_TASK_LISTS -> hilt.contentProviderDao.getGoogleTaskLists() + URI_GOOGLE_TASK_LISTS -> null else -> throw IllegalStateException("Unrecognized URI: $uri") } } @@ -70,8 +70,6 @@ class TasksContentProvider : ContentProvider() { companion object { private const val TODO_AGENDA_TABLES = """${Task.TABLE_NAME} - LEFT JOIN google_tasks ON gt_task = _id - LEFT JOIN google_task_lists ON gtl_remote_id = gt_list_id LEFT JOIN caldav_tasks ON cd_task = _id LEFT JOIN caldav_lists ON cdl_uuid = cd_calendar""" private const val AUTHORITY = BuildConfig.APPLICATION_ID diff --git a/app/src/main/java/org/tasks/ui/ChipListCache.kt b/app/src/main/java/org/tasks/ui/ChipListCache.kt index 0d2583bfd..43919188f 100644 --- a/app/src/main/java/org/tasks/ui/ChipListCache.kt +++ b/app/src/main/java/org/tasks/ui/ChipListCache.kt @@ -1,37 +1,26 @@ package org.tasks.ui -import com.todoroo.astrid.api.CaldavFilter -import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.TagFilter import org.tasks.LocalBroadcastManager -import org.tasks.data.* -import java.util.* +import org.tasks.data.CaldavCalendar +import org.tasks.data.CaldavDao +import org.tasks.data.TagData +import org.tasks.data.TagDataDao import javax.inject.Inject import javax.inject.Singleton @Singleton class ChipListCache @Inject internal constructor( - googleTaskListDao: GoogleTaskListDao, caldavDao: CaldavDao, tagDataDao: TagDataDao, private val localBroadcastManager: LocalBroadcastManager) { - private val googleTaskLists: MutableMap = HashMap() - private val caldavCalendars: MutableMap = HashMap() + private val caldavCalendars: MutableMap = HashMap() private val tagDatas: MutableMap = HashMap() - private fun updateGoogleTaskLists(updated: List) { - googleTaskLists.clear() - for (update in updated) { - googleTaskLists[update.remoteId] = GtasksFilter(update) - } - localBroadcastManager.broadcastRefresh() - } - private fun updateCaldavCalendars(updated: List) { caldavCalendars.clear() for (update in updated) { - caldavCalendars[update.uuid] = CaldavFilter(update) + caldavCalendars[update.uuid] = update } localBroadcastManager.broadcastRefresh() } @@ -44,14 +33,11 @@ class ChipListCache @Inject internal constructor( localBroadcastManager.broadcastRefresh() } - fun getGoogleTaskList(googleTaskList: String?): Filter? = googleTaskLists[googleTaskList] - - fun getCaldavList(caldav: String?): Filter? = caldavCalendars[caldav] + fun getCaldavList(caldav: String?): CaldavCalendar? = caldavCalendars[caldav] fun getTag(tag: String?): TagFilter? = tagDatas[tag] init { - googleTaskListDao.subscribeToLists().observeForever { updated: List -> updateGoogleTaskLists(updated) } caldavDao.subscribeToCalendars().observeForever { updated: List -> updateCaldavCalendars(updated) } tagDataDao.subscribeToTags().observeForever { updated: List -> updateTags(updated) } } diff --git a/app/src/main/java/org/tasks/ui/ChipProvider.kt b/app/src/main/java/org/tasks/ui/ChipProvider.kt index 4caa1f67d..1edc772a1 100644 --- a/app/src/main/java/org/tasks/ui/ChipProvider.kt +++ b/app/src/main/java/org/tasks/ui/ChipProvider.kt @@ -99,29 +99,16 @@ class ChipProvider @Inject constructor( ) } } - if (!isSubtask && preferences.showListChip) { - if (!isNullOrEmpty(task.googleTaskList) && filter !is GtasksFilter) { - lists.getGoogleTaskList(task.googleTaskList)?.let { list -> - FilterChip( - filter = list, - defaultIcon = R.drawable.ic_list_24px, - onClick = onClick, - showText = showText, - showIcon = showIcon, - colorProvider = this::getColor, - ) - } - } else if (!isNullOrEmpty(task.caldav) && filter !is CaldavFilter) { - lists.getCaldavList(task.caldav)?.let { list -> - FilterChip( - filter = list, - defaultIcon = R.drawable.ic_list_24px, - onClick = onClick, - showText = showText, - showIcon = showIcon, - colorProvider = this::getColor, - ) - } + if (!isSubtask && preferences.showListChip && filter !is CaldavFilter) { + lists.getCaldavList(task.caldav)?.let { list -> + FilterChip( + filter = if (task.isGoogleTask) GtasksFilter(list) else CaldavFilter(list), + defaultIcon = R.drawable.ic_list_24px, + onClick = onClick, + showText = showText, + showIcon = showIcon, + colorProvider = this::getColor, + ) } } val tagString = task.tagsString diff --git a/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt b/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt index 0b851e338..66d0340f7 100644 --- a/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt +++ b/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import org.tasks.LocalBroadcastManager import org.tasks.data.TaskDao import org.tasks.filters.FilterProvider +import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -51,7 +52,12 @@ class NavigationDrawerViewModel @Inject constructor( .navDrawerItems() .onEach { if (it is Filter && it.count == -1) { - it.count = taskDao.count(it) + it.count = try { + taskDao.count(it) + } catch (e: Exception) { + Timber.e(e) + 0 + } } } .let { filters -> _viewState.update { it.copy(filters = filters) } } diff --git a/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt b/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt index b4c9af8c8..06798fbec 100644 --- a/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt +++ b/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt @@ -10,7 +10,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.google.android.material.composethemeadapter.MdcTheme import com.todoroo.andlib.sql.Criterion -import com.todoroo.andlib.sql.Join import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.utility.DateUtilities.now import com.todoroo.astrid.api.Filter @@ -23,7 +22,6 @@ import kotlinx.coroutines.launch import org.tasks.R import org.tasks.compose.collectAsStateLifecycleAware import org.tasks.compose.edit.SubtaskRow -import org.tasks.data.GoogleTask import org.tasks.data.GoogleTaskDao import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible import org.tasks.preferences.Preferences @@ -57,8 +55,9 @@ class SubtaskControlSet : TaskEditControlFragment() { setContent { MdcTheme { SubtaskRow( + originalFilter = viewModel.originalList, filter = viewModel.selectedList.collectAsStateLifecycleAware().value, - googleTask = googleTaskDao.watchGoogleTask(viewModel.task.id).collectAsStateLifecycleAware(initial = null).value, + hasParent = viewModel.hasParent, desaturate = preferences.desaturateDarkMode, existingSubtasks = listViewModel.tasks.observeAsState(initial = emptyList()).value, newSubtasks = viewModel.newSubtasks.collectAsStateLifecycleAware().value, @@ -109,20 +108,10 @@ class SubtaskControlSet : TaskEditControlFragment() { companion object { const val TAG = R.string.TEA_ctrl_subtask_pref private fun getQueryTemplate(task: Task): QueryTemplate = QueryTemplate() - .join( - Join.left( - GoogleTask.TABLE, - Criterion.and( - GoogleTask.PARENT.eq(task.id), - GoogleTask.TASK.eq(Task.ID), - GoogleTask.DELETED.eq(0) - ) - ) - ) .where( Criterion.and( activeAndVisible(), - Criterion.or(Task.PARENT.eq(task.id), GoogleTask.TASK.gt(0)) + Task.PARENT.eq(task.id) ) ) } diff --git a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt index 92536db77..2ecc262cb 100644 --- a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt +++ b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt @@ -99,6 +99,8 @@ class TaskEditViewModel @Inject constructor( val estimatedSeconds = MutableStateFlow(task.estimatedSeconds) val elapsedSeconds = MutableStateFlow(task.elapsedSeconds) var newSubtasks = MutableStateFlow(emptyList()) + val hasParent: Boolean + get() = task.parent > 0 val dueDate = MutableStateFlow(task.dueDate) @@ -128,7 +130,7 @@ class TaskEditViewModel @Inject constructor( } var selectedCalendar = MutableStateFlow(originalCalendar) - private val originalList: Filter = savedStateHandle[TaskEditFragment.EXTRA_LIST]!! + val originalList: Filter = savedStateHandle[TaskEditFragment.EXTRA_LIST]!! var selectedList = MutableStateFlow(originalList) private var originalLocation: Location? = savedStateHandle[TaskEditFragment.EXTRA_LOCATION] @@ -292,10 +294,10 @@ class TaskEditViewModel @Inject constructor( firebase?.addTask("subtasks") when (selectedList.value) { is GtasksFilter -> { - val googleTask = GoogleTask(subtask.id, (selectedList.value as GtasksFilter).remoteId) - googleTask.parent = task.id + val googleTask = CaldavTask(subtask.id, (selectedList.value as GtasksFilter).remoteId, remoteId = null) + subtask.parent = task.id googleTask.isMoved = true - googleTaskDao.insertAndShift(googleTask, false) + googleTaskDao.insertAndShift(subtask, googleTask, false) } is CaldavFilter -> { val caldavTask = CaldavTask(subtask.id, (selectedList.value as CaldavFilter).uuid) diff --git a/app/src/main/java/org/tasks/widget/ChipProvider.kt b/app/src/main/java/org/tasks/widget/ChipProvider.kt index a4aa41462..3a20a5807 100644 --- a/app/src/main/java/org/tasks/widget/ChipProvider.kt +++ b/app/src/main/java/org/tasks/widget/ChipProvider.kt @@ -5,7 +5,6 @@ import android.widget.RemoteViews import com.todoroo.andlib.utility.DateUtilities import com.todoroo.astrid.api.CaldavFilter import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.TagFilter import com.todoroo.astrid.data.Task import dagger.hilt.android.qualifiers.ApplicationContext @@ -74,13 +73,9 @@ class ChipProvider @Inject constructor( } fun getListChip(filter: Filter?, task: TaskContainer): RemoteViews? { - task.googleTaskList - ?.takeIf { filter !is GtasksFilter } - ?.let { newChip(chipListCache.getGoogleTaskList(it), R.drawable.ic_list_24px) } - ?.let { return it } task.caldav ?.takeIf { filter !is CaldavFilter } - ?.let { newChip(chipListCache.getCaldavList(it), R.drawable.ic_list_24px) } + ?.let { newChip(CaldavFilter(chipListCache.getCaldavList(it)), R.drawable.ic_list_24px) } ?.let { return it } return null } diff --git a/app/src/test/java/org/tasks/caldav/iCalendarMergeTest.kt b/app/src/test/java/org/tasks/caldav/iCalendarMergeTest.kt index 7d5d9a4b2..0bb582559 100644 --- a/app/src/test/java/org/tasks/caldav/iCalendarMergeTest.kt +++ b/app/src/test/java/org/tasks/caldav/iCalendarMergeTest.kt @@ -12,19 +12,18 @@ import net.fortuna.ical4j.model.property.Status import org.junit.Assert.* import org.junit.Test import org.tasks.date.DateTimeUtils.newDateTime -import org.tasks.makers.CaldavTaskMaker.REMOTE_ORDER import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.TaskMaker import org.tasks.makers.TaskMaker.COMPLETION_TIME import org.tasks.makers.TaskMaker.CREATION_TIME import org.tasks.makers.TaskMaker.newTask +import org.tasks.makers.iCalMaker import org.tasks.makers.iCalMaker.COLLAPSED import org.tasks.makers.iCalMaker.COMPLETED_AT import org.tasks.makers.iCalMaker.CREATED_AT import org.tasks.makers.iCalMaker.DESCRIPTION import org.tasks.makers.iCalMaker.DUE_DATE -import org.tasks.makers.iCalMaker.ORDER import org.tasks.makers.iCalMaker.PARENT import org.tasks.makers.iCalMaker.PRIORITY import org.tasks.makers.iCalMaker.RRULE @@ -567,9 +566,9 @@ class iCalendarMergeTest { @Test fun remoteSetsOrder() = - newCaldavTask() + newTask() .applyRemote( - remote = newIcal(with(ORDER, 1234)), + remote = newIcal(with(iCalMaker.ORDER, 1234)), local = null ) .let { @@ -578,10 +577,10 @@ class iCalendarMergeTest { @Test fun remoteRemovesOrder() = - newCaldavTask(with(REMOTE_ORDER, 1234)) + newTask(with(TaskMaker.ORDER, 1234)) .applyRemote( remote = newIcal(), - local = newIcal(with(ORDER, 1234)) + local = newIcal(with(iCalMaker.ORDER, 1234)) ) .let { assertNull(it.order) @@ -589,10 +588,10 @@ class iCalendarMergeTest { @Test fun localRemovesOrder() = - newCaldavTask() + newTask() .applyRemote( - remote = newIcal(with(ORDER, 1234)), - local = newIcal(with(ORDER, 1234)) + remote = newIcal(with(iCalMaker.ORDER, 1234)), + local = newIcal(with(iCalMaker.ORDER, 1234)) ) .let { assertNull(it.order) @@ -600,10 +599,10 @@ class iCalendarMergeTest { @Test fun localBeatsRemoteOrder() = - newCaldavTask(with(REMOTE_ORDER, 789)) + newTask(with(TaskMaker.ORDER, 789L)) .applyRemote( - remote = newIcal(with(ORDER, 456)), - local = newIcal(with(ORDER, 123)) + remote = newIcal(with(iCalMaker.ORDER, 456L)), + local = newIcal(with(iCalMaker.ORDER, 123)) ) .let { assertEquals(789L, it.order) diff --git a/app/src/test/java/org/tasks/makers/CaldavAccountMaker.kt b/app/src/test/java/org/tasks/makers/CaldavAccountMaker.kt new file mode 100644 index 000000000..b8181865a --- /dev/null +++ b/app/src/test/java/org/tasks/makers/CaldavAccountMaker.kt @@ -0,0 +1,29 @@ +package org.tasks.makers + +import com.natpryce.makeiteasy.Instantiator +import com.natpryce.makeiteasy.Property +import com.natpryce.makeiteasy.Property.newProperty +import com.natpryce.makeiteasy.PropertyValue +import org.tasks.data.CaldavAccount +import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV +import org.tasks.makers.Maker.make + +object CaldavAccountMaker { + val ID: Property = newProperty() + val NAME: Property = newProperty() + val UUID: Property = newProperty() + val ACCOUNT_TYPE: Property = newProperty() + + private val instantiator = Instantiator { lookup -> + CaldavAccount().apply { + id = lookup.valueOf(ID, 0L) + name = lookup.valueOf(NAME, null as String?) + uuid = lookup.valueOf(UUID, "account") + accountType = lookup.valueOf(ACCOUNT_TYPE, TYPE_CALDAV) + } + } + + fun newCaldavAccount(vararg properties: PropertyValue): CaldavAccount { + return make(instantiator, *properties) + } +} \ No newline at end of file diff --git a/app/src/test/java/org/tasks/makers/CaldavCalendarMaker.kt b/app/src/test/java/org/tasks/makers/CaldavCalendarMaker.kt index 65a9a272b..851e9f3b9 100644 --- a/app/src/test/java/org/tasks/makers/CaldavCalendarMaker.kt +++ b/app/src/test/java/org/tasks/makers/CaldavCalendarMaker.kt @@ -8,13 +8,17 @@ import org.tasks.data.CaldavCalendar import org.tasks.makers.Maker.make object CaldavCalendarMaker { + val ID: Property = newProperty() val ACCOUNT: Property = newProperty() + val NAME: Property = newProperty() val UUID: Property = newProperty() private val instantiator = Instantiator { lookup -> CaldavCalendar( + id = lookup.valueOf(ID, 0L), + name = lookup.valueOf(NAME, null as String?), account = lookup.valueOf(ACCOUNT, "account"), - uuid = lookup.valueOf(UUID, "uuid"), + uuid = lookup.valueOf(UUID, "calendar"), ) } diff --git a/app/src/test/java/org/tasks/makers/CaldavTaskMaker.kt b/app/src/test/java/org/tasks/makers/CaldavTaskMaker.kt index 35fa8d63b..2bb4f4a51 100644 --- a/app/src/test/java/org/tasks/makers/CaldavTaskMaker.kt +++ b/app/src/test/java/org/tasks/makers/CaldavTaskMaker.kt @@ -12,15 +12,13 @@ object CaldavTaskMaker { val TASK: Property = newProperty() val REMOTE_ID: Property = newProperty() val REMOTE_PARENT: Property = newProperty() - val REMOTE_ORDER: Property = newProperty() val ETAG: Property = newProperty() val OBJECT: Property = newProperty() - private val instantiator = Instantiator { + private val instantiator = Instantiator { val task = CaldavTask(it.valueOf(TASK, 1L), it.valueOf(CALENDAR, "calendar")) task.remoteId = it.valueOf(REMOTE_ID, task.remoteId) task.remoteParent = it.valueOf(REMOTE_PARENT, null as String?) - task.order = it.valueOf(REMOTE_ORDER, null as Long?) task.etag = it.valueOf(ETAG, null as String?) task.`object` = it.valueOf(OBJECT, task.remoteId?.let { id -> "$id.ics" }) task diff --git a/app/src/test/java/org/tasks/makers/GoogleTaskListMaker.kt b/app/src/test/java/org/tasks/makers/GoogleTaskListMaker.kt deleted file mode 100644 index 997f1b05b..000000000 --- a/app/src/test/java/org/tasks/makers/GoogleTaskListMaker.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.tasks.makers - -import com.natpryce.makeiteasy.Instantiator -import com.natpryce.makeiteasy.Property -import com.natpryce.makeiteasy.PropertyLookup -import com.natpryce.makeiteasy.PropertyValue -import org.tasks.data.GoogleTaskList - -object GoogleTaskListMaker { - val REMOTE_ID: Property = Property.newProperty() - val ACCOUNT: Property = Property.newProperty() - - private val instantiator = Instantiator { lookup: PropertyLookup -> - val list = GoogleTaskList() - list.remoteId = lookup.valueOf(REMOTE_ID, "1234") - list.account = lookup.valueOf(ACCOUNT, null as String?) - list.setColor(0) - list - } - - fun newGoogleTaskList(vararg properties: PropertyValue): GoogleTaskList { - return Maker.make(instantiator, *properties) - } -} \ No newline at end of file diff --git a/app/src/test/java/org/tasks/makers/GoogleTaskMaker.kt b/app/src/test/java/org/tasks/makers/GoogleTaskMaker.kt deleted file mode 100644 index d29a02ff7..000000000 --- a/app/src/test/java/org/tasks/makers/GoogleTaskMaker.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.tasks.makers - -import com.natpryce.makeiteasy.Instantiator -import com.natpryce.makeiteasy.Property -import com.natpryce.makeiteasy.Property.newProperty -import com.natpryce.makeiteasy.PropertyValue -import com.todoroo.astrid.helper.UUIDHelper -import org.tasks.data.GoogleTask -import org.tasks.makers.Maker.make - -object GoogleTaskMaker { - val LIST: Property = newProperty() - val ORDER: Property = newProperty() - val REMOTE_ID: Property = newProperty() - val TASK: Property = newProperty() - val PARENT: Property = newProperty() - val REMOTE_PARENT: Property = newProperty() - - private val instantiator = Instantiator { - val task = GoogleTask() - task.listId = it.valueOf(LIST, "1") - task.order = it.valueOf(ORDER, 0) - task.remoteId = it.valueOf(REMOTE_ID, UUIDHelper.newUUID()) - task.task = it.valueOf(TASK, 1) - task.parent = it.valueOf(PARENT, 0L) - task.remoteParent = it.valueOf(REMOTE_PARENT, null as String?) - task - } - - fun newGoogleTask(vararg properties: PropertyValue): GoogleTask { - return make(instantiator, *properties) - } -} \ No newline at end of file diff --git a/app/src/test/java/org/tasks/makers/GtaskListMaker.kt b/app/src/test/java/org/tasks/makers/GtaskListMaker.kt deleted file mode 100644 index 2ea8b59ab..000000000 --- a/app/src/test/java/org/tasks/makers/GtaskListMaker.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.tasks.makers - -import com.natpryce.makeiteasy.Instantiator -import com.natpryce.makeiteasy.Property -import com.natpryce.makeiteasy.Property.newProperty -import com.natpryce.makeiteasy.PropertyLookup -import com.natpryce.makeiteasy.PropertyValue -import com.todoroo.astrid.api.FilterListItem.NO_ORDER -import org.tasks.data.GoogleTaskList -import org.tasks.makers.Maker.make - -object GtaskListMaker { - val ID: Property = newProperty() - val ACCOUNT: Property = newProperty() - val REMOTE_ID: Property = newProperty() - val LAST_SYNC: Property = newProperty() - val NAME: Property = newProperty() - private val ORDER: Property = newProperty() - private val COLOR: Property = newProperty() - - private val instantiator = Instantiator { lookup: PropertyLookup -> - val list = GoogleTaskList() - list.id = lookup.valueOf(ID, 0L) - list.account = lookup.valueOf(ACCOUNT, "account") - list.remoteId = lookup.valueOf(REMOTE_ID, "1") - list.title = lookup.valueOf(NAME, "Default") - list.order = lookup.valueOf(ORDER, NO_ORDER) - list.lastSync = lookup.valueOf(LAST_SYNC, 0L) - list.setColor(lookup.valueOf(COLOR, 0)) - list - } - - fun newGtaskList(vararg properties: PropertyValue): GoogleTaskList { - return make(instantiator, *properties) - } -} \ No newline at end of file diff --git a/app/src/test/java/org/tasks/makers/TaskMaker.kt b/app/src/test/java/org/tasks/makers/TaskMaker.kt index 3172e409d..a64ff52c2 100644 --- a/app/src/test/java/org/tasks/makers/TaskMaker.kt +++ b/app/src/test/java/org/tasks/makers/TaskMaker.kt @@ -34,6 +34,7 @@ object TaskMaker { val UUID: Property = newProperty() val COLLAPSED: Property = newProperty() val DESCRIPTION: Property = newProperty() + val ORDER: Property = newProperty() private val instantiator = Instantiator { lookup: PropertyLookup -> val task = Task() @@ -95,6 +96,7 @@ object TaskMaker { task.creationDate = creationTime.millis task.modificationDate = lookup.valueOf(MODIFICATION_TIME, creationTime).millis task.parent = lookup.valueOf(PARENT, 0L) + task.order = lookup.valueOf(ORDER, null as Long?) task }