mirror of https://github.com/tasks/tasks
Complete rewrite of Google Task manual ordering
parent
bef060591a
commit
087aae0a7b
@ -0,0 +1,996 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 63,
|
||||||
|
"identityHash": "6a0e3b6d4ed545092c478951e77574ba",
|
||||||
|
"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)",
|
||||||
|
"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"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX `index_notification_task` ON `${TABLE_NAME}` (`task`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "tagdata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT)",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": "task_attachments",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `task_id` TEXT, `name` TEXT, `path` TEXT, `content_type` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteId",
|
||||||
|
"columnName": "remoteId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "taskId",
|
||||||
|
"columnName": "task_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "uri",
|
||||||
|
"columnName": "path",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentType",
|
||||||
|
"columnName": "content_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "task_list_metadata",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteId",
|
||||||
|
"columnName": "remoteId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"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, `title` TEXT, `importance` INTEGER, `dueDate` INTEGER, `hideUntil` INTEGER, `created` INTEGER, `modified` INTEGER, `completed` INTEGER, `deleted` INTEGER, `notes` TEXT, `estimatedSeconds` INTEGER, `elapsedSeconds` INTEGER, `timerStart` INTEGER, `notificationFlags` INTEGER, `notifications` INTEGER, `lastNotified` INTEGER, `snoozeTime` INTEGER, `recurrence` TEXT, `repeatUntil` INTEGER, `calendarUri` TEXT, `remoteId` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "priority",
|
||||||
|
"columnName": "importance",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dueDate",
|
||||||
|
"columnName": "dueDate",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hideUntil",
|
||||||
|
"columnName": "hideUntil",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "created",
|
||||||
|
"columnName": "created",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "modified",
|
||||||
|
"columnName": "modified",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "completed",
|
||||||
|
"columnName": "completed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notes",
|
||||||
|
"columnName": "notes",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "estimatedSeconds",
|
||||||
|
"columnName": "estimatedSeconds",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "elapsedSeconds",
|
||||||
|
"columnName": "elapsedSeconds",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timerStart",
|
||||||
|
"columnName": "timerStart",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationFlags",
|
||||||
|
"columnName": "notificationFlags",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notifications",
|
||||||
|
"columnName": "notifications",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastNotified",
|
||||||
|
"columnName": "lastNotified",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "snoozeTime",
|
||||||
|
"columnName": "snoozeTime",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "recurrence",
|
||||||
|
"columnName": "recurrence",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "repeatUntil",
|
||||||
|
"columnName": "repeatUntil",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "calendarUri",
|
||||||
|
"columnName": "calendarUri",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteId",
|
||||||
|
"columnName": "remoteId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "t_rid",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"remoteId"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX `t_rid` ON `${TABLE_NAME}` (`remoteId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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)",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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)",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"place_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"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, `radius` INTEGER NOT NULL, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL)",
|
||||||
|
"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": "radius",
|
||||||
|
"columnName": "radius",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "arrival",
|
||||||
|
"columnName": "arrival",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "departure",
|
||||||
|
"columnName": "departure",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"geofence_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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)",
|
||||||
|
"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": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "google_tasks",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`gt_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gt_task` INTEGER NOT NULL, `gt_remote_id` TEXT, `gt_list_id` TEXT, `gt_parent` INTEGER NOT NULL, `gt_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_order` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, `gt_last_sync` INTEGER NOT NULL, `gt_deleted` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "gt_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "task",
|
||||||
|
"columnName": "gt_task",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteId",
|
||||||
|
"columnName": "gt_remote_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "listId",
|
||||||
|
"columnName": "gt_list_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "parent",
|
||||||
|
"columnName": "gt_parent",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteParent",
|
||||||
|
"columnName": "gt_remote_parent",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "moved",
|
||||||
|
"columnName": "gt_moved",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "order",
|
||||||
|
"columnName": "gt_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteOrder",
|
||||||
|
"columnName": "gt_remote_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastSync",
|
||||||
|
"columnName": "gt_last_sync",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deleted",
|
||||||
|
"columnName": "gt_deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"gt_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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)",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "google_task_lists",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `remote_id` TEXT, `title` TEXT, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `color` INTEGER)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "account",
|
||||||
|
"columnName": "account",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteId",
|
||||||
|
"columnName": "remote_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteOrder",
|
||||||
|
"columnName": "remote_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastSync",
|
||||||
|
"columnName": "last_sync",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "color",
|
||||||
|
"columnName": "color",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "caldav_calendar",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "account",
|
||||||
|
"columnName": "account",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "uuid",
|
||||||
|
"columnName": "uuid",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "color",
|
||||||
|
"columnName": "color",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ctag",
|
||||||
|
"columnName": "ctag",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "caldav_tasks",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `calendar` TEXT, `object` TEXT, `remote_id` TEXT, `etag` TEXT, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `vtodo` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "task",
|
||||||
|
"columnName": "task",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "calendar",
|
||||||
|
"columnName": "calendar",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "object",
|
||||||
|
"columnName": "object",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "remoteId",
|
||||||
|
"columnName": "remote_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "etag",
|
||||||
|
"columnName": "etag",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastSync",
|
||||||
|
"columnName": "last_sync",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "vtodo",
|
||||||
|
"columnName": "vtodo",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "caldav_account",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `url` TEXT, `username` TEXT, `password` TEXT, `error` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "uuid",
|
||||||
|
"columnName": "uuid",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "password",
|
||||||
|
"columnName": "password",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "error",
|
||||||
|
"columnName": "error",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "google_task_accounts",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `error` TEXT, `etag` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "account",
|
||||||
|
"columnName": "account",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "error",
|
||||||
|
"columnName": "error",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "etag",
|
||||||
|
"columnName": "etag",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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, \"6a0e3b6d4ed545092c478951e77574ba\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,208 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
|
||||||
import static junit.framework.Assert.assertTrue;
|
|
||||||
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
import com.google.api.services.tasks.model.TaskList;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.tasks.data.GoogleTask;
|
|
||||||
import org.tasks.data.GoogleTaskAccount;
|
|
||||||
import org.tasks.data.GoogleTaskDao;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.data.GoogleTaskListDao;
|
|
||||||
import org.tasks.injection.InjectingTestCase;
|
|
||||||
import org.tasks.injection.TestComponent;
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class GtasksIndentActionTest extends InjectingTestCase {
|
|
||||||
|
|
||||||
@Inject GtasksListService gtasksListService;
|
|
||||||
@Inject GoogleTaskListDao googleTaskListDao;
|
|
||||||
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
|
|
||||||
@Inject TaskDao taskDao;
|
|
||||||
@Inject GoogleTaskDao googleTaskDao;
|
|
||||||
|
|
||||||
private Task task;
|
|
||||||
private GoogleTaskList storeList;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithoutMetadata() {
|
|
||||||
givenTask(taskWithoutMetadata());
|
|
||||||
|
|
||||||
whenIncreaseIndent();
|
|
||||||
|
|
||||||
// should not crash
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void disabled_testIndentWithMetadataButNoOtherTasks() {
|
|
||||||
givenTask(taskWithMetadata(0, 0));
|
|
||||||
|
|
||||||
whenIncreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithMetadata() {
|
|
||||||
taskWithMetadata(0, 0);
|
|
||||||
givenTask(taskWithMetadata(1, 0));
|
|
||||||
|
|
||||||
whenIncreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeindentWithMetadata() {
|
|
||||||
givenTask(taskWithMetadata(0, 1));
|
|
||||||
|
|
||||||
whenDecreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeindentWithoutMetadata() {
|
|
||||||
givenTask(taskWithoutMetadata());
|
|
||||||
|
|
||||||
whenDecreaseIndent();
|
|
||||||
|
|
||||||
// should not crash
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeindentWhenAlreadyZero() {
|
|
||||||
givenTask(taskWithMetadata(0, 0));
|
|
||||||
|
|
||||||
whenDecreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithChildren() {
|
|
||||||
taskWithMetadata(0, 0);
|
|
||||||
givenTask(taskWithMetadata(1, 0));
|
|
||||||
Task child = taskWithMetadata(2, 1);
|
|
||||||
|
|
||||||
whenIncreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(1);
|
|
||||||
thenExpectIndentationLevel(child, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeindentWithChildren() {
|
|
||||||
taskWithMetadata(0, 0);
|
|
||||||
givenTask(taskWithMetadata(1, 1));
|
|
||||||
Task child = taskWithMetadata(2, 2);
|
|
||||||
|
|
||||||
whenDecreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(0);
|
|
||||||
thenExpectIndentationLevel(child, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSiblings() {
|
|
||||||
taskWithMetadata(0, 0);
|
|
||||||
givenTask(taskWithMetadata(1, 0));
|
|
||||||
Task sibling = taskWithMetadata(2, 0);
|
|
||||||
|
|
||||||
whenIncreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(1);
|
|
||||||
thenExpectIndentationLevel(sibling, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithChildrensChildren() {
|
|
||||||
taskWithMetadata(0, 0);
|
|
||||||
givenTask(taskWithMetadata(1, 0));
|
|
||||||
Task child = taskWithMetadata(2, 1);
|
|
||||||
Task grandchild = taskWithMetadata(3, 2);
|
|
||||||
|
|
||||||
whenIncreaseIndent();
|
|
||||||
|
|
||||||
thenExpectIndentationLevel(1);
|
|
||||||
thenExpectIndentationLevel(child, 2);
|
|
||||||
thenExpectIndentationLevel(grandchild, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- helpers
|
|
||||||
|
|
||||||
private void whenIncreaseIndent() {
|
|
||||||
gtasksTaskListUpdater.indent(storeList, task.getId(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void whenDecreaseIndent() {
|
|
||||||
gtasksTaskListUpdater.indent(storeList, task.getId(), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
List<TaskList> items = new ArrayList<>();
|
|
||||||
TaskList list = new TaskList();
|
|
||||||
list.setId("list");
|
|
||||||
list.setTitle("Test Tasks");
|
|
||||||
items.add(list);
|
|
||||||
GoogleTaskAccount account = new GoogleTaskAccount("account");
|
|
||||||
googleTaskListDao.insert(account);
|
|
||||||
gtasksListService.updateLists(account, items);
|
|
||||||
|
|
||||||
storeList = googleTaskListDao.getLists("account").get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(TestComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task taskWithMetadata(long order, int indentation) {
|
|
||||||
Task newTask = new Task();
|
|
||||||
taskDao.createNew(newTask);
|
|
||||||
GoogleTask metadata = new GoogleTask(newTask.getId(), "list");
|
|
||||||
metadata.setIndent(indentation);
|
|
||||||
metadata.setOrder(order);
|
|
||||||
googleTaskDao.insert(metadata);
|
|
||||||
return newTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void thenExpectIndentationLevel(int expected) {
|
|
||||||
thenExpectIndentationLevel(task, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void thenExpectIndentationLevel(Task targetTask, int expected) {
|
|
||||||
GoogleTask metadata = googleTaskDao.getByTaskId(targetTask.getId());
|
|
||||||
assertNotNull("task has metadata", metadata);
|
|
||||||
int indentation = metadata.getIndent();
|
|
||||||
assertTrue("indentation: " + indentation, indentation == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void givenTask(Task taskToTest) {
|
|
||||||
task = taskToTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task taskWithoutMetadata() {
|
|
||||||
Task task = new Task();
|
|
||||||
taskDao.createNew(task);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
|
||||||
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
import com.google.api.services.tasks.model.TaskList;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.tasks.data.GoogleTask;
|
|
||||||
import org.tasks.data.GoogleTaskAccount;
|
|
||||||
import org.tasks.data.GoogleTaskDao;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.data.GoogleTaskListDao;
|
|
||||||
import org.tasks.injection.InjectingTestCase;
|
|
||||||
import org.tasks.injection.TestComponent;
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class GtasksTaskListUpdaterTest extends InjectingTestCase {
|
|
||||||
|
|
||||||
private static final int VALUE_UNSET = -1;
|
|
||||||
|
|
||||||
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
|
|
||||||
@Inject GtasksListService gtasksListService;
|
|
||||||
@Inject TaskDao taskDao;
|
|
||||||
@Inject GoogleTaskDao googleTaskDao;
|
|
||||||
@Inject GoogleTaskListDao googleTaskListDao;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBasicParentComputation() {
|
|
||||||
Task[] tasks = givenTasksABCDE();
|
|
||||||
|
|
||||||
whenCalculatingParentsAndSiblings();
|
|
||||||
|
|
||||||
thenExpectParent(tasks[0], null);
|
|
||||||
thenExpectParent(tasks[1], tasks[0]);
|
|
||||||
thenExpectParent(tasks[2], tasks[0]);
|
|
||||||
thenExpectParent(tasks[3], tasks[2]);
|
|
||||||
thenExpectParent(tasks[4], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBasicSiblingComputation() {
|
|
||||||
Task[] tasks = givenTasksABCDE();
|
|
||||||
|
|
||||||
whenCalculatingParentsAndSiblings();
|
|
||||||
|
|
||||||
thenExpectSibling(tasks[0], null);
|
|
||||||
thenExpectSibling(tasks[1], null);
|
|
||||||
thenExpectSibling(tasks[2], tasks[1]);
|
|
||||||
thenExpectSibling(tasks[3], null);
|
|
||||||
thenExpectSibling(tasks[4], tasks[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
|
||||||
public void testMetadataParentComputation() {
|
|
||||||
Task[] tasks = givenTasksABCDE();
|
|
||||||
|
|
||||||
thenExpectMetadataParent(tasks[0], null);
|
|
||||||
thenExpectMetadataParent(tasks[1], tasks[0]);
|
|
||||||
thenExpectMetadataParent(tasks[2], tasks[0]);
|
|
||||||
thenExpectMetadataParent(tasks[3], tasks[2]);
|
|
||||||
thenExpectMetadataParent(tasks[4], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMetadataOrderComputation() {
|
|
||||||
Task[] tasks = givenTasksABCDE();
|
|
||||||
|
|
||||||
thenExpectMetadataIndentAndOrder(tasks[0], 0, 0);
|
|
||||||
thenExpectMetadataIndentAndOrder(tasks[1], 1, 1);
|
|
||||||
thenExpectMetadataIndentAndOrder(tasks[2], 2, 1);
|
|
||||||
thenExpectMetadataIndentAndOrder(tasks[3], 3, 2);
|
|
||||||
thenExpectMetadataIndentAndOrder(tasks[4], 4, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
|
||||||
public void testNewTaskOrder() {
|
|
||||||
givenTasksABCDE();
|
|
||||||
|
|
||||||
Task newTask = createTask("F", VALUE_UNSET, 0);
|
|
||||||
|
|
||||||
thenExpectMetadataIndentAndOrder(newTask, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- helpers
|
|
||||||
|
|
||||||
private void thenExpectMetadataIndentAndOrder(Task task, long order, int indent) {
|
|
||||||
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
|
|
||||||
assertNotNull("metadata was found", metadata);
|
|
||||||
assertEquals("order", order, metadata.getOrder());
|
|
||||||
assertEquals("indentation", indent, metadata.getIndent());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void thenExpectMetadataParent(Task task, Task expectedParent) {
|
|
||||||
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
|
|
||||||
long parent = metadata.getParent();
|
|
||||||
if (expectedParent == null) {
|
|
||||||
assertEquals("Task " + task.getTitle() + " parent none", 0, parent);
|
|
||||||
} else {
|
|
||||||
assertEquals(
|
|
||||||
"Task " + task.getTitle() + " parent " + expectedParent.getTitle(),
|
|
||||||
expectedParent.getId(),
|
|
||||||
parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void thenExpectSibling(Task task, Task expectedSibling) {
|
|
||||||
long sibling = gtasksTaskListUpdater.siblings.get(task.getId());
|
|
||||||
if (expectedSibling == null) {
|
|
||||||
assertEquals("Task " + task.getTitle() + " sibling null", 0L, sibling);
|
|
||||||
} else {
|
|
||||||
assertEquals(
|
|
||||||
"Task " + task.getTitle() + " sibling " + expectedSibling.getTitle(),
|
|
||||||
expectedSibling.getId(),
|
|
||||||
sibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void thenExpectParent(Task task, Task expectedParent) {
|
|
||||||
long parent = gtasksTaskListUpdater.parents.get(task.getId());
|
|
||||||
if (expectedParent == null) {
|
|
||||||
assertEquals("Task " + task.getTitle() + " parent null", 0L, parent);
|
|
||||||
} else {
|
|
||||||
assertEquals(
|
|
||||||
"Task " + task.getTitle() + " parent " + expectedParent.getTitle(),
|
|
||||||
expectedParent.getId(),
|
|
||||||
parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
List<TaskList> items = new ArrayList<>();
|
|
||||||
TaskList list = new TaskList();
|
|
||||||
list.setId("1");
|
|
||||||
list.setTitle("Tim's Tasks");
|
|
||||||
items.add(list);
|
|
||||||
GoogleTaskAccount account = new GoogleTaskAccount("account");
|
|
||||||
googleTaskListDao.insert(account);
|
|
||||||
gtasksListService.updateLists(account, items);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(TestComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void whenCalculatingParentsAndSiblings() {
|
|
||||||
createParentSiblingMaps();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createParentSiblingMaps() {
|
|
||||||
for (GoogleTaskList list : googleTaskListDao.getLists("account")) {
|
|
||||||
gtasksTaskListUpdater.updateParentSiblingMapsFor(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A
|
|
||||||
// B
|
|
||||||
// C
|
|
||||||
// D
|
|
||||||
// E
|
|
||||||
private Task[] givenTasksABCDE() {
|
|
||||||
return new Task[] {
|
|
||||||
createTask("A", 0, 0),
|
|
||||||
createTask("B", 1, 1),
|
|
||||||
createTask("C", 2, 1),
|
|
||||||
createTask("D", 3, 2),
|
|
||||||
createTask("E", 4, 0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task createTask(String title, long order, int indent) {
|
|
||||||
Task task = new Task();
|
|
||||||
task.setTitle(title);
|
|
||||||
taskDao.createNew(task);
|
|
||||||
GoogleTask metadata = new GoogleTask(task.getId(), "1");
|
|
||||||
if (order != VALUE_UNSET) {
|
|
||||||
metadata.setOrder(order);
|
|
||||||
}
|
|
||||||
if (indent != VALUE_UNSET) {
|
|
||||||
metadata.setIndent(indent);
|
|
||||||
}
|
|
||||||
googleTaskDao.insert(metadata);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,313 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.astrid.gtasks;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
|
||||||
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
import com.google.api.services.tasks.model.TaskList;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.tasks.data.GoogleTask;
|
|
||||||
import org.tasks.data.GoogleTaskAccount;
|
|
||||||
import org.tasks.data.GoogleTaskDao;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.data.GoogleTaskListDao;
|
|
||||||
import org.tasks.injection.InjectingTestCase;
|
|
||||||
import org.tasks.injection.TestComponent;
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class GtasksTaskMovingTest extends InjectingTestCase {
|
|
||||||
|
|
||||||
private static final int VALUE_UNSET = -1;
|
|
||||||
|
|
||||||
@Inject GtasksListService gtasksListService;
|
|
||||||
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
|
|
||||||
@Inject TaskDao taskDao;
|
|
||||||
@Inject GoogleTaskDao googleTaskDao;
|
|
||||||
@Inject GoogleTaskListDao googleTaskListDao;
|
|
||||||
|
|
||||||
private Task A, B, C, D, E, F;
|
|
||||||
private GoogleTaskList list;
|
|
||||||
|
|
||||||
/* Starting State:
|
|
||||||
*
|
|
||||||
* A
|
|
||||||
* B
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* E
|
|
||||||
* F
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveDownFromListBottom() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(F, null);
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 4, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(F, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveDownToListBottom() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(E, null);
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 5, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(F, 4, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveUpSimple() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(F, E);
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 5, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(F, 4, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveUpWithSubtasks() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(C, B);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* B
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 3, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 1, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 2, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveDownThroughSubtasks() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(B, E);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* B
|
|
||||||
* E
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 3, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 1, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 2, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveUpAboveParent() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(B, A);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* B
|
|
||||||
* A
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* E
|
|
||||||
* F
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 1, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveDownWithChildren() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(C, F);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A
|
|
||||||
* B
|
|
||||||
* E
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* F
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 1, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 3, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 4, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 2, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveDownIndentingTwice() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(D, F);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A
|
|
||||||
* B
|
|
||||||
* C
|
|
||||||
* E
|
|
||||||
* D
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 1, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 2, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 4, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 3, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveUpMultiple() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(C, A);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* A
|
|
||||||
* B
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 2, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 3, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveUpIntoSublist() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(F, D);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A
|
|
||||||
* B
|
|
||||||
* C
|
|
||||||
* F
|
|
||||||
* D
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 1, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 2, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 4, 2);
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 5, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(F, 3, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveDownMultiple() {
|
|
||||||
givenTasksABCDEF();
|
|
||||||
|
|
||||||
whenTriggerMove(B, F);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A
|
|
||||||
* C
|
|
||||||
* D
|
|
||||||
* E
|
|
||||||
* B
|
|
||||||
*/
|
|
||||||
|
|
||||||
thenExpectMetadataOrderAndIndent(A, 0, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(B, 4, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(C, 1, 1);
|
|
||||||
thenExpectMetadataOrderAndIndent(D, 2, 2);
|
|
||||||
thenExpectMetadataOrderAndIndent(E, 3, 0);
|
|
||||||
thenExpectMetadataOrderAndIndent(F, 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- helpers
|
|
||||||
|
|
||||||
/** moveTo = null => move to end */
|
|
||||||
private void whenTriggerMove(Task target, Task moveTo) {
|
|
||||||
gtasksTaskListUpdater.moveTo(list, target.getId(), moveTo == null ? -1 : moveTo.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void thenExpectMetadataOrderAndIndent(Task task, long order, int indent) {
|
|
||||||
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
|
|
||||||
assertNotNull("metadata was found", metadata);
|
|
||||||
assertEquals("order", order, metadata.getOrder());
|
|
||||||
assertEquals("indentation", indent, metadata.getIndent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
List<TaskList> items = new ArrayList<>();
|
|
||||||
TaskList taskList = new TaskList();
|
|
||||||
taskList.setId("1");
|
|
||||||
taskList.setTitle("Tim's Tasks");
|
|
||||||
items.add(taskList);
|
|
||||||
GoogleTaskAccount account = new GoogleTaskAccount("account");
|
|
||||||
googleTaskListDao.insert(account);
|
|
||||||
gtasksListService.updateLists(account, items);
|
|
||||||
|
|
||||||
list = googleTaskListDao.getLists("account").get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(TestComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A
|
|
||||||
// B
|
|
||||||
// C
|
|
||||||
// D
|
|
||||||
// E
|
|
||||||
// F
|
|
||||||
private void givenTasksABCDEF() {
|
|
||||||
A = createTask("A", 0, 0);
|
|
||||||
B = createTask("B", 1, 1);
|
|
||||||
C = createTask("C", 2, 1);
|
|
||||||
D = createTask("D", 3, 2);
|
|
||||||
E = createTask("E", 4, 0);
|
|
||||||
F = createTask("F", 5, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task createTask(String title, long order, int indent) {
|
|
||||||
Task task = new Task();
|
|
||||||
task.setTitle(title);
|
|
||||||
taskDao.createNew(task);
|
|
||||||
GoogleTask metadata = new GoogleTask(task.getId(), "1");
|
|
||||||
if (order != VALUE_UNSET) {
|
|
||||||
metadata.setOrder(order);
|
|
||||||
}
|
|
||||||
if (indent != VALUE_UNSET) {
|
|
||||||
metadata.setIndent(indent);
|
|
||||||
}
|
|
||||||
googleTaskDao.insert(metadata);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,187 @@
|
|||||||
|
package org.tasks.data;
|
||||||
|
|
||||||
|
import static com.natpryce.makeiteasy.MakeItEasy.with;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.tasks.makers.GoogleTaskMaker.REMOTE_ID;
|
||||||
|
import static org.tasks.makers.GoogleTaskMaker.TASK;
|
||||||
|
import static org.tasks.makers.GoogleTaskMaker.newGoogleTask;
|
||||||
|
import static org.tasks.makers.GtaskListMaker.newGtaskList;
|
||||||
|
import static org.tasks.makers.TaskMaker.newTask;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.todoroo.astrid.dao.TaskDao;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.tasks.injection.InjectingTestCase;
|
||||||
|
import org.tasks.injection.TestComponent;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class GoogleTaskDaoTests extends InjectingTestCase {
|
||||||
|
|
||||||
|
@Inject GoogleTaskListDao googleTaskListDao;
|
||||||
|
@Inject GoogleTaskDao googleTaskDao;
|
||||||
|
@Inject TaskDao taskDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
googleTaskListDao.insert(newGtaskList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertAtTopOfEmptyList() {
|
||||||
|
insertTop(newGoogleTask(with(REMOTE_ID, "1234")));
|
||||||
|
List<GoogleTask> tasks = googleTaskDao.getByLocalOrder("1");
|
||||||
|
assertEquals(1, tasks.size());
|
||||||
|
GoogleTask task = tasks.get(0);
|
||||||
|
assertEquals("1234", task.getRemoteId());
|
||||||
|
assertEquals(0, task.getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertAtBottomOfEmptyList() {
|
||||||
|
insertBottom(newGoogleTask(with(REMOTE_ID, "1234")));
|
||||||
|
List<GoogleTask> tasks = googleTaskDao.getByLocalOrder("1");
|
||||||
|
assertEquals(1, tasks.size());
|
||||||
|
GoogleTask task = tasks.get(0);
|
||||||
|
assertEquals("1234", task.getRemoteId());
|
||||||
|
assertEquals(0, task.getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPreviousIsNullForTopTask() {
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(), true);
|
||||||
|
assertNull(googleTaskDao.getPrevious("1", 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPrevious() {
|
||||||
|
insertTop(newGoogleTask());
|
||||||
|
insertTop(newGoogleTask(with(REMOTE_ID, "1234")));
|
||||||
|
|
||||||
|
assertEquals("1234", googleTaskDao.getPrevious("1", 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertAtTopOfList() {
|
||||||
|
insertTop(newGoogleTask(with(REMOTE_ID, "1234")));
|
||||||
|
insertTop(newGoogleTask(with(REMOTE_ID, "5678")));
|
||||||
|
|
||||||
|
List<GoogleTask> tasks = googleTaskDao.getByLocalOrder("1");
|
||||||
|
assertEquals(2, tasks.size());
|
||||||
|
GoogleTask top = tasks.get(0);
|
||||||
|
assertEquals("5678", top.getRemoteId());
|
||||||
|
assertEquals(0, top.getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insertAtTopOfListShiftsExisting() {
|
||||||
|
insertTop(newGoogleTask(with(REMOTE_ID, "1234")));
|
||||||
|
insertTop(newGoogleTask(with(REMOTE_ID, "5678")));
|
||||||
|
|
||||||
|
List<GoogleTask> tasks = googleTaskDao.getByLocalOrder("1");
|
||||||
|
assertEquals(2, tasks.size());
|
||||||
|
GoogleTask bottom = tasks.get(1);
|
||||||
|
assertEquals("1234", bottom.getRemoteId());
|
||||||
|
assertEquals(1, bottom.getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTaskFromRemoteId() {
|
||||||
|
googleTaskDao.insert(newGoogleTask(with(REMOTE_ID, "1234"), with(TASK, 4)));
|
||||||
|
assertEquals(4, googleTaskDao.getTask("1234"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getRemoteIdForTask() {
|
||||||
|
googleTaskDao.insert(newGoogleTask(with(REMOTE_ID, "1234"), with(TASK, 4)));
|
||||||
|
assertEquals("1234", googleTaskDao.getRemoteId(4L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moveDownInList() {
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "1")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "2")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false);
|
||||||
|
|
||||||
|
GoogleTask two = googleTaskDao.getByRemoteId("2");
|
||||||
|
|
||||||
|
googleTaskDao.move(two, 0, 0);
|
||||||
|
|
||||||
|
assertEquals(0, googleTaskDao.getByRemoteId("2").getOrder());
|
||||||
|
assertEquals(1, googleTaskDao.getByRemoteId("1").getOrder());
|
||||||
|
assertEquals(2, googleTaskDao.getByRemoteId("3").getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moveUpInList() {
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "1")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "2")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false);
|
||||||
|
|
||||||
|
GoogleTask one = googleTaskDao.getByRemoteId("1");
|
||||||
|
|
||||||
|
googleTaskDao.move(one, 0, 1);
|
||||||
|
|
||||||
|
assertEquals(0, googleTaskDao.getByRemoteId("2").getOrder());
|
||||||
|
assertEquals(1, googleTaskDao.getByRemoteId("1").getOrder());
|
||||||
|
assertEquals(2, googleTaskDao.getByRemoteId("3").getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moveToTop() {
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "1")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "2")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false);
|
||||||
|
|
||||||
|
GoogleTask three = googleTaskDao.getByRemoteId("3");
|
||||||
|
|
||||||
|
googleTaskDao.move(three, 0, 0);
|
||||||
|
|
||||||
|
assertEquals(0, googleTaskDao.getByRemoteId("3").getOrder());
|
||||||
|
assertEquals(1, googleTaskDao.getByRemoteId("1").getOrder());
|
||||||
|
assertEquals(2, googleTaskDao.getByRemoteId("2").getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moveToBottom() {
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "1")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "2")), false);
|
||||||
|
googleTaskDao.insertAndShift(newGoogleTask(with(REMOTE_ID, "3")), false);
|
||||||
|
|
||||||
|
GoogleTask one = googleTaskDao.getByRemoteId("1");
|
||||||
|
|
||||||
|
googleTaskDao.move(one, 0, 2);
|
||||||
|
|
||||||
|
assertEquals(0, googleTaskDao.getByRemoteId("2").getOrder());
|
||||||
|
assertEquals(1, googleTaskDao.getByRemoteId("3").getOrder());
|
||||||
|
assertEquals(2, googleTaskDao.getByRemoteId("1").getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertTop(GoogleTask googleTask) {
|
||||||
|
insert(googleTask, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertBottom(GoogleTask googleTask) {
|
||||||
|
insert(googleTask, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insert(GoogleTask googleTask, boolean top) {
|
||||||
|
Task task = newTask();
|
||||||
|
taskDao.createNew(task);
|
||||||
|
googleTask.setTask(task.getId());
|
||||||
|
googleTaskDao.insertAndShift(googleTask, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void inject(TestComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.tasks.makers;
|
||||||
|
|
||||||
|
import static com.natpryce.makeiteasy.Property.newProperty;
|
||||||
|
import static org.tasks.makers.Maker.make;
|
||||||
|
|
||||||
|
import com.natpryce.makeiteasy.Instantiator;
|
||||||
|
import com.natpryce.makeiteasy.Property;
|
||||||
|
import com.natpryce.makeiteasy.PropertyValue;
|
||||||
|
import com.todoroo.astrid.helper.UUIDHelper;
|
||||||
|
import org.tasks.data.GoogleTask;
|
||||||
|
|
||||||
|
public class GoogleTaskMaker {
|
||||||
|
|
||||||
|
public static final Property<GoogleTask, String> LIST = newProperty();
|
||||||
|
public static final Property<GoogleTask, Integer> ORDER = newProperty();
|
||||||
|
public static final Property<GoogleTask, String> REMOTE_ID = newProperty();
|
||||||
|
public static final Property<GoogleTask, Integer> TASK = newProperty();
|
||||||
|
|
||||||
|
private static final Instantiator<GoogleTask> instantiator = lookup -> {
|
||||||
|
GoogleTask task = new GoogleTask();
|
||||||
|
task.setListId(lookup.valueOf(LIST, "1"));
|
||||||
|
task.setOrder(lookup.valueOf(ORDER, 0));
|
||||||
|
task.setRemoteId(lookup.valueOf(REMOTE_ID, UUIDHelper.newUUID()));
|
||||||
|
task.setTask(lookup.valueOf(TASK, 1));
|
||||||
|
return task;
|
||||||
|
};
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static GoogleTask newGoogleTask(PropertyValue<? super GoogleTask, ?>... properties) {
|
||||||
|
return make(instantiator, properties);
|
||||||
|
}
|
||||||
|
}
|
@ -1,404 +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.api.Filter;
|
|
||||||
import com.todoroo.astrid.api.GtasksFilter;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.data.GoogleTask;
|
|
||||||
import org.tasks.data.GoogleTaskDao;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.injection.ApplicationScope;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
@ApplicationScope
|
|
||||||
public class GtasksTaskListUpdater {
|
|
||||||
|
|
||||||
/** map of task -> parent task */
|
|
||||||
final HashMap<Long, Long> parents = new HashMap<>();
|
|
||||||
|
|
||||||
/** map of task -> prior sibling */
|
|
||||||
final HashMap<Long, Long> siblings = new HashMap<>();
|
|
||||||
|
|
||||||
private final GtasksSyncService gtasksSyncService;
|
|
||||||
private final GoogleTaskDao googleTaskDao;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GtasksTaskListUpdater(GtasksSyncService gtasksSyncService, GoogleTaskDao googleTaskDao) {
|
|
||||||
this.gtasksSyncService = gtasksSyncService;
|
|
||||||
this.googleTaskDao = googleTaskDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(Filter filter) {
|
|
||||||
String query = GtasksFilter.toManualOrder(filter.getSqlQuery());
|
|
||||||
filter.setFilterQueryOverride(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- overrides
|
|
||||||
|
|
||||||
public GoogleTask getTaskMetadata(long taskId) {
|
|
||||||
return googleTaskDao.getByTaskId(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void iterateThroughList(GoogleTaskList list, OrderedListIterator iterator) {
|
|
||||||
String listId = list.getRemoteId();
|
|
||||||
gtasksSyncService.iterateThroughList(listId, iterator, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onMovedOrIndented(GoogleTaskList googleTaskList, GoogleTask googleTask) {
|
|
||||||
gtasksSyncService.triggerMoveForMetadata(googleTaskList, googleTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- used during synchronization
|
|
||||||
|
|
||||||
public void correctOrderAndIndentForList(String listId) {
|
|
||||||
orderAndIndentHelper(listId, new AtomicLong(0L), Task.NO_ID, 0, new HashSet<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void orderAndIndentHelper(
|
|
||||||
final String listId,
|
|
||||||
final AtomicLong order,
|
|
||||||
final long parent,
|
|
||||||
final int indentLevel,
|
|
||||||
final Set<Long> alreadyChecked) {
|
|
||||||
for (GoogleTask curr : googleTaskDao.byRemoteOrder(listId, parent)) {
|
|
||||||
if (!alreadyChecked.contains(curr.getTask())) {
|
|
||||||
curr.setIndent(indentLevel);
|
|
||||||
curr.setOrder(order.getAndIncrement());
|
|
||||||
googleTaskDao.update(curr);
|
|
||||||
alreadyChecked.add(curr.getTask());
|
|
||||||
|
|
||||||
orderAndIndentHelper(listId, order, curr.getTask(), indentLevel + 1, alreadyChecked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateParentSiblingMapsFor(GoogleTaskList list) {
|
|
||||||
final AtomicLong previousTask = new AtomicLong(Task.NO_ID);
|
|
||||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
|
||||||
|
|
||||||
iterateThroughList(
|
|
||||||
list,
|
|
||||||
(taskId, metadata) -> {
|
|
||||||
int indent = metadata.getIndent();
|
|
||||||
|
|
||||||
try {
|
|
||||||
long parent, sibling;
|
|
||||||
if (indent > previousIndent.get()) {
|
|
||||||
parent = previousTask.get();
|
|
||||||
sibling = Task.NO_ID;
|
|
||||||
} else if (indent == previousIndent.get()) {
|
|
||||||
sibling = previousTask.get();
|
|
||||||
parent = parents.get(sibling);
|
|
||||||
} else {
|
|
||||||
// move up once for each indent
|
|
||||||
sibling = previousTask.get();
|
|
||||||
for (int i = indent; i < previousIndent.get(); i++) {
|
|
||||||
sibling = parents.get(sibling);
|
|
||||||
}
|
|
||||||
if (parents.containsKey(sibling)) {
|
|
||||||
parent = parents.get(sibling);
|
|
||||||
} else {
|
|
||||||
parent = Task.NO_ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parents.put(taskId, parent);
|
|
||||||
siblings.put(taskId, sibling);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
previousTask.set(taskId);
|
|
||||||
previousIndent.set(indent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Indent a task and all its children */
|
|
||||||
public void indent(final GoogleTaskList list, final long targetTaskId, final int delta) {
|
|
||||||
if (list == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateParentSiblingMapsFor(list);
|
|
||||||
|
|
||||||
final AtomicInteger targetTaskIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicLong previousTask = new AtomicLong(Task.NO_ID);
|
|
||||||
final AtomicLong globalOrder = new AtomicLong(-1);
|
|
||||||
|
|
||||||
iterateThroughList(
|
|
||||||
list,
|
|
||||||
(taskId, googleTask) -> {
|
|
||||||
int indent = googleTask.getIndent();
|
|
||||||
|
|
||||||
long order = globalOrder.incrementAndGet();
|
|
||||||
googleTask.setOrder(order);
|
|
||||||
|
|
||||||
if (targetTaskId == taskId) {
|
|
||||||
// if indenting is warranted, indent me and my children
|
|
||||||
if (indent + delta <= previousIndent.get() + 1 && indent + delta >= 0) {
|
|
||||||
targetTaskIndent.set(indent);
|
|
||||||
googleTask.setIndent(indent + delta);
|
|
||||||
|
|
||||||
long newParent = computeNewParent(list, taskId, indent + delta - 1);
|
|
||||||
if (newParent == taskId) {
|
|
||||||
googleTask.setParent(Task.NO_ID);
|
|
||||||
} else {
|
|
||||||
googleTask.setParent(newParent);
|
|
||||||
}
|
|
||||||
saveAndUpdateModifiedDate(googleTask);
|
|
||||||
}
|
|
||||||
} else if (targetTaskIndent.get() > -1) {
|
|
||||||
// found first task that is not beneath target
|
|
||||||
if (indent <= targetTaskIndent.get()) {
|
|
||||||
targetTaskIndent.set(-1);
|
|
||||||
} else {
|
|
||||||
googleTask.setIndent(indent + delta);
|
|
||||||
saveAndUpdateModifiedDate(googleTask);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
previousIndent.set(indent);
|
|
||||||
previousTask.set(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAndUpdateModifiedDate(googleTask);
|
|
||||||
});
|
|
||||||
onMovedOrIndented(list, getTaskMetadata(targetTaskId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to iterate through a list and compute a new parent for the target task based on
|
|
||||||
* the target parent's indent
|
|
||||||
*/
|
|
||||||
private long computeNewParent(GoogleTaskList list, long targetTaskId, int targetParentIndent) {
|
|
||||||
final AtomicInteger desiredParentIndent = new AtomicInteger(targetParentIndent);
|
|
||||||
final AtomicLong targetTask = new AtomicLong(targetTaskId);
|
|
||||||
final AtomicLong lastPotentialParent = new AtomicLong(Task.NO_ID);
|
|
||||||
final AtomicBoolean computedParent = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
iterateThroughList(
|
|
||||||
list,
|
|
||||||
(taskId, googleTask) -> {
|
|
||||||
if (targetTask.get() == taskId) {
|
|
||||||
computedParent.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int indent = googleTask.getIndent();
|
|
||||||
if (!computedParent.get() && indent == desiredParentIndent.get()) {
|
|
||||||
lastPotentialParent.set(taskId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (lastPotentialParent.get() == Task.NO_ID) {
|
|
||||||
return Task.NO_ID;
|
|
||||||
}
|
|
||||||
return lastPotentialParent.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move a task and all its children to the position right above taskIdToMoveto. Will change the
|
|
||||||
* indent level to match taskIdToMoveTo.
|
|
||||||
*/
|
|
||||||
public void moveTo(GoogleTaskList list, final long targetTaskId, final long moveBeforeTaskId) {
|
|
||||||
if (list == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node root = buildTreeModel(list);
|
|
||||||
Node target = findNode(root, targetTaskId);
|
|
||||||
|
|
||||||
if (target != null && target.parent != null) {
|
|
||||||
if (moveBeforeTaskId == -1) {
|
|
||||||
target.parent.children.remove(target);
|
|
||||||
root.children.add(target);
|
|
||||||
target.parent = root;
|
|
||||||
} else {
|
|
||||||
Node sibling = findNode(root, moveBeforeTaskId);
|
|
||||||
if (sibling != null && !ancestorOf(target, sibling)) {
|
|
||||||
int index = sibling.parent.children.indexOf(sibling);
|
|
||||||
|
|
||||||
if (target.parent == sibling.parent && target.parent.children.indexOf(target) < index) {
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
|
|
||||||
target.parent.children.remove(target);
|
|
||||||
sibling.parent.children.add(index, target);
|
|
||||||
target.parent = sibling.parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
traverseTreeAndWriteValues(list, root, new AtomicLong(0), -1);
|
|
||||||
onMovedOrIndented(list, getTaskMetadata(targetTaskId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- task moving
|
|
||||||
|
|
||||||
private boolean ancestorOf(Node ancestor, Node descendant) {
|
|
||||||
if (descendant.parent == ancestor) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (descendant.parent == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ancestorOf(ancestor, descendant.parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void traverseTreeAndWriteValues(
|
|
||||||
GoogleTaskList list, Node node, AtomicLong order, int indent) {
|
|
||||||
if (node.taskId != Task.NO_ID) {
|
|
||||||
GoogleTask googleTask = getTaskMetadata(node.taskId);
|
|
||||||
if (googleTask == null) {
|
|
||||||
googleTask = new GoogleTask(node.taskId, list.getRemoteId());
|
|
||||||
}
|
|
||||||
googleTask.setOrder(order.getAndIncrement());
|
|
||||||
googleTask.setIndent(indent);
|
|
||||||
boolean parentChanged = false;
|
|
||||||
if (googleTask.getParent() != node.parent.taskId) {
|
|
||||||
parentChanged = true;
|
|
||||||
googleTask.setParent(node.parent.taskId);
|
|
||||||
}
|
|
||||||
saveAndUpdateModifiedDate(googleTask);
|
|
||||||
if (parentChanged) {
|
|
||||||
onMovedOrIndented(list, googleTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Node child : node.children) {
|
|
||||||
traverseTreeAndWriteValues(list, child, order, indent + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node findNode(Node node, long taskId) {
|
|
||||||
if (node.taskId == taskId) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
for (Node child : node.children) {
|
|
||||||
Node found = findNode(child, taskId);
|
|
||||||
if (found != null) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node buildTreeModel(GoogleTaskList list) {
|
|
||||||
final Node root = new Node(Task.NO_ID, null);
|
|
||||||
final AtomicInteger previoustIndent = new AtomicInteger(-1);
|
|
||||||
final AtomicReference<Node> currentNode = new AtomicReference<>(root);
|
|
||||||
|
|
||||||
iterateThroughList(
|
|
||||||
list,
|
|
||||||
(taskId, googleTask) -> {
|
|
||||||
int indent = googleTask.getIndent();
|
|
||||||
|
|
||||||
int previousIndentValue = previoustIndent.get();
|
|
||||||
if (indent == previousIndentValue) { // sibling
|
|
||||||
Node parent = currentNode.get().parent;
|
|
||||||
currentNode.set(new Node(taskId, parent));
|
|
||||||
parent.children.add(currentNode.get());
|
|
||||||
} else if (indent > previousIndentValue) { // child
|
|
||||||
Node parent = currentNode.get();
|
|
||||||
currentNode.set(new Node(taskId, parent));
|
|
||||||
parent.children.add(currentNode.get());
|
|
||||||
} else { // in a different tree
|
|
||||||
Node node = currentNode.get().parent;
|
|
||||||
for (int i = indent; i < previousIndentValue; i++) {
|
|
||||||
node = node.parent;
|
|
||||||
if (node == null) {
|
|
||||||
node = root;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentNode.set(new Node(taskId, node));
|
|
||||||
node.children.add(currentNode.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
previoustIndent.set(indent);
|
|
||||||
});
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveAndUpdateModifiedDate(GoogleTask googleTask) {
|
|
||||||
googleTaskDao.update(googleTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Apply an operation only to the children of the task */
|
|
||||||
public void applyToChildren(GoogleTaskList list, long targetTaskId, OrderedListNodeVisitor visitor) {
|
|
||||||
|
|
||||||
Node root = buildTreeModel(list);
|
|
||||||
Node target = findNode(root, targetTaskId);
|
|
||||||
|
|
||||||
if (target != null) {
|
|
||||||
for (Node child : target.children) {
|
|
||||||
applyVisitor(child, visitor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyVisitor(Node node, OrderedListNodeVisitor visitor) {
|
|
||||||
visitor.visitNode(node);
|
|
||||||
for (Node child : node.children) {
|
|
||||||
applyVisitor(child, visitor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- task cascading operations
|
|
||||||
|
|
||||||
/** Removes a task from the order hierarchy and un-indent children */
|
|
||||||
public void onDeleteTask(GoogleTaskList list, final long targetTaskId) {
|
|
||||||
if (list == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node root = buildTreeModel(list);
|
|
||||||
Node target = findNode(root, targetTaskId);
|
|
||||||
|
|
||||||
if (target != null && target.parent != null) {
|
|
||||||
int targetIndex = target.parent.children.indexOf(target);
|
|
||||||
target.parent.children.remove(targetIndex);
|
|
||||||
for (Node node : target.children) {
|
|
||||||
node.parent = target.parent;
|
|
||||||
target.parent.children.add(targetIndex++, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
traverseTreeAndWriteValues(list, root, new AtomicLong(0), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface OrderedListIterator {
|
|
||||||
|
|
||||||
void processTask(long taskId, GoogleTask googleTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface OrderedListNodeVisitor {
|
|
||||||
|
|
||||||
void visitNode(Node node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Node {
|
|
||||||
|
|
||||||
public final long taskId;
|
|
||||||
final ArrayList<Node> children = new ArrayList<>();
|
|
||||||
Node parent;
|
|
||||||
|
|
||||||
Node(long taskId, Node parent) {
|
|
||||||
this.taskId = taskId;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.astrid.gtasks.api;
|
|
||||||
|
|
||||||
import com.google.api.services.tasks.model.Task;
|
|
||||||
import java.io.IOException;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates a request to the api to change the ordering on the given task
|
|
||||||
*
|
|
||||||
* @author Sam Bosley
|
|
||||||
*/
|
|
||||||
public class MoveRequest {
|
|
||||||
|
|
||||||
private final GtasksInvoker service;
|
|
||||||
private final String taskId;
|
|
||||||
private final String destinationList;
|
|
||||||
private String parentId;
|
|
||||||
private String priorSiblingId;
|
|
||||||
|
|
||||||
public MoveRequest(
|
|
||||||
GtasksInvoker service,
|
|
||||||
String taskId,
|
|
||||||
String destinationList,
|
|
||||||
String parentId,
|
|
||||||
String priorSiblingId) {
|
|
||||||
this.service = service;
|
|
||||||
this.taskId = taskId;
|
|
||||||
this.destinationList = destinationList;
|
|
||||||
this.parentId = parentId;
|
|
||||||
this.priorSiblingId = priorSiblingId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task push() throws IOException {
|
|
||||||
try {
|
|
||||||
return executePush();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e);
|
|
||||||
recover();
|
|
||||||
return executePush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task executePush() throws IOException {
|
|
||||||
return service.moveGtask(destinationList, taskId, parentId, priorSiblingId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recover() {
|
|
||||||
parentId = null;
|
|
||||||
priorSiblingId = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.astrid.gtasks.sync;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
|
|
||||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
|
|
||||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
|
|
||||||
import com.todoroo.astrid.gtasks.api.MoveRequest;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.analytics.Tracker;
|
|
||||||
import org.tasks.data.GoogleTask;
|
|
||||||
import org.tasks.data.GoogleTaskDao;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.data.GoogleTaskListDao;
|
|
||||||
import org.tasks.gtasks.GoogleAccountManager;
|
|
||||||
import org.tasks.gtasks.GtaskSyncAdapterHelper;
|
|
||||||
import org.tasks.injection.ApplicationScope;
|
|
||||||
import org.tasks.preferences.Preferences;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
@ApplicationScope
|
|
||||||
public class GtasksSyncService {
|
|
||||||
|
|
||||||
private final TaskDao taskDao;
|
|
||||||
private final Preferences preferences;
|
|
||||||
private final LinkedBlockingQueue<SyncOnSaveOperation> operationQueue =
|
|
||||||
new LinkedBlockingQueue<>();
|
|
||||||
private final GtaskSyncAdapterHelper gtaskSyncAdapterHelper;
|
|
||||||
private final Tracker tracker;
|
|
||||||
private final GoogleTaskDao googleTaskDao;
|
|
||||||
private final GoogleAccountManager googleAccountManager;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GtasksSyncService(
|
|
||||||
TaskDao taskDao,
|
|
||||||
Preferences preferences,
|
|
||||||
GtaskSyncAdapterHelper gtaskSyncAdapterHelper,
|
|
||||||
Tracker tracker,
|
|
||||||
GoogleTaskDao googleTaskDao,
|
|
||||||
GoogleTaskListDao googleTaskListDao,
|
|
||||||
GoogleAccountManager googleAccountManager) {
|
|
||||||
this.taskDao = taskDao;
|
|
||||||
this.preferences = preferences;
|
|
||||||
this.gtaskSyncAdapterHelper = gtaskSyncAdapterHelper;
|
|
||||||
this.tracker = tracker;
|
|
||||||
this.googleTaskDao = googleTaskDao;
|
|
||||||
this.googleAccountManager = googleAccountManager;
|
|
||||||
new OperationPushThread(operationQueue).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void triggerMoveForMetadata(GoogleTaskList googleTaskList, GoogleTask googleTask) {
|
|
||||||
if (googleTask == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (googleTask.isSuppressSync()) {
|
|
||||||
googleTask.setSuppressSync(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (preferences.isSyncOngoing()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!gtaskSyncAdapterHelper.isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
operationQueue.offer(new MoveOp(googleTaskList, googleTask));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pushMetadataOnSave(GoogleTask model, GtasksInvoker invoker) throws IOException {
|
|
||||||
AndroidUtilities.sleepDeep(1000L);
|
|
||||||
|
|
||||||
String taskId = model.getRemoteId();
|
|
||||||
String listId = model.getListId();
|
|
||||||
String parent = getRemoteParentId(model);
|
|
||||||
String priorSibling = getRemoteSiblingId(listId, model);
|
|
||||||
|
|
||||||
MoveRequest move = new MoveRequest(invoker, taskId, listId, parent, priorSibling);
|
|
||||||
com.google.api.services.tasks.model.Task result = move.push();
|
|
||||||
// Update order googleTask from result
|
|
||||||
if (result != null) {
|
|
||||||
model.setRemoteOrder(Long.parseLong(result.getPosition()));
|
|
||||||
model.setSuppressSync(true);
|
|
||||||
googleTaskDao.update(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void iterateThroughList(
|
|
||||||
String listId,
|
|
||||||
final GtasksTaskListUpdater.OrderedListIterator iterator,
|
|
||||||
long startAtOrder,
|
|
||||||
boolean reverse) {
|
|
||||||
List<GoogleTask> tasks =
|
|
||||||
reverse
|
|
||||||
? googleTaskDao.getTasksFromReverse(listId, startAtOrder)
|
|
||||||
: googleTaskDao.getTasksFrom(listId, startAtOrder);
|
|
||||||
for (GoogleTask entry : tasks) {
|
|
||||||
iterator.processTask(entry.getTask(), entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the remote id string of the parent task */
|
|
||||||
public String getRemoteParentId(GoogleTask googleTask) {
|
|
||||||
String parent = null;
|
|
||||||
long parentId = googleTask.getParent();
|
|
||||||
GoogleTask parentTask = googleTaskDao.getByTaskId(parentId);
|
|
||||||
if (parentTask != null) {
|
|
||||||
parent = parentTask.getRemoteId();
|
|
||||||
if (TextUtils.isEmpty(parent)) {
|
|
||||||
parent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the remote id string of the previous sibling task */
|
|
||||||
public String getRemoteSiblingId(String listId, GoogleTask gtasksMetadata) {
|
|
||||||
final AtomicInteger indentToMatch = new AtomicInteger(gtasksMetadata.getIndent());
|
|
||||||
final AtomicLong parentToMatch = new AtomicLong(gtasksMetadata.getParent());
|
|
||||||
final AtomicReference<String> sibling = new AtomicReference<>();
|
|
||||||
GtasksTaskListUpdater.OrderedListIterator iterator =
|
|
||||||
(taskId, googleTask) -> {
|
|
||||||
Task t = taskDao.fetch(taskId);
|
|
||||||
if (t == null || t.isDeleted()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int currIndent = googleTask.getIndent();
|
|
||||||
long currParent = googleTask.getParent();
|
|
||||||
|
|
||||||
if (currIndent == indentToMatch.get() && currParent == parentToMatch.get()) {
|
|
||||||
if (sibling.get() == null) {
|
|
||||||
sibling.set(googleTask.getRemoteId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
iterateThroughList(listId, iterator, gtasksMetadata.getOrder(), true);
|
|
||||||
return sibling.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SyncOnSaveOperation {
|
|
||||||
|
|
||||||
void op() throws IOException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MoveOp implements SyncOnSaveOperation {
|
|
||||||
|
|
||||||
final GoogleTask googleTask;
|
|
||||||
private final GoogleTaskList googleTaskList;
|
|
||||||
|
|
||||||
MoveOp(GoogleTaskList googleTaskList, GoogleTask googleTask) {
|
|
||||||
this.googleTaskList = googleTaskList;
|
|
||||||
this.googleTask = googleTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void op() throws IOException {
|
|
||||||
GtasksInvoker invoker = new GtasksInvoker(googleTaskList.getAccount(), googleAccountManager);
|
|
||||||
pushMetadataOnSave(googleTask, invoker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OperationPushThread extends Thread {
|
|
||||||
|
|
||||||
private final LinkedBlockingQueue<SyncOnSaveOperation> queue;
|
|
||||||
|
|
||||||
OperationPushThread(LinkedBlockingQueue<SyncOnSaveOperation> queue) {
|
|
||||||
this.queue = queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
//noinspection InfiniteLoopStatement
|
|
||||||
while (true) {
|
|
||||||
SyncOnSaveOperation op;
|
|
||||||
try {
|
|
||||||
op = queue.take();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Timber.e(e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
op.op();
|
|
||||||
} catch (UserRecoverableAuthIOException ignored) {
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
tracker.reportException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +1,41 @@
|
|||||||
package org.tasks.tasklist;
|
package org.tasks.tasklist;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback;
|
|
||||||
import com.todoroo.astrid.adapter.TaskAdapter;
|
import com.todoroo.astrid.adapter.TaskAdapter;
|
||||||
|
import java.util.List;
|
||||||
import org.tasks.data.TaskContainer;
|
import org.tasks.data.TaskContainer;
|
||||||
|
|
||||||
class DiffCallback extends ItemCallback<TaskContainer> {
|
class DiffCallback extends DiffUtil.Callback {
|
||||||
|
|
||||||
private final TaskAdapter adapter;
|
private final List<TaskContainer> oldList;
|
||||||
|
private final List<TaskContainer> newList;
|
||||||
|
@Deprecated private final TaskAdapter adapter;
|
||||||
|
|
||||||
public DiffCallback(TaskAdapter adapter) {
|
DiffCallback(List<TaskContainer> oldList, List<TaskContainer> newList, TaskAdapter adapter) {
|
||||||
|
this.oldList = oldList;
|
||||||
|
this.newList = newList;
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areItemsTheSame(@NonNull TaskContainer oldItem, @NonNull TaskContainer newItem) {
|
public int getOldListSize() {
|
||||||
return oldItem.getId() == newItem.getId();
|
return oldList.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean areContentsTheSame(
|
public int getNewListSize() {
|
||||||
@NonNull TaskContainer oldItem, @NonNull TaskContainer newItem) {
|
return newList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
TaskContainer oldItem = oldList.get(oldItemPosition);
|
||||||
|
TaskContainer newItem = newList.get(newItemPosition);
|
||||||
return oldItem.equals(newItem) && oldItem.getIndent() == adapter.getIndent(newItem);
|
return oldItem.equals(newItem) && oldItem.getIndent() == adapter.getIndent(newItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue