@ -0,0 +1,832 @@ | |||
{ | |||
"formatVersion": 1, | |||
"database": { | |||
"version": 4, | |||
"identityHash": "334fa594d108afe37ea079b4e2081e38", | |||
"entities": [ | |||
{ | |||
"tableName": "identity", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `email` TEXT NOT NULL, `replyto` TEXT, `account` INTEGER NOT NULL, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `starttls` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `primary` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `store_sent` INTEGER NOT NULL, `state` TEXT, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "name", | |||
"columnName": "name", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "email", | |||
"columnName": "email", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "replyto", | |||
"columnName": "replyto", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "account", | |||
"columnName": "account", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "host", | |||
"columnName": "host", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "port", | |||
"columnName": "port", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "starttls", | |||
"columnName": "starttls", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "user", | |||
"columnName": "user", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "password", | |||
"columnName": "password", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "primary", | |||
"columnName": "primary", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "synchronize", | |||
"columnName": "synchronize", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "store_sent", | |||
"columnName": "store_sent", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "state", | |||
"columnName": "state", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "error", | |||
"columnName": "error", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [ | |||
{ | |||
"name": "index_identity_account", | |||
"unique": false, | |||
"columnNames": [ | |||
"account" | |||
], | |||
"createSql": "CREATE INDEX `index_identity_account` ON `${TABLE_NAME}` (`account`)" | |||
} | |||
], | |||
"foreignKeys": [ | |||
{ | |||
"table": "account", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"account" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
} | |||
] | |||
}, | |||
{ | |||
"tableName": "account", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `primary` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `store_sent` INTEGER NOT NULL, `poll_interval` INTEGER NOT NULL, `seen_until` INTEGER, `state` TEXT, `error` TEXT)", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "name", | |||
"columnName": "name", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "host", | |||
"columnName": "host", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "port", | |||
"columnName": "port", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "user", | |||
"columnName": "user", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "password", | |||
"columnName": "password", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "primary", | |||
"columnName": "primary", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "synchronize", | |||
"columnName": "synchronize", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "store_sent", | |||
"columnName": "store_sent", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "poll_interval", | |||
"columnName": "poll_interval", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "seen_until", | |||
"columnName": "seen_until", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "state", | |||
"columnName": "state", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "error", | |||
"columnName": "error", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [], | |||
"foreignKeys": [] | |||
}, | |||
{ | |||
"tableName": "folder", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `synchronize` INTEGER NOT NULL, `after` INTEGER NOT NULL, `state` TEXT, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "account", | |||
"columnName": "account", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "name", | |||
"columnName": "name", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "type", | |||
"columnName": "type", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "synchronize", | |||
"columnName": "synchronize", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "after", | |||
"columnName": "after", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "state", | |||
"columnName": "state", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "error", | |||
"columnName": "error", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [ | |||
{ | |||
"name": "index_folder_account_name", | |||
"unique": true, | |||
"columnNames": [ | |||
"account", | |||
"name" | |||
], | |||
"createSql": "CREATE UNIQUE INDEX `index_folder_account_name` ON `${TABLE_NAME}` (`account`, `name`)" | |||
}, | |||
{ | |||
"name": "index_folder_account", | |||
"unique": false, | |||
"columnNames": [ | |||
"account" | |||
], | |||
"createSql": "CREATE INDEX `index_folder_account` ON `${TABLE_NAME}` (`account`)" | |||
}, | |||
{ | |||
"name": "index_folder_name", | |||
"unique": false, | |||
"columnNames": [ | |||
"name" | |||
], | |||
"createSql": "CREATE INDEX `index_folder_name` ON `${TABLE_NAME}` (`name`)" | |||
}, | |||
{ | |||
"name": "index_folder_type", | |||
"unique": false, | |||
"columnNames": [ | |||
"type" | |||
], | |||
"createSql": "CREATE INDEX `index_folder_type` ON `${TABLE_NAME}` (`type`)" | |||
} | |||
], | |||
"foreignKeys": [ | |||
{ | |||
"table": "account", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"account" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
} | |||
] | |||
}, | |||
{ | |||
"tableName": "message", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER, `folder` INTEGER NOT NULL, `identity` INTEGER, `replying` INTEGER, `uid` INTEGER, `msgid` TEXT, `references` TEXT, `inreplyto` TEXT, `thread` TEXT, `from` TEXT, `to` TEXT, `cc` TEXT, `bcc` TEXT, `reply` TEXT, `subject` TEXT, `sent` INTEGER, `received` INTEGER NOT NULL, `stored` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `ui_seen` INTEGER NOT NULL, `ui_hide` INTEGER NOT NULL, `error` TEXT, FOREIGN KEY(`account`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`identity`) REFERENCES `identity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`replying`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "account", | |||
"columnName": "account", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "folder", | |||
"columnName": "folder", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "identity", | |||
"columnName": "identity", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "replying", | |||
"columnName": "replying", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "uid", | |||
"columnName": "uid", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "msgid", | |||
"columnName": "msgid", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "references", | |||
"columnName": "references", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "inreplyto", | |||
"columnName": "inreplyto", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "thread", | |||
"columnName": "thread", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "from", | |||
"columnName": "from", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "to", | |||
"columnName": "to", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "cc", | |||
"columnName": "cc", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "bcc", | |||
"columnName": "bcc", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "reply", | |||
"columnName": "reply", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "subject", | |||
"columnName": "subject", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "sent", | |||
"columnName": "sent", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "received", | |||
"columnName": "received", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "stored", | |||
"columnName": "stored", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "seen", | |||
"columnName": "seen", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "ui_seen", | |||
"columnName": "ui_seen", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "ui_hide", | |||
"columnName": "ui_hide", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "error", | |||
"columnName": "error", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [ | |||
{ | |||
"name": "index_message_account", | |||
"unique": false, | |||
"columnNames": [ | |||
"account" | |||
], | |||
"createSql": "CREATE INDEX `index_message_account` ON `${TABLE_NAME}` (`account`)" | |||
}, | |||
{ | |||
"name": "index_message_folder", | |||
"unique": false, | |||
"columnNames": [ | |||
"folder" | |||
], | |||
"createSql": "CREATE INDEX `index_message_folder` ON `${TABLE_NAME}` (`folder`)" | |||
}, | |||
{ | |||
"name": "index_message_identity", | |||
"unique": false, | |||
"columnNames": [ | |||
"identity" | |||
], | |||
"createSql": "CREATE INDEX `index_message_identity` ON `${TABLE_NAME}` (`identity`)" | |||
}, | |||
{ | |||
"name": "index_message_replying", | |||
"unique": false, | |||
"columnNames": [ | |||
"replying" | |||
], | |||
"createSql": "CREATE INDEX `index_message_replying` ON `${TABLE_NAME}` (`replying`)" | |||
}, | |||
{ | |||
"name": "index_message_folder_uid", | |||
"unique": true, | |||
"columnNames": [ | |||
"folder", | |||
"uid" | |||
], | |||
"createSql": "CREATE UNIQUE INDEX `index_message_folder_uid` ON `${TABLE_NAME}` (`folder`, `uid`)" | |||
}, | |||
{ | |||
"name": "index_message_msgid_folder", | |||
"unique": true, | |||
"columnNames": [ | |||
"msgid", | |||
"folder" | |||
], | |||
"createSql": "CREATE UNIQUE INDEX `index_message_msgid_folder` ON `${TABLE_NAME}` (`msgid`, `folder`)" | |||
}, | |||
{ | |||
"name": "index_message_thread", | |||
"unique": false, | |||
"columnNames": [ | |||
"thread" | |||
], | |||
"createSql": "CREATE INDEX `index_message_thread` ON `${TABLE_NAME}` (`thread`)" | |||
}, | |||
{ | |||
"name": "index_message_received", | |||
"unique": false, | |||
"columnNames": [ | |||
"received" | |||
], | |||
"createSql": "CREATE INDEX `index_message_received` ON `${TABLE_NAME}` (`received`)" | |||
}, | |||
{ | |||
"name": "index_message_ui_seen", | |||
"unique": false, | |||
"columnNames": [ | |||
"ui_seen" | |||
], | |||
"createSql": "CREATE INDEX `index_message_ui_seen` ON `${TABLE_NAME}` (`ui_seen`)" | |||
}, | |||
{ | |||
"name": "index_message_ui_hide", | |||
"unique": false, | |||
"columnNames": [ | |||
"ui_hide" | |||
], | |||
"createSql": "CREATE INDEX `index_message_ui_hide` ON `${TABLE_NAME}` (`ui_hide`)" | |||
} | |||
], | |||
"foreignKeys": [ | |||
{ | |||
"table": "account", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"account" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
}, | |||
{ | |||
"table": "folder", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"folder" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
}, | |||
{ | |||
"table": "identity", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"identity" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
}, | |||
{ | |||
"table": "message", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"replying" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
} | |||
] | |||
}, | |||
{ | |||
"tableName": "attachment", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` INTEGER NOT NULL, `sequence` INTEGER NOT NULL, `name` TEXT, `type` TEXT NOT NULL, `size` INTEGER, `progress` INTEGER, `available` INTEGER NOT NULL, FOREIGN KEY(`message`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "message", | |||
"columnName": "message", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "sequence", | |||
"columnName": "sequence", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "name", | |||
"columnName": "name", | |||
"affinity": "TEXT", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "type", | |||
"columnName": "type", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "size", | |||
"columnName": "size", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "progress", | |||
"columnName": "progress", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "available", | |||
"columnName": "available", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [ | |||
{ | |||
"name": "index_attachment_message", | |||
"unique": false, | |||
"columnNames": [ | |||
"message" | |||
], | |||
"createSql": "CREATE INDEX `index_attachment_message` ON `${TABLE_NAME}` (`message`)" | |||
}, | |||
{ | |||
"name": "index_attachment_message_sequence", | |||
"unique": true, | |||
"columnNames": [ | |||
"message", | |||
"sequence" | |||
], | |||
"createSql": "CREATE UNIQUE INDEX `index_attachment_message_sequence` ON `${TABLE_NAME}` (`message`, `sequence`)" | |||
} | |||
], | |||
"foreignKeys": [ | |||
{ | |||
"table": "message", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"message" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
} | |||
] | |||
}, | |||
{ | |||
"tableName": "operation", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `folder` INTEGER NOT NULL, `message` INTEGER NOT NULL, `name` TEXT NOT NULL, `args` TEXT NOT NULL, `created` INTEGER NOT NULL, FOREIGN KEY(`folder`) REFERENCES `folder`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`message`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "folder", | |||
"columnName": "folder", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "message", | |||
"columnName": "message", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "name", | |||
"columnName": "name", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "args", | |||
"columnName": "args", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "created", | |||
"columnName": "created", | |||
"affinity": "INTEGER", | |||
"notNull": true | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [ | |||
{ | |||
"name": "index_operation_folder", | |||
"unique": false, | |||
"columnNames": [ | |||
"folder" | |||
], | |||
"createSql": "CREATE INDEX `index_operation_folder` ON `${TABLE_NAME}` (`folder`)" | |||
}, | |||
{ | |||
"name": "index_operation_message", | |||
"unique": false, | |||
"columnNames": [ | |||
"message" | |||
], | |||
"createSql": "CREATE INDEX `index_operation_message` ON `${TABLE_NAME}` (`message`)" | |||
} | |||
], | |||
"foreignKeys": [ | |||
{ | |||
"table": "folder", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"folder" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
}, | |||
{ | |||
"table": "message", | |||
"onDelete": "CASCADE", | |||
"onUpdate": "NO ACTION", | |||
"columns": [ | |||
"message" | |||
], | |||
"referencedColumns": [ | |||
"id" | |||
] | |||
} | |||
] | |||
}, | |||
{ | |||
"tableName": "answer", | |||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `text` TEXT NOT NULL)", | |||
"fields": [ | |||
{ | |||
"fieldPath": "id", | |||
"columnName": "id", | |||
"affinity": "INTEGER", | |||
"notNull": false | |||
}, | |||
{ | |||
"fieldPath": "name", | |||
"columnName": "name", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
}, | |||
{ | |||
"fieldPath": "text", | |||
"columnName": "text", | |||
"affinity": "TEXT", | |||
"notNull": true | |||
} | |||
], | |||
"primaryKey": { | |||
"columnNames": [ | |||
"id" | |||
], | |||
"autoGenerate": true | |||
}, | |||
"indices": [], | |||
"foreignKeys": [] | |||
} | |||
], | |||
"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, \"334fa594d108afe37ea079b4e2081e38\")" | |||
] | |||
} | |||
} |
@ -0,0 +1,168 @@ | |||
package eu.faircode.email; | |||
/* | |||
This file is part of FairEmail. | |||
FairEmail is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
NetGuard is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. | |||
Copyright 2018 by Marcel Bokhorst (M66B) | |||
*/ | |||
import android.content.Context; | |||
import android.util.Log; | |||
import android.view.LayoutInflater; | |||
import android.view.View; | |||
import android.view.ViewGroup; | |||
import android.widget.TextView; | |||
import java.text.Collator; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import androidx.annotation.NonNull; | |||
import androidx.recyclerview.widget.DiffUtil; | |||
import androidx.recyclerview.widget.ListUpdateCallback; | |||
import androidx.recyclerview.widget.RecyclerView; | |||
public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder> { | |||
private Context context; | |||
private List<EntityAnswer> all = new ArrayList<>(); | |||
private List<EntityAnswer> filtered = new ArrayList<>(); | |||
public class ViewHolder extends RecyclerView.ViewHolder { | |||
View itemView; | |||
TextView tvName; | |||
ViewHolder(View itemView) { | |||
super(itemView); | |||
this.itemView = itemView; | |||
tvName = itemView.findViewById(R.id.tvName); | |||
} | |||
private void bindTo(EntityAnswer answer) { | |||
tvName.setText(answer.name); | |||
} | |||
} | |||
AdapterAnswer(Context context) { | |||
this.context = context; | |||
setHasStableIds(true); | |||
} | |||
public void set(@NonNull List<EntityAnswer> answers) { | |||
Log.i(Helper.TAG, "Set answers=" + answers.size()); | |||
final Collator collator = Collator.getInstance(Locale.getDefault()); | |||
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc | |||
Collections.sort(answers, new Comparator<EntityAnswer>() { | |||
@Override | |||
public int compare(EntityAnswer a1, EntityAnswer a2) { | |||
return collator.compare(a1.name, a2.name); | |||
} | |||
}); | |||
all.clear(); | |||
all.addAll(answers); | |||
DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new MessageDiffCallback(filtered, all)); | |||
filtered.clear(); | |||
filtered.addAll(all); | |||
diff.dispatchUpdatesTo(new ListUpdateCallback() { | |||
@Override | |||
public void onInserted(int position, int count) { | |||
Log.i(Helper.TAG, "Inserted @" + position + " #" + count); | |||
} | |||
@Override | |||
public void onRemoved(int position, int count) { | |||
Log.i(Helper.TAG, "Removed @" + position + " #" + count); | |||
} | |||
@Override | |||
public void onMoved(int fromPosition, int toPosition) { | |||
Log.i(Helper.TAG, "Moved " + fromPosition + ">" + toPosition); | |||
} | |||
@Override | |||
public void onChanged(int position, int count, Object payload) { | |||
Log.i(Helper.TAG, "Changed @" + position + " #" + count); | |||
} | |||
}); | |||
diff.dispatchUpdatesTo(this); | |||
} | |||
private class MessageDiffCallback extends DiffUtil.Callback { | |||
private List<EntityAnswer> prev; | |||
private List<EntityAnswer> next; | |||
MessageDiffCallback(List<EntityAnswer> prev, List<EntityAnswer> next) { | |||
this.prev = prev; | |||
this.next = next; | |||
} | |||
@Override | |||
public int getOldListSize() { | |||
return prev.size(); | |||
} | |||
@Override | |||
public int getNewListSize() { | |||
return next.size(); | |||
} | |||
@Override | |||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { | |||
EntityAnswer a1 = prev.get(oldItemPosition); | |||
EntityAnswer a2 = next.get(newItemPosition); | |||
return a1.id.equals(a2.id); | |||
} | |||
@Override | |||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { | |||
EntityAnswer a1 = prev.get(oldItemPosition); | |||
EntityAnswer a2 = next.get(newItemPosition); | |||
return a1.equals(a2); | |||
} | |||
} | |||
@Override | |||
public long getItemId(int position) { | |||
return filtered.get(position).id; | |||
} | |||
@Override | |||
public int getItemCount() { | |||
return filtered.size(); | |||
} | |||
@Override | |||
@NonNull | |||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | |||
return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_folder, parent, false)); | |||
} | |||
@Override | |||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { | |||
EntityAnswer answer = filtered.get(position); | |||
holder.bindTo(answer); | |||
} | |||
} |
@ -0,0 +1,46 @@ | |||
package eu.faircode.email; | |||
/* | |||
This file is part of FairEmail. | |||
FairEmail is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
NetGuard is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. | |||
Copyright 2018 by Marcel Bokhorst (M66B) | |||
*/ | |||
import java.util.List; | |||
import androidx.lifecycle.LiveData; | |||
import androidx.room.Dao; | |||
import androidx.room.Insert; | |||
import androidx.room.Query; | |||
import androidx.room.Update; | |||
@Dao | |||
public interface DaoAnswer { | |||
@Query("SELECT * FROM answer") | |||
LiveData<List<EntityAnswer>> liveAnswers(); | |||
@Query("SELECT * FROM answer WHERE id = :id") | |||
LiveData<EntityAnswer> liveAnswer(long id); | |||
@Insert | |||
long insertAnswer(EntityAnswer answer); | |||
@Update | |||
int updateAnswer(EntityAnswer answer); | |||
@Query("DELETE FROM answer WHERE id = :id") | |||
void deleteAnswer(long id); | |||
} |
@ -0,0 +1,58 @@ | |||
package eu.faircode.email; | |||
/* | |||
This file is part of FairEmail. | |||
FairEmail is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
NetGuard is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. | |||
Copyright 2018 by Marcel Bokhorst (M66B) | |||
*/ | |||
import java.io.Serializable; | |||
import androidx.annotation.NonNull; | |||
import androidx.room.Entity; | |||
import androidx.room.PrimaryKey; | |||
// https://developer.android.com/training/data-storage/room/defining-data | |||
@Entity( | |||
tableName = EntityAnswer.TABLE_NAME, | |||
foreignKeys = { | |||
}, | |||
indices = { | |||
} | |||
) | |||
public class EntityAnswer implements Serializable { | |||
static final String TABLE_NAME = "answer"; | |||
@PrimaryKey(autoGenerate = true) | |||
public Long id; | |||
@NonNull | |||
public String name; | |||
@NonNull | |||
public String text; | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (obj instanceof EntityAnswer) { | |||
EntityAnswer other = (EntityAnswer) obj; | |||
return (this.name.equals(other.name) && | |||
this.text.equals(other.text) | |||
); | |||
} | |||
return false; | |||
} | |||
} |
@ -0,0 +1,75 @@ | |||
package eu.faircode.email; | |||
/* | |||
This file is part of FairEmail. | |||
FairEmail is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
NetGuard is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. | |||
Copyright 2018 by Marcel Bokhorst (M66B) | |||
*/ | |||
import android.os.Bundle; | |||
import android.view.LayoutInflater; | |||
import android.view.View; | |||
import android.view.ViewGroup; | |||
import android.widget.ProgressBar; | |||
import android.widget.TextView; | |||
import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | |||
import androidx.constraintlayout.widget.Group; | |||
import androidx.lifecycle.Observer; | |||
public class FragmentAnswer extends FragmentEx { | |||
private ViewGroup view; | |||
private TextView etName; | |||
private TextView etText; | |||
private ProgressBar pbWait; | |||
private Group grpReady; | |||
@Override | |||
@Nullable | |||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | |||
view = (ViewGroup) inflater.inflate(R.layout.fragment_answer, container, false); | |||
// Get controls | |||
etName = view.findViewById(R.id.etName); | |||
etText = view.findViewById(R.id.etText); | |||
pbWait = view.findViewById(R.id.pbWait); | |||
grpReady = view.findViewById(R.id.grpReady); | |||
grpReady.setVisibility(View.GONE); | |||
pbWait.setVisibility(View.VISIBLE); | |||
return view; | |||
} | |||
@Override | |||
public void onActivityCreated(@Nullable final Bundle savedInstanceState) { | |||
super.onActivityCreated(savedInstanceState); | |||
// Get arguments | |||
Bundle args = getArguments(); | |||
long id = (args == null ? -1 : args.getLong("id")); | |||
DB.getInstance(getContext()).answer().liveAnswer(id).observe(getViewLifecycleOwner(), new Observer<EntityAnswer>() { | |||
@Override | |||
public void onChanged(EntityAnswer entityAnswer) { | |||
pbWait.setVisibility(View.GONE); | |||
grpReady.setVisibility(View.VISIBLE); | |||
} | |||
}); | |||
} | |||
} |
@ -0,0 +1,100 @@ | |||
package eu.faircode.email; | |||
/* | |||
This file is part of FairEmail. | |||
FairEmail is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
NetGuard is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>. | |||
Copyright 2018 by Marcel Bokhorst (M66B) | |||
*/ | |||
import android.os.Bundle; | |||
import android.view.LayoutInflater; | |||
import android.view.View; | |||
import android.view.ViewGroup; | |||
import android.widget.ProgressBar; | |||
import com.google.android.material.floatingactionbutton.FloatingActionButton; | |||
import java.util.List; | |||
import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | |||
import androidx.constraintlayout.widget.Group; | |||
import androidx.fragment.app.FragmentTransaction; | |||
import androidx.lifecycle.Observer; | |||
import androidx.recyclerview.widget.LinearLayoutManager; | |||
import androidx.recyclerview.widget.RecyclerView; | |||
public class FragmentAnswers extends FragmentEx { | |||
private RecyclerView rvAnswer; | |||
private ProgressBar pbWait; | |||
private Group grpReady; | |||
private FloatingActionButton fab; | |||
private AdapterAnswer adapter; | |||
@Override | |||
@Nullable | |||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | |||
View view = inflater.inflate(R.layout.fragment_answers, container, false); | |||
setHasOptionsMenu(true); | |||
// Get controls | |||
rvAnswer = view.findViewById(R.id.rvAnswer); | |||
pbWait = view.findViewById(R.id.pbWait); | |||
grpReady = view.findViewById(R.id.grpReady); | |||
fab = view.findViewById(R.id.fab); | |||
// Wire controls | |||
rvAnswer.setHasFixedSize(false); | |||
LinearLayoutManager llm = new LinearLayoutManager(getContext()); | |||
rvAnswer.setLayoutManager(llm); | |||
adapter = new AdapterAnswer(getContext()); | |||
rvAnswer.setAdapter(adapter); | |||
fab.setOnClickListener(new View.OnClickListener() { | |||
@Override | |||
public void onClick(View view) { | |||
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); | |||
fragmentTransaction.replace(R.id.content_frame, new FragmentAnswer()).addToBackStack("answer"); | |||
fragmentTransaction.commit(); | |||
} | |||
}); | |||
// Initialize | |||
grpReady.setVisibility(View.GONE); | |||
pbWait.setVisibility(View.VISIBLE); | |||
return view; | |||
} | |||
@Override | |||
public void onActivityCreated(@Nullable Bundle savedInstanceState) { | |||
super.onActivityCreated(savedInstanceState); | |||
DB db = DB.getInstance(getContext()); | |||
db.answer().liveAnswers().observe(getViewLifecycleOwner(), new Observer<List<EntityAnswer>>() { | |||
@Override | |||
public void onChanged(List<EntityAnswer> answers) { | |||
adapter.set(answers); | |||
pbWait.setVisibility(View.GONE); | |||
grpReady.setVisibility(View.VISIBLE); | |||
} | |||
}); | |||
} | |||
} |
@ -0,0 +1,69 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent"> | |||
<EditText | |||
android:id="@+id/etName" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:layout_marginEnd="6dp" | |||
android:layout_marginStart="6dp" | |||
android:background="@null" | |||
android:hint="@string/title_body_hint" | |||
android:inputType="textCapSentences" | |||
android:textAppearance="@style/TextAppearance.AppCompat.Small" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<View | |||
android:id="@+id/vSeparator" | |||
android:layout_width="match_parent" | |||
android:layout_height="1dp" | |||
android:layout_marginTop="6dp" | |||
android:background="?attr/colorSeparator" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/etName" /> | |||
<ScrollView | |||
android:id="@+id/scroll" | |||
android:layout_width="match_parent" | |||
android:layout_height="0dp" | |||
android:layout_marginEnd="6dp" | |||
android:layout_marginStart="6dp" | |||
android:fillViewport="true" | |||
android:orientation="vertical" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@id/vSeparator"> | |||
<EditText | |||
android:id="@+id/etText" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content" | |||
android:background="@null" | |||
android:fontFamily="monospace" | |||
android:gravity="top" | |||
android:hint="@string/title_body_hint" | |||
android:inputType="textCapSentences|textMultiLine" | |||
android:textAppearance="@style/TextAppearance.AppCompat.Small" /> | |||
</ScrollView> | |||
<ProgressBar | |||
android:id="@+id/pbWait" | |||
style="@style/Base.Widget.AppCompat.ProgressBar" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:indeterminate="true" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<androidx.constraintlayout.widget.Group | |||
android:id="@+id/grpReady" | |||
android:layout_width="0dp" | |||
android:layout_height="0dp" | |||
app:constraint_referenced_ids="etName,vSeparator,scroll" /> | |||
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,48 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
tools:context=".ActivityView"> | |||
<androidx.recyclerview.widget.RecyclerView | |||
android:id="@+id/rvAnswer" | |||
android:layout_width="0dp" | |||
android:layout_height="0dp" | |||
android:scrollbarStyle="outsideOverlay" | |||
android:scrollbars="vertical" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<ProgressBar | |||
android:id="@+id/pbWait" | |||
style="@style/Base.Widget.AppCompat.ProgressBar" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:indeterminate="true" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<com.google.android.material.floatingactionbutton.FloatingActionButton | |||
android:id="@+id/fab" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_gravity="end|bottom" | |||
android:layout_margin="16dp" | |||
android:src="@drawable/baseline_add_24" | |||
android:tint="@color/colorActionForeground" | |||
app:backgroundTint="?attr/colorAccent" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" /> | |||
<androidx.constraintlayout.widget.Group | |||
android:id="@+id/grpReady" | |||
android:layout_width="0dp" | |||
android:layout_height="0dp" | |||
app:constraint_referenced_ids="rvAnswer" /> | |||
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,20 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||
android:layout_width="match_parent" | |||
android:layout_height="wrap_content"> | |||
<TextView | |||
android:id="@+id/tvName" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:layout_marginEnd="6dp" | |||
android:layout_marginStart="6dp" | |||
android:ellipsize="end" | |||
android:maxLines="1" | |||
android:text="Name" | |||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" | |||
app:layout_constraintEnd_toStartOf="@+id/tvSize" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
</androidx.constraintlayout.widget.ConstraintLayout> |