@ -0,0 +1,850 @@ | |||||
{ | |||||
"formatVersion": 1, | |||||
"database": { | |||||
"version": 6, | |||||
"identityHash": "e9ae946be1049502f01f8f30275abc2f", | |||||
"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, `auth_type` INTEGER 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": "auth_type", | |||||
"columnName": "auth_type", | |||||
"affinity": "INTEGER", | |||||
"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, `auth_type` INTEGER 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": "auth_type", | |||||
"columnName": "auth_type", | |||||
"affinity": "INTEGER", | |||||
"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, `ui_found` 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": "ui_found", | |||||
"columnName": "ui_found", | |||||
"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, \"e9ae946be1049502f01f8f30275abc2f\")" | |||||
] | |||||
} | |||||
} |
@ -1,241 +0,0 @@ | |||||
package eu.faircode.email; | |||||
import android.content.Context; | |||||
import android.os.Handler; | |||||
import android.os.Looper; | |||||
import android.text.TextUtils; | |||||
import android.util.Log; | |||||
import android.util.SparseArray; | |||||
import android.widget.Toast; | |||||
import com.sun.mail.imap.IMAPFolder; | |||||
import com.sun.mail.imap.IMAPStore; | |||||
import java.io.IOException; | |||||
import java.io.UnsupportedEncodingException; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Properties; | |||||
import javax.mail.FetchProfile; | |||||
import javax.mail.Folder; | |||||
import javax.mail.Message; | |||||
import javax.mail.MessagingException; | |||||
import javax.mail.Session; | |||||
import javax.mail.UIDFolder; | |||||
import javax.mail.internet.MimeMessage; | |||||
import javax.mail.search.BodyTerm; | |||||
import javax.mail.search.FromStringTerm; | |||||
import javax.mail.search.OrTerm; | |||||
import javax.mail.search.SubjectTerm; | |||||
import androidx.lifecycle.Lifecycle; | |||||
import androidx.lifecycle.LifecycleObserver; | |||||
import androidx.lifecycle.LifecycleOwner; | |||||
import androidx.lifecycle.OnLifecycleEvent; | |||||
import androidx.paging.PositionalDataSource; | |||||
public class SearchDataSource extends PositionalDataSource<TupleMessageEx> implements LifecycleObserver { | |||||
private Context context; | |||||
private LifecycleOwner owner; | |||||
private long fid; | |||||
private String search; | |||||
private EntityFolder folder; | |||||
private EntityAccount account; | |||||
private IMAPStore istore = null; | |||||
private IMAPFolder ifolder; | |||||
private Message[] imessages; | |||||
private SparseArray<TupleMessageEx> cache = new SparseArray<>(); | |||||
private static final float MAX_MEMORY_USAGE = 0.6f; // percent | |||||
SearchDataSource(Context context, LifecycleOwner owner, long folder, String search) { | |||||
Log.i(Helper.TAG, "SDS create"); | |||||
this.context = context; | |||||
this.owner = owner; | |||||
this.fid = folder; | |||||
this.search = search; | |||||
owner.getLifecycle().addObserver(this); | |||||
} | |||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) | |||||
public void onDestroyed() { | |||||
Log.i(Helper.TAG, "SDS destroy"); | |||||
new Thread(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
try { | |||||
if (istore != null) | |||||
istore.close(); | |||||
} catch (MessagingException ex) { | |||||
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); | |||||
} finally { | |||||
istore = null; | |||||
ifolder = null; | |||||
imessages = null; | |||||
cache.clear(); | |||||
} | |||||
} | |||||
}).start(); | |||||
owner.getLifecycle().removeObserver(this); | |||||
} | |||||
@Override | |||||
public void loadInitial(LoadInitialParams params, LoadInitialCallback<TupleMessageEx> callback) { | |||||
Log.i(Helper.TAG, "SDS load initial"); | |||||
try { | |||||
SearchResult result = search(search, params.requestedStartPosition, params.requestedLoadSize); | |||||
callback.onResult(result.messages, params.requestedStartPosition, result.total); | |||||
} catch (Throwable ex) { | |||||
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); | |||||
reportError(ex); | |||||
} | |||||
} | |||||
@Override | |||||
public void loadRange(LoadRangeParams params, LoadRangeCallback<TupleMessageEx> callback) { | |||||
Log.i(Helper.TAG, "SDS load range"); | |||||
try { | |||||
SearchResult result = search(search, params.startPosition, params.loadSize); | |||||
callback.onResult(result.messages); | |||||
} catch (Throwable ex) { | |||||
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); | |||||
reportError(ex); | |||||
} | |||||
} | |||||
private SearchResult search(String term, int from, int count) throws MessagingException, IOException { | |||||
Log.i(Helper.TAG, "SDS search from=" + from + " count=" + count); | |||||
if (istore == null) { | |||||
DB db = DB.getInstance(context); | |||||
folder = db.folder().getFolder(fid); | |||||
account = db.account().getAccount(folder.account); | |||||
// Refresh token | |||||
if (account.auth_type == Helper.AUTH_TYPE_GMAIL) { | |||||
account.password = Helper.refreshToken(context, "com.google", account.user, account.password); | |||||
db.account().setAccountPassword(account.id, account.password); | |||||
} | |||||
Properties props = MessageHelper.getSessionProperties(context, account.auth_type); | |||||
props.setProperty("mail.imap.throwsearchexception", "true"); | |||||
Session isession = Session.getInstance(props, null); | |||||
Log.i(Helper.TAG, "SDS connecting account=" + account.name); | |||||
istore = (IMAPStore) isession.getStore("imaps"); | |||||
istore.connect(account.host, account.port, account.user, account.password); | |||||
Log.i(Helper.TAG, "SDS opening folder=" + folder.name); | |||||
ifolder = (IMAPFolder) istore.getFolder(folder.name); | |||||
ifolder.open(Folder.READ_WRITE); | |||||
Log.i(Helper.TAG, "SDS searching term=" + term); | |||||
imessages = ifolder.search( | |||||
new OrTerm( | |||||
new FromStringTerm(term), | |||||
new OrTerm( | |||||
new SubjectTerm(term), | |||||
new BodyTerm(term)))); | |||||
Log.i(Helper.TAG, "SDS found messages=" + imessages.length); | |||||
} | |||||
SearchResult result = new SearchResult(); | |||||
result.total = imessages.length; | |||||
result.messages = new ArrayList<>(); | |||||
List<Message> selected = new ArrayList<>(); | |||||
int base = imessages.length - 1 - from; | |||||
for (int i = base; i >= 0 && i >= base - count + 1; i--) | |||||
selected.add(imessages[i]); | |||||
Log.i(Helper.TAG, "SDS selected messages=" + selected.size()); | |||||
FetchProfile fp = new FetchProfile(); | |||||
fp.add(UIDFolder.FetchProfileItem.UID); | |||||
fp.add(IMAPFolder.FetchProfileItem.FLAGS); | |||||
fp.add(FetchProfile.Item.ENVELOPE); | |||||
fp.add(FetchProfile.Item.CONTENT_INFO); | |||||
fp.add(IMAPFolder.FetchProfileItem.HEADERS); | |||||
fp.add(IMAPFolder.FetchProfileItem.MESSAGE); | |||||
ifolder.fetch(selected.toArray(new Message[0]), fp); | |||||
for (int s = 0; s < selected.size(); s++) { | |||||
int pos = from + s; | |||||
if (cache.get(pos) != null) { | |||||
Log.i(Helper.TAG, "SDS from cache pos=" + pos); | |||||
result.messages.add(cache.get(pos)); | |||||
continue; | |||||
} | |||||
Message imessage = selected.get(s); | |||||
long uid = ifolder.getUID(imessage); | |||||
MessageHelper helper = new MessageHelper((MimeMessage) imessage); | |||||
boolean seen = helper.getSeen(); | |||||
TupleMessageEx message = new TupleMessageEx(); | |||||
message.id = uid; | |||||
message.account = folder.account; | |||||
message.folder = folder.id; | |||||
message.uid = uid; | |||||
message.msgid = helper.getMessageID(); | |||||
message.references = TextUtils.join(" ", helper.getReferences()); | |||||
message.inreplyto = helper.getInReplyTo(); | |||||
message.thread = helper.getThreadId(uid); | |||||
message.from = helper.getFrom(); | |||||
message.to = helper.getTo(); | |||||
message.cc = helper.getCc(); | |||||
message.bcc = helper.getBcc(); | |||||
message.reply = helper.getReply(); | |||||
message.subject = imessage.getSubject(); | |||||
message.received = imessage.getReceivedDate().getTime(); | |||||
message.sent = (imessage.getSentDate() == null ? null : imessage.getSentDate().getTime()); | |||||
message.seen = seen; | |||||
message.ui_seen = seen; | |||||
message.ui_hide = false; | |||||
message.accountName = account.name; | |||||
message.folderName = folder.name; | |||||
message.folderType = folder.type; | |||||
message.count = 1; | |||||
message.unseen = (seen ? 0 : 1); | |||||
message.attachments = 0; | |||||
message.body = helper.getHtml(); | |||||
message.virtual = true; | |||||
result.messages.add(message); | |||||
Runtime rt = Runtime.getRuntime(); | |||||
float used = (float) (rt.totalMemory() - rt.freeMemory()) / rt.maxMemory(); | |||||
if (used < MAX_MEMORY_USAGE) | |||||
cache.put(pos, message); | |||||
else | |||||
Log.i(Helper.TAG, "SDS memory used=" + used); | |||||
} | |||||
Log.i(Helper.TAG, "SDS result=" + result.messages.size()); | |||||
return result; | |||||
} | |||||
private void reportError(final Throwable ex) { | |||||
new Handler(Looper.getMainLooper()).post(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
Toast.makeText(context, Helper.formatThrowable(ex), Toast.LENGTH_LONG).show(); | |||||
} | |||||
}); | |||||
} | |||||
private class SearchResult { | |||||
int total; | |||||
List<TupleMessageEx> messages; | |||||
} | |||||
} |