diff --git a/app/schemas/com.todoroo.astrid.dao.Database/72.json b/app/schemas/com.todoroo.astrid.dao.Database/72.json new file mode 100644 index 000000000..aa4f6c184 --- /dev/null +++ b/app/schemas/com.todoroo.astrid.dao.Database/72.json @@ -0,0 +1,1138 @@ +{ + "formatVersion": 1, + "database": { + "version": 72, + "identityHash": "b123c7de9f2287377d2f8febb60dc301", + "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)", + "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 + } + ], + "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, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL, `parent_uuid` 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 + }, + { + "fieldPath": "collapsed", + "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)", + "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": [ + { + "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": "arrival", + "columnName": "arrival", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "departure", + "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": "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": [ + { + "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)", + "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 + } + ], + "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": "remoteOrder", + "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)", + "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 + } + ], + "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)", + "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 + } + ], + "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": "suppressRepeatingTasks", + "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": "collapsed", + "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": "collapsed", + "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, 'b123c7de9f2287377d2f8febb60dc301')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java index 2ce4b4e7e..0ff8c9c04 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java @@ -17,14 +17,19 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterListItem; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; +import org.tasks.LocalBroadcastManager; import org.tasks.billing.Inventory; +import org.tasks.data.CaldavDao; +import org.tasks.data.GoogleTaskDao; import org.tasks.filters.NavigationDrawerSubheader; import org.tasks.locale.Locale; +import org.tasks.preferences.Preferences; import org.tasks.themes.ColorProvider; import org.tasks.themes.Theme; import org.tasks.themes.ThemeAccent; @@ -39,6 +44,10 @@ public class FilterAdapter extends BaseAdapter { private final Locale locale; private final Inventory inventory; private final ColorProvider colorProvider; + private final Preferences preferences; + private final GoogleTaskDao googleTaskDao; + private final CaldavDao caldavDao; + private final LocalBroadcastManager localBroadcastManager; private final LayoutInflater inflater; private Filter selected = null; private List items = new ArrayList<>(); @@ -49,12 +58,20 @@ public class FilterAdapter extends BaseAdapter { Theme theme, Locale locale, Inventory inventory, - ColorProvider colorProvider) { + ColorProvider colorProvider, + Preferences preferences, + GoogleTaskDao googleTaskDao, + CaldavDao caldavDao, + LocalBroadcastManager localBroadcastManager) { this.activity = activity; this.accent = theme.getThemeAccent(); this.locale = locale; this.inventory = inventory; this.colorProvider = colorProvider; + this.preferences = preferences; + this.googleTaskDao = googleTaskDao; + this.caldavDao = caldavDao; + this.localBroadcastManager = localBroadcastManager; this.inflater = theme.getLayoutInflater(activity); } @@ -69,13 +86,9 @@ public class FilterAdapter extends BaseAdapter { } public void setData(List items, @Nullable Filter selected) { - setData(items, selected, -1); - } - - public void setData(List items, @Nullable Filter selected, int defaultIndex) { assertMainThread(); this.items = items; - this.selected = defaultIndex >= 0 ? getFilter(indexOf(selected, defaultIndex)) : selected; + this.selected = selected; notifyDataSetChanged(); } @@ -91,11 +104,6 @@ public class FilterAdapter extends BaseAdapter { return items.get(position); } - private Filter getFilter(int position) { - FilterListItem item = getItem(position); - return item instanceof Filter ? (Filter) item : null; - } - @Override public long getItemId(int position) { return position; @@ -105,7 +113,7 @@ public class FilterAdapter extends BaseAdapter { private View newView(View convertView, ViewGroup parent, FilterListItem.Type viewType) { if (convertView == null) { convertView = inflater.inflate(viewType.layout, parent, false); - FilterViewHolder viewHolder; + ViewHolder viewHolder; switch (viewType) { case ITEM: viewHolder = @@ -116,7 +124,14 @@ public class FilterAdapter extends BaseAdapter { viewHolder = new FilterViewHolder(convertView); break; case SUBHEADER: - viewHolder = new FilterViewHolder(convertView, activity); + viewHolder = + new SubheaderViewHolder( + convertView, + activity, + preferences, + googleTaskDao, + caldavDao, + localBroadcastManager); break; default: throw new RuntimeException(); @@ -142,13 +157,13 @@ public class FilterAdapter extends BaseAdapter { public View getView(int position, View convertView, @NonNull ViewGroup parent) { FilterListItem item = getItem(position); convertView = newView(convertView, parent, item.getItemType()); - FilterViewHolder viewHolder = (FilterViewHolder) convertView.getTag(); + ViewHolder viewHolder = (ViewHolder) convertView.getTag(); switch (item.getItemType()) { case ITEM: - viewHolder.bind(item, item.equals(selected), 0); + ((FilterViewHolder) viewHolder).bind(item, item.equals(selected), 0); break; case SUBHEADER: - viewHolder.bind((NavigationDrawerSubheader) item); + ((SubheaderViewHolder) viewHolder).bind((NavigationDrawerSubheader) item); break; case SEPARATOR: break; diff --git a/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.java b/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.java index d025516bb..48eb69565 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/FilterViewHolder.java @@ -25,7 +25,6 @@ import com.todoroo.astrid.api.GtasksFilter; import com.todoroo.astrid.api.TagFilter; import org.tasks.R; import org.tasks.billing.Inventory; -import org.tasks.filters.NavigationDrawerSubheader; import org.tasks.locale.Locale; import org.tasks.preferences.SyncPreferences; import org.tasks.themes.ColorProvider; @@ -164,11 +163,6 @@ public class FilterViewHolder extends RecyclerView.ViewHolder { } } - public void bind(NavigationDrawerSubheader filter) { - text.setText(filter.listingTitle); - icon.setVisibility(filter.error ? View.VISIBLE : View.GONE); - } - public interface OnClick { void onClick(@Nullable FilterListItem item); } diff --git a/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.java index 237bb8432..7b02b9196 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.java @@ -20,6 +20,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.DiffUtil.ItemCallback; import androidx.recyclerview.widget.ListAdapter; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.todoroo.astrid.adapter.FilterViewHolder.OnClick; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterListItem; @@ -27,14 +28,18 @@ import com.todoroo.astrid.api.FilterListItem.Type; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; +import org.tasks.LocalBroadcastManager; import org.tasks.billing.Inventory; +import org.tasks.data.CaldavDao; +import org.tasks.data.GoogleTaskDao; import org.tasks.filters.NavigationDrawerSubheader; import org.tasks.locale.Locale; +import org.tasks.preferences.Preferences; import org.tasks.themes.ColorProvider; import org.tasks.themes.Theme; import org.tasks.themes.ThemeAccent; -public class NavigationDrawerAdapter extends ListAdapter { +public class NavigationDrawerAdapter extends ListAdapter { private static final String TOKEN_SELECTED = "token_selected"; private final Activity activity; @@ -42,6 +47,10 @@ public class NavigationDrawerAdapter extends ListAdapter= 0 ? item.count : counts.get(item)); + ((FilterViewHolder) holder) + .bind(item, item.equals(selected), item.count >= 0 ? item.count : counts.get(item)); } else if (type == SUBHEADER) { - holder.bind((NavigationDrawerSubheader) item); + ((SubheaderViewHolder) holder).bind((NavigationDrawerSubheader) item); } } diff --git a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderViewHolder.java b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderViewHolder.java new file mode 100644 index 000000000..7580a3560 --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderViewHolder.java @@ -0,0 +1,83 @@ +package com.todoroo.astrid.adapter; + +import android.app.Activity; +import android.content.Intent; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import org.tasks.LocalBroadcastManager; +import org.tasks.R; +import org.tasks.data.CaldavDao; +import org.tasks.data.GoogleTaskDao; +import org.tasks.filters.NavigationDrawerSubheader; +import org.tasks.preferences.Preferences; +import org.tasks.preferences.SyncPreferences; +import org.tasks.themes.DrawableUtil; + +public class SubheaderViewHolder extends RecyclerView.ViewHolder { + + private final Preferences preferences; + private final GoogleTaskDao googleTaskDao; + private final CaldavDao caldavDao; + private final LocalBroadcastManager localBroadcastManager; + @BindView(R.id.text) + TextView text; + + @BindView(R.id.icon_error) + ImageView errorIcon; + + private NavigationDrawerSubheader subheader; + + SubheaderViewHolder( + @NonNull View itemView, + Activity activity, + Preferences preferences, + GoogleTaskDao googleTaskDao, + CaldavDao caldavDao, + LocalBroadcastManager localBroadcastManager) { + super(itemView); + this.preferences = preferences; + this.googleTaskDao = googleTaskDao; + this.caldavDao = caldavDao; + this.localBroadcastManager = localBroadcastManager; + + ButterKnife.bind(this, itemView); + + errorIcon.setOnClickListener( + v -> activity.startActivity(new Intent(activity, SyncPreferences.class))); + } + + @OnClick(R.id.subheader_row) + public void onClick() { + boolean collapsed = !subheader.isCollapsed(); + switch (subheader.getSubheaderType()) { + case PREFERENCE: + preferences.setBoolean((int) subheader.getId(), collapsed); + break; + case GOOGLE_TASKS: + googleTaskDao.setCollapsed(subheader.getId(), collapsed); + break; + case CALDAV: + caldavDao.setCollapsed(subheader.getId(), collapsed); + break; + } + localBroadcastManager.broadcastRefreshList(); + } + + public void bind(NavigationDrawerSubheader subheader) { + this.subheader = subheader; + text.setText(subheader.listingTitle); + errorIcon.setVisibility(subheader.error ? View.VISIBLE : View.GONE); + DrawableUtil.setRightDrawable( + itemView.getContext(), + text, + subheader.isCollapsed() + ? R.drawable.ic_keyboard_arrow_up_black_18dp + : R.drawable.ic_keyboard_arrow_down_black_18dp); + } +} diff --git a/app/src/main/java/com/todoroo/astrid/dao/Database.java b/app/src/main/java/com/todoroo/astrid/dao/Database.java index 83c1add5b..dab287ae9 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/Database.java +++ b/app/src/main/java/com/todoroo/astrid/dao/Database.java @@ -58,7 +58,7 @@ import org.tasks.notifications.NotificationDao; CaldavAccount.class, GoogleTaskAccount.class }, - version = 71) + version = 72) public abstract class Database extends RoomDatabase { public static final String NAME = "database"; diff --git a/app/src/main/java/org/tasks/Function.java b/app/src/main/java/org/tasks/Function.java new file mode 100644 index 000000000..606a5fa02 --- /dev/null +++ b/app/src/main/java/org/tasks/Function.java @@ -0,0 +1,5 @@ +package org.tasks; + +public interface Function { + T call(); +} diff --git a/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java b/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java index 6df447986..e721facdb 100644 --- a/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java +++ b/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java @@ -1,5 +1,7 @@ package org.tasks.activities; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import com.todoroo.andlib.utility.AndroidUtilities; @@ -10,6 +12,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; import javax.inject.Inject; +import org.tasks.LocalBroadcastManager; import org.tasks.dialogs.DialogBuilder; import org.tasks.filters.FilterProvider; import org.tasks.injection.ActivityComponent; @@ -26,9 +29,16 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity { @Inject DialogBuilder dialogBuilder; @Inject FilterAdapter filterAdapter; @Inject FilterProvider filterProvider; + @Inject LocalBroadcastManager localBroadcastManager; private CompositeDisposable disposables; private Filter selected; + private BroadcastReceiver refreshReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + refresh(); + } + }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -72,17 +82,18 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity { super.onResume(); disposables = new CompositeDisposable(); - disposables.add( - Single.fromCallable(() -> filterProvider.getItems(false)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(items -> filterAdapter.setData(items, selected))); + + localBroadcastManager.registerRefreshListReceiver(refreshReceiver); + + refresh(); } @Override protected void onPause() { super.onPause(); + localBroadcastManager.unregisterReceiver(refreshReceiver); + disposables.dispose(); } @@ -93,6 +104,14 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity { filterAdapter.save(outState); } + private void refresh() { + disposables.add( + Single.fromCallable(() -> filterProvider.getItems(false)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(items -> filterAdapter.setData(items, selected))); + } + @Override public void inject(ActivityComponent component) { component.inject(this); diff --git a/app/src/main/java/org/tasks/activities/RemoteListPicker.java b/app/src/main/java/org/tasks/activities/RemoteListPicker.java index 00b92943f..9306ac661 100644 --- a/app/src/main/java/org/tasks/activities/RemoteListPicker.java +++ b/app/src/main/java/org/tasks/activities/RemoteListPicker.java @@ -2,6 +2,8 @@ package org.tasks.activities; import android.app.Activity; import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import androidx.annotation.NonNull; @@ -17,6 +19,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; import javax.inject.Inject; +import org.tasks.LocalBroadcastManager; import org.tasks.R; import org.tasks.dialogs.AlertDialogBuilder; import org.tasks.dialogs.DialogBuilder; @@ -37,8 +40,15 @@ public class RemoteListPicker extends InjectingDialogFragment @Inject FilterAdapter filterAdapter; @Inject FilterProvider filterProvider; @Inject SyncAdapters syncAdapters; + @Inject LocalBroadcastManager localBroadcastManager; private CompositeDisposable disposables; + private BroadcastReceiver refreshReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + refresh(); + } + }; public static RemoteListPicker newRemoteListSupportPicker( Filter selected, Fragment targetFragment, int requestCode) { @@ -103,22 +113,19 @@ public class RemoteListPicker extends InjectingDialogFragment public void onResume() { super.onResume(); - Bundle arguments = getArguments(); - boolean noSelection = arguments.getBoolean(EXTRA_NO_SELECTION, false); - Filter selected = noSelection ? null : arguments.getParcelable(EXTRA_SELECTED_FILTER); + disposables = new CompositeDisposable(); - disposables = - new CompositeDisposable( - Single.fromCallable(filterProvider::getRemoteListPickerItems) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(items -> filterAdapter.setData(items, selected, noSelection ? -1 : 0))); + localBroadcastManager.registerRefreshListReceiver(refreshReceiver); + + refresh(); } @Override public void onPause() { super.onPause(); + localBroadcastManager.unregisterReceiver(refreshReceiver); + disposables.dispose(); } @@ -147,4 +154,15 @@ public class RemoteListPicker extends InjectingDialogFragment Activity.RESULT_OK, new Intent().putExtra(EXTRA_SELECTED_FILTER, filter)); } + + private void refresh() { + Bundle arguments = getArguments(); + boolean noSelection = arguments.getBoolean(EXTRA_NO_SELECTION, false); + Filter selected = noSelection ? null : arguments.getParcelable(EXTRA_SELECTED_FILTER); + + disposables.add(Single.fromCallable(filterProvider::getRemoteListPickerItems) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(items -> filterAdapter.setData(items, selected))); + } } diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.java b/app/src/main/java/org/tasks/data/CaldavAccount.java index dbb60c837..540f5d9e5 100644 --- a/app/src/main/java/org/tasks/data/CaldavAccount.java +++ b/app/src/main/java/org/tasks/data/CaldavAccount.java @@ -61,6 +61,9 @@ public class CaldavAccount implements Parcelable { @ColumnInfo(name = "cda_account_type") private int accountType; + @ColumnInfo(name = "cda_collapsed") + private boolean collapsed; + public CaldavAccount() {} @Ignore @@ -75,6 +78,7 @@ public class CaldavAccount implements Parcelable { suppressRepeatingTasks = ParcelCompat.readBoolean(source); accountType = source.readInt(); encryptionKey = source.readString(); + collapsed = ParcelCompat.readBoolean(source); } public long getId() { @@ -173,6 +177,14 @@ public class CaldavAccount implements Parcelable { return accountType == TYPE_ETESYNC; } + public boolean isCollapsed() { + return collapsed; + } + + public void setCollapsed(boolean collapsed) { + this.collapsed = collapsed; + } + @Override public String toString() { return "CaldavAccount{" @@ -203,6 +215,8 @@ public class CaldavAccount implements Parcelable { + '\'' + ", accountType=" + accountType + + ", collapsed=" + + collapsed + '}'; } @@ -226,6 +240,9 @@ public class CaldavAccount implements Parcelable { if (accountType != that.accountType) { return false; } + if (collapsed != that.collapsed) { + return false; + } if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) { return false; } @@ -261,6 +278,7 @@ public class CaldavAccount implements Parcelable { result = 31 * result + (suppressRepeatingTasks ? 1 : 0); result = 31 * result + (encryptionKey != null ? encryptionKey.hashCode() : 0); result = 31 * result + accountType; + result = 31 * result + (collapsed ? 1 : 0); return result; } @@ -281,5 +299,6 @@ public class CaldavAccount implements Parcelable { ParcelCompat.writeBoolean(dest, suppressRepeatingTasks); dest.writeInt(accountType); dest.writeString(encryptionKey); + ParcelCompat.writeBoolean(dest, collapsed); } } diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java index 0c297d419..ad8acc362 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.java +++ b/app/src/main/java/org/tasks/data/CaldavDao.java @@ -30,6 +30,9 @@ public abstract class CaldavDao { @Query("SELECT * FROM caldav_accounts ORDER BY UPPER(cda_name) ASC") public abstract List getAccounts(); + @Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id") + public abstract void setCollapsed(long id, boolean collapsed); + @Insert public abstract long insert(CaldavAccount caldavAccount); diff --git a/app/src/main/java/org/tasks/data/GoogleTaskAccount.java b/app/src/main/java/org/tasks/data/GoogleTaskAccount.java index 6d6041ebb..fa6bdbaca 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskAccount.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskAccount.java @@ -2,6 +2,7 @@ package org.tasks.data; import android.os.Parcel; import android.os.Parcelable; +import androidx.core.os.ParcelCompat; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.Ignore; @@ -35,6 +36,9 @@ public class GoogleTaskAccount implements Parcelable { @ColumnInfo(name = "gta_etag") private String etag; + @ColumnInfo(name = "gta_collapsed") + private boolean collapsed; + public GoogleTaskAccount() {} @Ignore @@ -43,6 +47,7 @@ public class GoogleTaskAccount implements Parcelable { account = source.readString(); error = source.readString(); etag = source.readString(); + collapsed = ParcelCompat.readBoolean(source); } @Ignore @@ -82,12 +87,20 @@ public class GoogleTaskAccount implements Parcelable { this.etag = etag; } + public boolean isCollapsed() { + return collapsed; + } + + public void setCollapsed(boolean collapsed) { + this.collapsed = collapsed; + } + @Override public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof GoogleTaskAccount)) { return false; } @@ -96,6 +109,9 @@ public class GoogleTaskAccount implements Parcelable { if (id != that.id) { return false; } + if (collapsed != that.collapsed) { + return false; + } if (account != null ? !account.equals(that.account) : that.account != null) { return false; } @@ -111,6 +127,7 @@ public class GoogleTaskAccount implements Parcelable { result = 31 * result + (account != null ? account.hashCode() : 0); result = 31 * result + (error != null ? error.hashCode() : 0); result = 31 * result + (etag != null ? etag.hashCode() : 0); + result = 31 * result + (collapsed ? 1 : 0); return result; } @@ -128,6 +145,8 @@ public class GoogleTaskAccount implements Parcelable { + ", etag='" + etag + '\'' + + ", collapsed=" + + collapsed + '}'; } @@ -142,5 +161,6 @@ public class GoogleTaskAccount implements Parcelable { dest.writeString(account); dest.writeString(error); dest.writeString(etag); + ParcelCompat.writeBoolean(dest, collapsed); } } diff --git a/app/src/main/java/org/tasks/data/GoogleTaskDao.java b/app/src/main/java/org/tasks/data/GoogleTaskDao.java index e4bf9421c..9c349af26 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskDao.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskDao.java @@ -67,6 +67,9 @@ public abstract class GoogleTaskDao { update(task); } + @Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id") + public abstract void setCollapsed(long id, boolean collapsed); + @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1") public abstract GoogleTask getByTaskId(long taskId); diff --git a/app/src/main/java/org/tasks/db/Migrations.java b/app/src/main/java/org/tasks/db/Migrations.java index 9abc5abe8..30f525ea0 100644 --- a/app/src/main/java/org/tasks/db/Migrations.java +++ b/app/src/main/java/org/tasks/db/Migrations.java @@ -391,6 +391,17 @@ public class Migrations { } }; + private static final Migration MIGRATION_71_72 = + new Migration(71, 72) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL( + "ALTER TABLE `caldav_accounts` ADD COLUMN `cda_collapsed` INTEGER NOT NULL DEFAULT 0"); + database.execSQL( + "ALTER TABLE `google_task_accounts` ADD COLUMN `gta_collapsed` INTEGER NOT NULL DEFAULT 0"); + } + }; + public static final Migration[] MIGRATIONS = new Migration[] { MIGRATION_35_36, @@ -419,7 +430,8 @@ public class Migrations { MIGRATION_67_68, MIGRATION_68_69, MIGRATION_69_70, - MIGRATION_70_71 + MIGRATION_70_71, + MIGRATION_71_72 }; private static Migration NOOP(int from, int to) { diff --git a/app/src/main/java/org/tasks/filters/FilterProvider.java b/app/src/main/java/org/tasks/filters/FilterProvider.java index e492cb806..ceee2f240 100644 --- a/app/src/main/java/org/tasks/filters/FilterProvider.java +++ b/app/src/main/java/org/tasks/filters/FilterProvider.java @@ -21,12 +21,14 @@ import com.todoroo.astrid.gtasks.GtasksFilterExposer; import com.todoroo.astrid.tags.TagFilterExposer; import com.todoroo.astrid.timers.TimerFilterExposer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.inject.Inject; import org.tasks.BuildConfig; +import org.tasks.Function; import org.tasks.R; import org.tasks.activities.GoogleTaskListSettingsActivity; import org.tasks.activities.TagSettingsActivity; @@ -36,9 +38,11 @@ import org.tasks.caldav.CaldavFilterExposer; import org.tasks.data.CaldavAccount; import org.tasks.data.GoogleTaskAccount; import org.tasks.etesync.EteSyncCalendarSettingsActivity; +import org.tasks.filters.NavigationDrawerSubheader.SubheaderType; import org.tasks.injection.ForApplication; import org.tasks.preferences.HelpAndFeedback; import org.tasks.preferences.MainPreferences; +import org.tasks.preferences.Preferences; import org.tasks.ui.NavigationDrawerFragment; public class FilterProvider { @@ -51,6 +55,7 @@ public class FilterProvider { private final TagFilterExposer tagFilterExposer; private final GtasksFilterExposer gtasksFilterExposer; private final CaldavFilterExposer caldavFilterExposer; + private final Preferences preferences; @Inject public FilterProvider( @@ -61,7 +66,8 @@ public class FilterProvider { CustomFilterExposer customFilterExposer, TagFilterExposer tagFilterExposer, GtasksFilterExposer gtasksFilterExposer, - CaldavFilterExposer caldavFilterExposer) { + CaldavFilterExposer caldavFilterExposer, + Preferences preferences) { this.context = context; this.inventory = inventory; this.builtInFilterExposer = builtInFilterExposer; @@ -70,6 +76,7 @@ public class FilterProvider { this.tagFilterExposer = tagFilterExposer; this.gtasksFilterExposer = gtasksFilterExposer; this.caldavFilterExposer = caldavFilterExposer; + this.preferences = preferences; } public List getRemoteListPickerItems() { @@ -85,14 +92,26 @@ public class FilterProvider { GoogleTaskAccount account = filters.getKey(); items.addAll( getSubmenu( - account.getAccount(), !isNullOrEmpty(account.getError()), filters.getValue(), true)); + account.getAccount(), + !isNullOrEmpty(account.getError()), + account.isCollapsed() ? Collections.emptyList() : filters.getValue(), + true, + account.isCollapsed(), + SubheaderType.GOOGLE_TASKS, + account.getId())); } for (Map.Entry> filters : getCaldavFilters()) { CaldavAccount account = filters.getKey(); items.addAll( getSubmenu( - account.getName(), !isNullOrEmpty(account.getError()), filters.getValue(), true)); + account.getName(), + !isNullOrEmpty(account.getError()), + account.isCollapsed() ? Collections.emptyList() : filters.getValue(), + true, + account.isCollapsed(), + SubheaderType.CALDAV, + account.getId())); } return items; @@ -105,9 +124,9 @@ public class FilterProvider { items.add(builtInFilterExposer.getMyTasksFilter()); - items.addAll(getSubmenu(R.string.filters, getFilters())); + items.addAll(getSubmenu(R.string.filters, R.string.p_collapse_filters, this::getFilters)); - if (navigationDrawer) { + if (navigationDrawer && !preferences.getBoolean(R.string.p_collapse_filters, false)) { items.add( new NavigationDrawerAction( context.getString(R.string.FLA_new_filter), @@ -116,9 +135,10 @@ public class FilterProvider { NavigationDrawerFragment.REQUEST_NEW_LIST)); } - items.addAll(getSubmenu(R.string.tags, tagFilterExposer.getFilters())); + items.addAll( + getSubmenu(R.string.tags, R.string.p_collapse_tags, tagFilterExposer::getFilters)); - if (navigationDrawer) { + if (navigationDrawer && !preferences.getBoolean(R.string.p_collapse_tags, false)) { items.add( new NavigationDrawerAction( context.getString(R.string.new_tag), @@ -133,10 +153,13 @@ public class FilterProvider { getSubmenu( account.getAccount(), !isNullOrEmpty(account.getError()), - filters.getValue(), - !navigationDrawer)); + account.isCollapsed() ? Collections.emptyList() : filters.getValue(), + !navigationDrawer, + account.isCollapsed(), + SubheaderType.GOOGLE_TASKS, + account.getId())); - if (navigationDrawer) { + if (navigationDrawer && !account.isCollapsed()) { items.add( new NavigationDrawerAction( context.getString(R.string.new_list), @@ -153,10 +176,13 @@ public class FilterProvider { getSubmenu( account.getName(), !isNullOrEmpty(account.getError()), - filters.getValue(), - !navigationDrawer)); + account.isCollapsed() ? Collections.emptyList() : filters.getValue(), + !navigationDrawer, + account.isCollapsed(), + SubheaderType.CALDAV, + account.getId())); - if (navigationDrawer) { + if (navigationDrawer && !account.isCollapsed()) { items.add( new NavigationDrawerAction( context.getString(R.string.new_list), @@ -223,15 +249,29 @@ public class FilterProvider { return caldavFilterExposer.getFilters().entrySet(); } - private List getSubmenu(int title, List filters) { - return getSubmenu(context.getString(title), false, filters, false); + private List getSubmenu(int title, int prefId, Function> getFilters) { + boolean collapsed = preferences.getBoolean(prefId, false); + return newArrayList( + concat( + ImmutableList.of( + new NavigationDrawerSubheader( + context.getString(title), false, collapsed, SubheaderType.PREFERENCE, prefId)), + collapsed ? Collections.emptyList() : getFilters.call())); } private List getSubmenu( - String title, boolean error, List filters, boolean hideIfEmpty) { - return hideIfEmpty && filters.isEmpty() + String title, + boolean error, + List filters, + boolean hideIfEmpty, + boolean collapsed, + SubheaderType type, + long id) { + return hideIfEmpty && filters.isEmpty() && !collapsed ? ImmutableList.of() : newArrayList( - concat(ImmutableList.of(new NavigationDrawerSubheader(title, error)), filters)); + concat( + ImmutableList.of(new NavigationDrawerSubheader(title, error, collapsed, type, id)), + filters)); } } diff --git a/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java b/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java index 20371ac13..bfac30b15 100644 --- a/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java +++ b/app/src/main/java/org/tasks/filters/NavigationDrawerSubheader.java @@ -3,6 +3,7 @@ package org.tasks.filters; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; +import androidx.core.os.ParcelCompat; import com.todoroo.astrid.api.FilterListItem; public class NavigationDrawerSubheader extends FilterListItem { @@ -24,40 +25,104 @@ public class NavigationDrawerSubheader extends FilterListItem { return new NavigationDrawerSubheader[size]; } }; - public boolean error; + private boolean collapsed; + private SubheaderType subheaderType; + private long id; private NavigationDrawerSubheader() {} - public NavigationDrawerSubheader(String listingTitle, boolean error) { + public NavigationDrawerSubheader( + String listingTitle, boolean error, boolean collapsed, SubheaderType subheaderType, long id) { this.error = error; + this.collapsed = collapsed; + this.subheaderType = subheaderType; + this.id = id; this.listingTitle = listingTitle; } + public long getId() { + return id; + } + + public boolean isCollapsed() { + return collapsed; + } + + public SubheaderType getSubheaderType() { + return subheaderType; + } + @Override protected void readFromParcel(Parcel source) { super.readFromParcel(source); - error = source.readInt() == 1; + error = ParcelCompat.readBoolean(source); + collapsed = ParcelCompat.readBoolean(source); + subheaderType = (SubheaderType) source.readSerializable(); + id = source.readLong(); } @Override public boolean areItemsTheSame(@NonNull FilterListItem other) { - return other instanceof NavigationDrawerSubheader && listingTitle.equals(other.listingTitle); + return other instanceof NavigationDrawerSubheader + && subheaderType == ((NavigationDrawerSubheader) other).getSubheaderType() + && id == ((NavigationDrawerSubheader) other).getId(); } @Override public boolean areContentsTheSame(@NonNull FilterListItem other) { - return super.areContentsTheSame(other) && error == ((NavigationDrawerSubheader) other).error; + return this.equals(other); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof NavigationDrawerSubheader)) { + return false; + } + + NavigationDrawerSubheader that = (NavigationDrawerSubheader) o; + + if (error != that.error) { + return false; + } + if (collapsed != that.collapsed) { + return false; + } + if (id != that.id) { + return false; + } + return subheaderType == that.subheaderType; + } + + @Override + public int hashCode() { + int result = (error ? 1 : 0); + result = 31 * result + (collapsed ? 1 : 0); + result = 31 * result + (subheaderType != null ? subheaderType.hashCode() : 0); + result = 31 * result + (int) (id ^ (id >>> 32)); + return result; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeInt(error ? 1 : 0); + ParcelCompat.writeBoolean(dest, error); + ParcelCompat.writeBoolean(dest, collapsed); + dest.writeSerializable(subheaderType); + dest.writeLong(id); } @Override public Type getItemType() { return Type.SUBHEADER; } + + public enum SubheaderType { + PREFERENCE, + GOOGLE_TASKS, + CALDAV + } } diff --git a/app/src/main/java/org/tasks/themes/DrawableUtil.java b/app/src/main/java/org/tasks/themes/DrawableUtil.java index f8a950e96..be1a71b5a 100644 --- a/app/src/main/java/org/tasks/themes/DrawableUtil.java +++ b/app/src/main/java/org/tasks/themes/DrawableUtil.java @@ -25,6 +25,15 @@ public class DrawableUtil { } } + public static void setRightDrawable(Context context, TextView tv, @DrawableRes int resId) { + Drawable wrapped = getWrapped(context, resId); + if (atLeastJellybeanMR1()) { + tv.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, wrapped, null); + } else { + tv.setCompoundDrawablesWithIntrinsicBounds(null, null, wrapped, null); + } + } + public static Drawable getLeftDrawable(TextView tv) { return atLeastJellybeanMR1() ? tv.getCompoundDrawablesRelative()[0] diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_down_black_18dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_down_black_18dp.xml new file mode 100644 index 000000000..114731718 --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_down_black_18dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_up_black_18dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_18dp.xml new file mode 100644 index 000000000..37f5a18aa --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_18dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/filter_adapter_row.xml b/app/src/main/res/layout/filter_adapter_row.xml index ce253d8ac..575a9f963 100644 --- a/app/src/main/res/layout/filter_adapter_row.xml +++ b/app/src/main/res/layout/filter_adapter_row.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/row" android:background="@drawable/drawer_background_selector" + android:foreground="?attr/selectableItemBackground" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingTop="12dp" diff --git a/app/src/main/res/layout/filter_adapter_separator.xml b/app/src/main/res/layout/filter_adapter_separator.xml index dce792159..d159998e0 100644 --- a/app/src/main/res/layout/filter_adapter_separator.xml +++ b/app/src/main/res/layout/filter_adapter_separator.xml @@ -2,12 +2,14 @@ - + diff --git a/app/src/main/res/layout/filter_adapter_subheader.xml b/app/src/main/res/layout/filter_adapter_subheader.xml index 3585e7a09..68946990b 100644 --- a/app/src/main/res/layout/filter_adapter_subheader.xml +++ b/app/src/main/res/layout/filter_adapter_subheader.xml @@ -2,58 +2,62 @@ - + - + - + \ No newline at end of file diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 1f8c4e08b..3374d2cfe 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -291,4 +291,6 @@ chip_style chip_appearance desaturate_colors + collapse_filters + collapse_tags