diff --git a/app/schemas/com.todoroo.astrid.dao.Database/77.json b/app/schemas/com.todoroo.astrid.dao.Database/77.json
new file mode 100644
index 000000000..8c1402c71
--- /dev/null
+++ b/app/schemas/com.todoroo.astrid.dao.Database/77.json
@@ -0,0 +1,1186 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 77,
+ "identityHash": "744a4f4c3ec8ff862cbef93ef76de6d1",
+ "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 IF NOT EXISTS `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, `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": "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 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, `notifications` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `snoozeTime` INTEGER NOT NULL, `recurrence` TEXT, `repeatUntil` INTEGER NOT NULL, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL, `parent_uuid` TEXT)",
+ "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": "reminderFlags",
+ "columnName": "notificationFlags",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reminderPeriod",
+ "columnName": "notifications",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reminderLast",
+ "columnName": "lastNotified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reminderSnooze",
+ "columnName": "snoozeTime",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recurrence",
+ "columnName": "recurrence",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "repeatUntil",
+ "columnName": "repeatUntil",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "parentUuid",
+ "columnName": "parent_uuid",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "t_rid",
+ "unique": true,
+ "columnNames": [
+ "remoteId"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `${TABLE_NAME}` (`remoteId`)"
+ },
+ {
+ "name": "active_and_visible",
+ "unique": false,
+ "columnNames": [
+ "completed",
+ "deleted",
+ "hideUntil"
+ ],
+ "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)",
+ "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, `place_color` INTEGER NOT NULL, `place_icon` INTEGER NOT NULL, `place_order` INTEGER 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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "place_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "place_uid",
+ "unique": true,
+ "columnNames": [
+ "uid"
+ ],
+ "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, `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": "isArrival",
+ "columnName": "arrival",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDeparture",
+ "columnName": "departure",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "geofence_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "geo_task",
+ "unique": false,
+ "columnNames": [
+ "task"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `geo_task` ON `${TABLE_NAME}` (`task`)"
+ }
+ ],
+ "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": [
+ {
+ "name": "tag_task",
+ "unique": false,
+ "columnNames": [
+ "task"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `tag_task` ON `${TABLE_NAME}` (`task`)"
+ }
+ ],
+ "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": "isMoved",
+ "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": [
+ {
+ "name": "gt_task",
+ "unique": false,
+ "columnNames": [
+ "gt_task"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `gt_task` ON `${TABLE_NAME}` (`gt_task`)"
+ },
+ {
+ "name": "gt_list_parent",
+ "unique": false,
+ "columnNames": [
+ "gt_list_id",
+ "gt_parent"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `gt_list_parent` ON `${TABLE_NAME}` (`gt_list_id`, `gt_parent`)"
+ }
+ ],
+ "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, `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": "google_task_lists",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`gtl_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gtl_account` TEXT, `gtl_remote_id` TEXT, `gtl_title` TEXT, `gtl_remote_order` INTEGER NOT NULL, `gtl_last_sync` INTEGER NOT NULL, `gtl_color` INTEGER, `gtl_icon` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "gtl_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "account",
+ "columnName": "gtl_account",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteId",
+ "columnName": "gtl_remote_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "gtl_title",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "gtl_remote_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastSync",
+ "columnName": "gtl_last_sync",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "gtl_color",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "icon",
+ "columnName": "gtl_icon",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "gtl_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)",
+ "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
+ }
+ ],
+ "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_vtodo` TEXT, `cd_remote_parent` TEXT, `cd_order` INTEGER)",
+ "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": "vtodo",
+ "columnName": "cd_vtodo",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "remoteParent",
+ "columnName": "cd_remote_parent",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "cd_order",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "cd_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "cd_task",
+ "unique": false,
+ "columnNames": [
+ "cd_task"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `cd_task` ON `${TABLE_NAME}` (`cd_task`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "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_repeat` INTEGER NOT NULL, `cda_encryption_key` TEXT, `cda_account_type` INTEGER NOT NULL, `cda_collapsed` 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": "isSuppressRepeatingTasks",
+ "columnName": "cda_repeat",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "encryptionKey",
+ "columnName": "cda_encryption_key",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "accountType",
+ "columnName": "cda_account_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCollapsed",
+ "columnName": "cda_collapsed",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "cda_id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "google_task_accounts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`gta_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gta_account` TEXT, `gta_error` TEXT, `gta_etag` TEXT, `gta_collapsed` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "gta_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "account",
+ "columnName": "gta_account",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "error",
+ "columnName": "gta_error",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "etag",
+ "columnName": "gta_etag",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isCollapsed",
+ "columnName": "gta_collapsed",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "gta_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, '744a4f4c3ec8ff862cbef93ef76de6d1')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/org/tasks/caldav/CaldavSynchronizerTest.kt b/app/src/androidTest/java/org/tasks/caldav/CaldavSynchronizerTest.kt
index 71e63b074..1b6bb183c 100644
--- a/app/src/androidTest/java/org/tasks/caldav/CaldavSynchronizerTest.kt
+++ b/app/src/androidTest/java/org/tasks/caldav/CaldavSynchronizerTest.kt
@@ -119,6 +119,44 @@ class CaldavSynchronizerTest : CaldavTest() {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /remote.php/dav/principals/users/user1/
+
HTTP/1.1 200 OK
diff --git a/app/src/androidTest/java/org/tasks/caldav/OwnCloudSynchronizationTest.kt b/app/src/androidTest/java/org/tasks/caldav/OwnCloudSynchronizationTest.kt
new file mode 100644
index 000000000..ccdb7ba4a
--- /dev/null
+++ b/app/src/androidTest/java/org/tasks/caldav/OwnCloudSynchronizationTest.kt
@@ -0,0 +1,134 @@
+package org.tasks.caldav
+
+import com.todoroo.astrid.helper.UUIDHelper
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.UninstallModules
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.tasks.data.CaldavAccount
+import org.tasks.data.CaldavCalendar
+import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER
+import org.tasks.injection.ProductionModule
+
+@UninstallModules(ProductionModule::class)
+@HiltAndroidTest
+class OwnCloudSynchronizationTest : CaldavTest() {
+
+ @Before
+ override fun setUp() = runBlocking {
+ super.setUp()
+ account = CaldavAccount().apply {
+ uuid = UUIDHelper.newUUID()
+ username = "username"
+ password = encryption.encrypt("password")
+ url = server.url("/remote.php/dav/calendars/user1/").toString()
+ id = caldavDao.insert(this)
+ }
+ }
+
+ @Test
+ fun calendarOwner() = runBlocking {
+ val calendar = CaldavCalendar().apply {
+ account = this@OwnCloudSynchronizationTest.account.uuid
+ ctag = "http://sabre.io/ns/sync/1"
+ url = "${this@OwnCloudSynchronizationTest.account.url}test-shared/"
+ caldavDao.insert(this)
+ }
+ enqueue(OC_OWNER)
+ enqueueFailure()
+
+ synchronizer.sync(account)
+
+ assertEquals(ACCESS_OWNER, caldavDao.getCalendarByUuid(calendar.uuid!!)?.access)
+ }
+
+ companion object {
+ init {
+ CaldavSynchronizer.registerFactories()
+ }
+
+ private val OC_OWNER = """
+
+
+
+ /remote.php/dav/calendars/user1/test-shared/
+
+
+
+
+
+
+ Test shared
+
+
+
+ http://sabre.io/ns/sync/1
+ #0082c9
+ http://sabre.io/ns/sync/1
+ principals/users/user1
+
+
+ principal:principals/users/user2
+ user2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /remote.php/dav/principals/users/user1/
+
+
+ HTTP/1.1 200 OK
+
+
+
+
+
+
+ HTTP/1.1 404 Not Found
+
+
+
+ """.trimIndent()
+ }
+}
\ No newline at end of file
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 01d407ef3..41fe07e92 100644
--- a/app/src/main/java/com/todoroo/astrid/dao/Database.kt
+++ b/app/src/main/java/com/todoroo/astrid/dao/Database.kt
@@ -27,7 +27,7 @@ import org.tasks.notifications.NotificationDao
CaldavTask::class,
CaldavAccount::class,
GoogleTaskAccount::class],
- version = 76)
+ version = 77)
abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDao
abstract val tagDataDao: TagDataDao
diff --git a/app/src/main/java/org/tasks/Tasks.kt b/app/src/main/java/org/tasks/Tasks.kt
index 917af0cf2..2b5bf8dbe 100644
--- a/app/src/main/java/org/tasks/Tasks.kt
+++ b/app/src/main/java/org/tasks/Tasks.kt
@@ -8,7 +8,6 @@ import android.util.Log
import androidx.core.app.JobIntentService
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
-import at.bitfire.dav4jvm.PropertyRegistry
import com.todoroo.astrid.service.Upgrader
import dagger.Lazy
import dagger.hilt.android.HiltAndroidApp
@@ -18,11 +17,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.tasks.billing.BillingClient
import org.tasks.billing.Inventory
-import org.tasks.caldav.property.Invite
-import org.tasks.caldav.property.OCInvite
-import org.tasks.caldav.property.OCOwnerPrincipal
-import org.tasks.caldav.property.PropertyUtils.register
-import org.tasks.caldav.property.ShareAccess
+import org.tasks.caldav.CaldavSynchronizer
import org.tasks.files.FileHelper
import org.tasks.injection.InjectingJobIntentService
import org.tasks.jobs.WorkManager
@@ -96,12 +91,7 @@ class Tasks : Application(), Configuration.Provider {
FileHelper.delete(context, preferences.cacheDirectory)
billingClient.get().queryPurchases()
appWidgetManager.get().reconfigureWidgets()
- PropertyRegistry.register(
- ShareAccess.Factory(),
- Invite.Factory(),
- OCOwnerPrincipal.Factory(),
- OCInvite.Factory(),
- )
+ CaldavSynchronizer.registerFactories()
}
override fun getWorkManagerConfiguration(): Configuration = Configuration.Builder()
diff --git a/app/src/main/java/org/tasks/caldav/CaldavClient.kt b/app/src/main/java/org/tasks/caldav/CaldavClient.kt
index 48bc87a1e..dc2eee12c 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavClient.kt
+++ b/app/src/main/java/org/tasks/caldav/CaldavClient.kt
@@ -224,6 +224,8 @@ open class CaldavClient(
Invite.NAME,
OCOwnerPrincipal.NAME,
OCInvite.NAME,
+ CurrentUserPrivilegeSet.NAME,
+ CurrentUserPrincipal.NAME,
)
private suspend fun DavResource.propfind(
diff --git a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt
index e5e542434..4dc27a889 100644
--- a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt
+++ b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt
@@ -4,6 +4,7 @@ import android.content.Context
import at.bitfire.dav4jvm.DavCalendar
import at.bitfire.dav4jvm.DavCalendar.Companion.MIME_ICALENDAR
import at.bitfire.dav4jvm.DavResource
+import at.bitfire.dav4jvm.PropertyRegistry
import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation
import at.bitfire.dav4jvm.exception.DavException
@@ -29,9 +30,19 @@ import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory
import org.tasks.caldav.iCalendar.Companion.fromVtodo
+import org.tasks.caldav.property.Invite
+import org.tasks.caldav.property.OCInvite
+import org.tasks.caldav.property.OCOwnerPrincipal
+import org.tasks.caldav.property.PropertyUtils.register
+import org.tasks.caldav.property.ShareAccess
+import org.tasks.caldav.property.ShareAccess.Companion.READ
+import org.tasks.caldav.property.ShareAccess.Companion.SHARED_OWNER
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavAccount.Companion.ERROR_UNAUTHORIZED
import org.tasks.data.CaldavCalendar
+import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER
+import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_ONLY
+import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_WRITE
import org.tasks.data.CaldavDao
import org.tasks.data.CaldavTask
import timber.log.Timber
@@ -120,6 +131,7 @@ class CaldavSynchronizer @Inject constructor(
var calendar = caldavDao.getCalendarByUrl(account.uuid!!, url)
val remoteName = resource[DisplayName::class.java]!!.displayName
val calendarColor = resource[CalendarColor::class.java]
+ val access = resource.accessLevel
val color = calendarColor?.color ?: 0
if (calendar == null) {
calendar = CaldavCalendar()
@@ -128,10 +140,15 @@ class CaldavSynchronizer @Inject constructor(
calendar.url = url
calendar.uuid = UUIDHelper.newUUID()
calendar.color = color
+ calendar.access = access
caldavDao.insert(calendar)
- } else if (calendar.name != remoteName || calendar.color != color) {
+ } else if (calendar.name != remoteName
+ || calendar.color != color
+ || calendar.access != access
+ ) {
calendar.color = color
calendar.name = remoteName
+ calendar.access = access
caldavDao.update(calendar)
localBroadcastManager.broadcastRefreshList()
}
@@ -156,7 +173,9 @@ class CaldavSynchronizer @Inject constructor(
httpClient: OkHttpClient) {
Timber.d("sync(%s)", caldavCalendar)
val httpUrl = resource.href
- pushLocalChanges(caldavCalendar, httpClient, httpUrl)
+ if (caldavCalendar.access != ACCESS_READ_ONLY) {
+ pushLocalChanges(caldavCalendar, httpClient, httpUrl)
+ }
val remoteCtag = resource.ctag
if (caldavCalendar.ctag?.equals(remoteCtag) == true) {
Timber.d("%s up to date", caldavCalendar.name)
@@ -291,7 +310,37 @@ class CaldavSynchronizer @Inject constructor(
prodId = ProdId("+//IDN tasks.org//android-" + BuildConfig.VERSION_CODE + "//EN")
}
+ fun registerFactories() {
+ PropertyRegistry.register(
+ ShareAccess.Factory(),
+ Invite.Factory(),
+ OCOwnerPrincipal.Factory(),
+ OCInvite.Factory(),
+ )
+ }
+
val Response.ctag: String?
get() = this[SyncToken::class.java]?.token ?: this[GetCTag::class.java]?.cTag
+
+ val Response.accessLevel: Int
+ get() {
+ this[ShareAccess::class.java]?.let {
+ return when (it.access) {
+ SHARED_OWNER -> ACCESS_OWNER
+ READ -> ACCESS_READ_ONLY
+ else -> ACCESS_READ_WRITE
+ }
+ }
+ this[OCOwnerPrincipal::class.java]?.owner?.let {
+ val current = this[CurrentUserPrincipal::class.java]?.href
+ if (current?.endsWith("$it/") == true) {
+ return ACCESS_OWNER
+ }
+ }
+ return when (this[CurrentUserPrivilegeSet::class.java]?.mayWriteContent) {
+ false -> ACCESS_READ_ONLY
+ else -> ACCESS_READ_WRITE
+ }
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/data/CaldavCalendar.kt b/app/src/main/java/org/tasks/data/CaldavCalendar.kt
index 6606466aa..be0b5f78e 100644
--- a/app/src/main/java/org/tasks/data/CaldavCalendar.kt
+++ b/app/src/main/java/org/tasks/data/CaldavCalendar.kt
@@ -42,6 +42,9 @@ class CaldavCalendar : Parcelable {
@ColumnInfo(name = "cdl_order")
var order = NO_ORDER
+ @ColumnInfo(name = "cdl_access")
+ var access = 0
+
constructor()
@Ignore
@@ -61,6 +64,7 @@ class CaldavCalendar : Parcelable {
url = source.readString()
icon = source.readInt()
order = source.readInt()
+ access = source.readInt()
}
fun getIcon(): Int? {
@@ -84,6 +88,7 @@ class CaldavCalendar : Parcelable {
writeString(url)
writeInt(getIcon()!!)
writeInt(order)
+ writeInt(access)
}
}
@@ -100,6 +105,7 @@ class CaldavCalendar : Parcelable {
if (url != other.url) return false
if (icon != other.icon) return false
if (order != other.order) return false
+ if (access != other.access) return false
return true
}
@@ -114,13 +120,19 @@ class CaldavCalendar : Parcelable {
result = 31 * result + (url?.hashCode() ?: 0)
result = 31 * result + (icon ?: 0)
result = 31 * result + order
+ result = 31 * result + access
return result
}
- override fun toString(): String =
- "CaldavCalendar(id=$id, account=$account, uuid=$uuid, name=$name, color=$color, ctag=$ctag, url=$url, icon=$icon, order=$order)"
+ override fun toString(): String {
+ return "CaldavCalendar(id=$id, account=$account, uuid=$uuid, name=$name, color=$color, ctag=$ctag, url=$url, icon=$icon, order=$order, access=$access)"
+ }
companion object {
+ const val ACCESS_OWNER = 0
+ const val ACCESS_READ_WRITE = 1
+ const val ACCESS_READ_ONLY = 2
+
@JvmField val TABLE = Table("caldav_lists")
val ACCOUNT = TABLE.column("cdl_account")
@JvmField val UUID = TABLE.column("cdl_uuid")
diff --git a/app/src/main/java/org/tasks/db/Migrations.kt b/app/src/main/java/org/tasks/db/Migrations.kt
index d764a19f7..2bf1292ef 100644
--- a/app/src/main/java/org/tasks/db/Migrations.kt
+++ b/app/src/main/java/org/tasks/db/Migrations.kt
@@ -357,6 +357,13 @@ object Migrations {
}
}
+ private val MIGRATION_76_77: Migration = object : Migration(76, 77) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL(
+ "ALTER TABLE `caldav_lists` ADD COLUMN `cdl_access` INTEGER NOT NULL DEFAULT 0")
+ }
+ }
+
val MIGRATIONS = arrayOf(
MIGRATION_35_36,
MIGRATION_36_37,
@@ -389,7 +396,8 @@ object Migrations {
MIGRATION_72_73,
MIGRATION_73_74,
MIGRATION_74_75,
- MIGRATION_75_76
+ MIGRATION_75_76,
+ MIGRATION_76_77,
)
private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) {