From 24991d621ba6f824d7eeba8b9d99332c4521a5d5 Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 20 Oct 2018 17:56:09 +0000 Subject: [PATCH] Search improvements --- app/schemas/eu.faircode.email.DB/22.json | 1016 +++++++++++++++++ .../email/BoundaryCallbackMessages.java | 3 +- app/src/main/java/eu/faircode/email/DB.java | 12 +- .../java/eu/faircode/email/DaoMessage.java | 23 +- .../java/eu/faircode/email/EntityMessage.java | 4 +- .../eu/faircode/email/FragmentMessages.java | 39 +- .../eu/faircode/email/ServiceSynchronize.java | 6 +- .../eu/faircode/email/ViewModelBrowse.java | 29 +- 8 files changed, 1081 insertions(+), 51 deletions(-) create mode 100644 app/schemas/eu.faircode.email.DB/22.json diff --git a/app/schemas/eu.faircode.email.DB/22.json b/app/schemas/eu.faircode.email.DB/22.json new file mode 100644 index 0000000000..43bdef703d --- /dev/null +++ b/app/schemas/eu.faircode.email.DB/22.json @@ -0,0 +1,1016 @@ +{ + "formatVersion": 1, + "database": { + "version": 22, + "identityHash": "4a69fca3293eda334465a8c9a5cda77f", + "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, `signature` TEXT, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `auth_type` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `primary` INTEGER NOT NULL, `color` INTEGER, `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": "signature", + "columnName": "signature", + "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": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "primary", + "columnName": "primary", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "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, `poll_interval` INTEGER, `after` INTEGER NOT NULL, `display` TEXT, `hide` INTEGER NOT NULL, `unified` 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": "poll_interval", + "columnName": "poll_interval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "after", + "columnName": "after", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "display", + "columnName": "display", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hide", + "columnName": "hide", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unified", + "columnName": "unified", + "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`)" + }, + { + "name": "index_folder_unified", + "unique": false, + "columnNames": [ + "unified" + ], + "createSql": "CREATE INDEX `index_folder_unified` ON `${TABLE_NAME}` (`unified`)" + } + ], + "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, `deliveredto` TEXT, `inreplyto` TEXT, `thread` TEXT, `avatar` TEXT, `from` TEXT, `to` TEXT, `cc` TEXT, `bcc` TEXT, `reply` TEXT, `headers` TEXT, `subject` TEXT, `size` INTEGER, `content` INTEGER NOT NULL, `sent` INTEGER, `received` INTEGER NOT NULL, `stored` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `flagged` INTEGER NOT NULL, `ui_seen` INTEGER NOT NULL, `ui_flagged` INTEGER NOT NULL, `ui_hide` INTEGER NOT NULL, `ui_found` INTEGER NOT NULL, `ui_ignored` 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": "deliveredto", + "columnName": "deliveredto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inreplyto", + "columnName": "inreplyto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thread", + "columnName": "thread", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatar", + "columnName": "avatar", + "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": "headers", + "columnName": "headers", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "INTEGER", + "notNull": true + }, + { + "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": "flagged", + "columnName": "flagged", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_seen", + "columnName": "ui_seen", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_flagged", + "columnName": "ui_flagged", + "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": "ui_ignored", + "columnName": "ui_ignored", + "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_ui_found", + "unique": true, + "columnNames": [ + "folder", + "uid", + "ui_found" + ], + "createSql": "CREATE UNIQUE INDEX `index_message_folder_uid_ui_found` ON `${TABLE_NAME}` (`folder`, `uid`, `ui_found`)" + }, + { + "name": "index_message_msgid_folder_ui_found", + "unique": true, + "columnNames": [ + "msgid", + "folder", + "ui_found" + ], + "createSql": "CREATE UNIQUE INDEX `index_message_msgid_folder_ui_found` ON `${TABLE_NAME}` (`msgid`, `folder`, `ui_found`)" + }, + { + "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`)" + }, + { + "name": "index_message_ui_found", + "unique": false, + "columnNames": [ + "ui_found" + ], + "createSql": "CREATE INDEX `index_message_ui_found` ON `${TABLE_NAME}` (`ui_found`)" + }, + { + "name": "index_message_ui_ignored", + "unique": false, + "columnNames": [ + "ui_ignored" + ], + "createSql": "CREATE INDEX `index_message_ui_ignored` ON `${TABLE_NAME}` (`ui_ignored`)" + } + ], + "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, `cid` TEXT, `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": "cid", + "columnName": "cid", + "affinity": "TEXT", + "notNull": false + }, + { + "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`)" + }, + { + "name": "index_attachment_message_cid", + "unique": true, + "columnNames": [ + "message", + "cid" + ], + "createSql": "CREATE UNIQUE INDEX `index_attachment_message_cid` ON `${TABLE_NAME}` (`message`, `cid`)" + } + ], + "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": [] + }, + { + "tableName": "log", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `time` INTEGER NOT NULL, `data` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_log_time", + "unique": false, + "columnNames": [ + "time" + ], + "createSql": "CREATE INDEX `index_log_time` ON `${TABLE_NAME}` (`time`)" + } + ], + "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, \"4a69fca3293eda334465a8c9a5cda77f\")" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java b/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java index 5fa3e2cd09..51897d91a1 100644 --- a/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java +++ b/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java @@ -35,7 +35,8 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback 0" + " ORDER BY CASE" + @@ -83,7 +84,7 @@ public interface DaoMessage { " JOIN folder f ON f.id = :folder" + " WHERE (message.account = f.account OR folder.type = '" + EntityFolder.OUTBOX + "')" + " AND (NOT message.ui_hide OR :debug)" + - " AND (NOT :found OR ui_found = :found)" + + " AND ui_found = :found" + " GROUP BY CASE WHEN message.thread IS NULL THEN message.id ELSE message.thread END" + " HAVING SUM(CASE WHEN folder.id = :folder THEN 1 ELSE 0 END) > 0" + " ORDER BY CASE" + @@ -126,26 +127,30 @@ public interface DaoMessage { @Query("SELECT *" + " FROM message" + " WHERE folder = :folder" + - " AND uid = :uid") - EntityMessage getMessageByUid(long folder, long uid); + " AND uid = :uid" + + " AND ui_found = :found") + EntityMessage getMessageByUid(long folder, long uid, boolean found); @Query("SELECT *" + " FROM message" + - " WHERE folder = :folder") + " WHERE folder = :folder" + + " AND NOT ui_found") List getMessageByFolder(long folder); @Query("SELECT *" + " FROM message" + " WHERE account = :account" + - " AND thread = :thread") + " AND thread = :thread" + + " AND NOT ui_found") List getMessageByThread(long account, String thread); @Query("SELECT message.* FROM message" + " JOIN folder ON folder.id = message.folder" + " WHERE message.account = :account" + " AND (message.msgid = :msgid" + - " OR message.msgid = :reference)") - List getMessageByMsgId(long account, String msgid, String reference); + " OR message.msgid = :reference)" + + " AND ui_found = :found") + List getMessageByMsgId(long account, String msgid, String reference, boolean found); @Query("SELECT message.*" + ", account.name AS accountName, account.color AS accountColor" + @@ -167,6 +172,7 @@ public interface DaoMessage { " AND folder.unified" + " AND NOT message.ui_seen" + " AND NOT message.ui_hide" + + " AND NOT message.ui_found" + " AND NOT message.ui_ignored" + " ORDER BY message.received") LiveData> liveUnseenUnified(); @@ -208,9 +214,6 @@ public interface DaoMessage { @Query("UPDATE message SET error = :error WHERE id = :id") int setMessageError(long id, String error); - @Query("UPDATE message SET ui_found = :found WHERE id = :id") - int setMessageFound(long id, boolean found); - @Query("UPDATE message SET content = :content WHERE id = :id") int setMessageContent(long id, boolean content); diff --git a/app/src/main/java/eu/faircode/email/EntityMessage.java b/app/src/main/java/eu/faircode/email/EntityMessage.java index 29770591b1..5fc3bf0baf 100644 --- a/app/src/main/java/eu/faircode/email/EntityMessage.java +++ b/app/src/main/java/eu/faircode/email/EntityMessage.java @@ -58,8 +58,8 @@ import static androidx.room.ForeignKey.CASCADE; @Index(value = {"folder"}), @Index(value = {"identity"}), @Index(value = {"replying"}), - @Index(value = {"folder", "uid"}, unique = true), - @Index(value = {"msgid", "folder"}, unique = true), + @Index(value = {"folder", "uid", "ui_found"}, unique = true), + @Index(value = {"msgid", "folder", "ui_found"}, unique = true), @Index(value = {"thread"}), @Index(value = {"received"}), @Index(value = {"ui_seen"}), diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 9baf5ab20f..be31a2ca27 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -644,15 +644,30 @@ public class FragmentMessages extends FragmentEx { menuSearch.collapseActionView(); if (Helper.isPro(getContext())) { - Intent intent = new Intent(); - intent.putExtra("folder", folder); - intent.putExtra("search", query); + Bundle args = new Bundle(); + args.putLong("folder", folder); + args.putString("search", query); + + new SimpleTask() { + @Override + protected Void onLoad(Context context, Bundle args) throws Throwable { + DB.getInstance(context).message().deleteFoundMessages(); + return null; + } - FragmentMessages fragment = new FragmentMessages(); - fragment.setArguments(intent.getExtras()); - FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); - fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("search"); - fragmentTransaction.commit(); + @Override + protected void onLoaded(Bundle args, Void data) { + Intent intent = new Intent(); + intent.putExtra("folder", args.getLong("folder", folder)); + intent.putExtra("search", args.getString("search")); + + FragmentMessages fragment = new FragmentMessages(); + fragment.setArguments(intent.getExtras()); + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("search"); + fragmentTransaction.commit(); + } + }.load(FragmentMessages.this, args); } else { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.content_frame, new FragmentPro()).addToBackStack("pro"); @@ -741,7 +756,7 @@ public class FragmentMessages extends FragmentEx { private void loadMessages() { final DB db = DB.getInstance(getContext()); - ViewModelBrowse model = ViewModelProviders.of(this).get(ViewModelBrowse.class); + ViewModelBrowse model = ViewModelProviders.of(getActivity()).get(ViewModelBrowse.class); model.set(getContext(), folder, search, REMOTE_PAGE_SIZE); // Observe folder/messages/search @@ -760,8 +775,7 @@ public class FragmentMessages extends FragmentEx { break; case FOLDER: if (searchCallback == null) - searchCallback = new BoundaryCallbackMessages( - this, model, + searchCallback = new BoundaryCallbackMessages(this, model, new BoundaryCallbackMessages.IBoundaryCallbackMessages() { @Override public void onLoading() { @@ -802,8 +816,7 @@ public class FragmentMessages extends FragmentEx { } } else { if (searchCallback == null) - searchCallback = new BoundaryCallbackMessages( - this, model, + searchCallback = new BoundaryCallbackMessages(this, model, new BoundaryCallbackMessages.IBoundaryCallbackMessages() { @Override public void onLoading() { diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 21c3bd2d62..a50a771b97 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -1596,7 +1596,7 @@ public class ServiceSynchronize extends LifecycleService { List full = new ArrayList<>(); for (Message imessage : isub) { long uid = ifolder.getUID(imessage); - EntityMessage message = db.message().getMessageByUid(folder.id, uid); + EntityMessage message = db.message().getMessageByUid(folder.id, uid, false); if (message == null) full.add(imessage); } @@ -1695,7 +1695,7 @@ public class ServiceSynchronize extends LifecycleService { DB db = DB.getInstance(context); // Find message by uid (fast, no headers required) - EntityMessage message = db.message().getMessageByUid(folder.id, uid); + EntityMessage message = db.message().getMessageByUid(folder.id, uid, found); // Find message by Message-ID (slow, headers required) // - messages in inbox have same id as message sent to self @@ -1706,7 +1706,7 @@ public class ServiceSynchronize extends LifecycleService { String[] refs = helper.getReferences(); String reference = (refs.length == 1 && refs[0].indexOf(BuildConfig.APPLICATION_ID) > 0 ? refs[0] : msgid); Log.i(Helper.TAG, "Searching for " + msgid + " / " + reference); - for (EntityMessage dup : db.message().getMessageByMsgId(folder.account, msgid, reference)) { + for (EntityMessage dup : db.message().getMessageByMsgId(folder.account, msgid, reference, found)) { EntityFolder dfolder = db.folder().getFolder(dup.folder); boolean outbox = EntityFolder.OUTBOX.equals(dfolder.type); Log.i(Helper.TAG, folder.name + " found as id=" + dup.id + "/" + dup.uid + diff --git a/app/src/main/java/eu/faircode/email/ViewModelBrowse.java b/app/src/main/java/eu/faircode/email/ViewModelBrowse.java index bcb7c663ce..3e20073ccf 100644 --- a/app/src/main/java/eu/faircode/email/ViewModelBrowse.java +++ b/app/src/main/java/eu/faircode/email/ViewModelBrowse.java @@ -8,9 +8,7 @@ import com.sun.mail.imap.IMAPMessage; import com.sun.mail.imap.IMAPStore; import com.sun.mail.util.FolderClosedIOException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Properties; import javax.mail.FetchProfile; @@ -38,7 +36,6 @@ public class ViewModelBrowse extends ViewModel { private IMAPStore istore = null; private IMAPFolder ifolder = null; private Message[] imessages = null; - private List existing = new ArrayList<>(); private int index; private boolean searching = false; private int loaded = 0; @@ -48,6 +45,10 @@ public class ViewModelBrowse extends ViewModel { this.fid = folder; this.search = search; this.pageSize = pageSize; + + this.index = -1; + this.searching = false; + this.loaded = 0; } Context getContext() { @@ -130,15 +131,9 @@ public class ViewModelBrowse extends ViewModel { try { long uid = ifolder.getUID(isub[j]); Log.i(Helper.TAG, "Boundary sync uid=" + uid); - EntityMessage message = db.message().getMessageByUid(fid, uid); - if (message == null) { - ServiceSynchronize.synchronizeMessage(context, folder, ifolder, (IMAPMessage) isub[j], search != null); - count++; - loaded++; - } else if (search != null) { - existing.add(message.id); - db.message().setMessageFound(message.id, true); - } + ServiceSynchronize.synchronizeMessage(context, folder, ifolder, (IMAPMessage) isub[j], search != null); + count++; + loaded++; } catch (MessageRemovedException ex) { Log.w(Helper.TAG, "Boundary " + ex + "\n" + Log.getStackTraceString(ex)); } catch (FolderClosedException ex) { @@ -164,24 +159,16 @@ public class ViewModelBrowse extends ViewModel { } void clear() { - Log.i(Helper.TAG, "Boundary close"); - - DB db = DB.getInstance(context); - for (long id : existing) - db.message().setMessageFound(id, false); - db.message().deleteFoundMessages(); - + Log.i(Helper.TAG, "Boundary clear"); try { if (istore != null) istore.close(); } catch (Throwable ex) { Log.e(Helper.TAG, "Boundary " + ex + "\n" + Log.getStackTraceString(ex)); } finally { - context = null; istore = null; ifolder = null; imessages = null; - existing.clear(); } } }