diff --git a/app/schemas/eu.faircode.email.DB/27.json b/app/schemas/eu.faircode.email.DB/27.json new file mode 100644 index 0000000000..70d2665533 --- /dev/null +++ b/app/schemas/eu.faircode.email.DB/27.json @@ -0,0 +1,1067 @@ +{ + "formatVersion": 1, + "database": { + "version": 27, + "identityHash": "676ec980c61b308375284cd46672d61e", + "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, `starttls` INTEGER NOT NULL, `insecure` INTEGER NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `auth_type` INTEGER NOT NULL, `primary` INTEGER NOT NULL, `color` INTEGER, `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": "starttls", + "columnName": "starttls", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insecure", + "columnName": "insecure", + "affinity": "INTEGER", + "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": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "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`)" + }, + { + "name": "index_identity_account_email", + "unique": false, + "columnNames": [ + "account", + "email" + ], + "createSql": "CREATE INDEX `index_identity_account_email` ON `${TABLE_NAME}` (`account`, `email`)" + } + ], + "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, `starttls` INTEGER NOT NULL, `insecure` INTEGER 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, `created` 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": "starttls", + "columnName": "starttls", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insecure", + "columnName": "insecure", + "affinity": "INTEGER", + "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": "created", + "columnName": "created", + "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, `extra` TEXT, `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, `preview` TEXT, `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": "extra", + "columnName": "extra", + "affinity": "TEXT", + "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": "preview", + "columnName": "preview", + "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": "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, \"676ec980c61b308375284cd46672d61e\")" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/AdapterIdentity.java b/app/src/main/java/eu/faircode/email/AdapterIdentity.java index b46815d615..47e6a74cff 100644 --- a/app/src/main/java/eu/faircode/email/AdapterIdentity.java +++ b/app/src/main/java/eu/faircode/email/AdapterIdentity.java @@ -21,6 +21,7 @@ package eu.faircode.email; import android.content.Context; import android.content.Intent; +import android.graphics.Color; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -49,6 +50,7 @@ public class AdapterIdentity extends RecyclerView.Adapter liveIdentity(long id); diff --git a/app/src/main/java/eu/faircode/email/DaoMessage.java b/app/src/main/java/eu/faircode/email/DaoMessage.java index eff599650d..709b9b7493 100644 --- a/app/src/main/java/eu/faircode/email/DaoMessage.java +++ b/app/src/main/java/eu/faircode/email/DaoMessage.java @@ -36,7 +36,7 @@ public interface DaoMessage { // https://www.sqlite.org/lang_select.html @Query("SELECT message.*" + - ", account.name AS accountName, account.color AS accountColor" + + ", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", COUNT(message.id) AS count" + ", SUM(CASE WHEN message.ui_seen" + @@ -51,6 +51,7 @@ public interface DaoMessage { ", MAX(CASE WHEN folder.unified THEN message.id ELSE 0 END) AS dummy" + " FROM message" + " JOIN account ON account.id = message.account" + + " LEFT JOIN identity ON identity.id = message.identity" + " JOIN folder ON folder.id = message.folder" + " WHERE account.`synchronize`" + " AND (NOT message.ui_hide OR :debug)" + @@ -65,7 +66,7 @@ public interface DaoMessage { DataSource.Factory pagedUnifiedInbox(String sort, boolean debug); @Query("SELECT message.*" + - ", account.name AS accountName, account.color AS accountColor" + + ", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", COUNT(message.id) AS count" + ", SUM(CASE WHEN message.ui_seen" + @@ -80,6 +81,7 @@ public interface DaoMessage { ", MAX(CASE WHEN folder.id = :folder THEN message.id ELSE 0 END) AS dummy" + " FROM message" + " JOIN account ON account.id = message.account" + + " LEFT JOIN identity ON identity.id = message.identity" + " JOIN folder ON folder.id = message.folder" + " JOIN folder f ON f.id = :folder" + " WHERE (message.account = f.account OR folder.type = '" + EntityFolder.OUTBOX + "')" + @@ -95,7 +97,7 @@ public interface DaoMessage { DataSource.Factory pagedFolder(long folder, String sort, boolean found, boolean debug); @Query("SELECT message.*" + - ", account.name AS accountName, account.color AS accountColor" + + ", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", (SELECT COUNT(m1.id) FROM message m1 WHERE m1.account = message.account AND m1.thread = message.thread AND NOT m1.ui_hide) AS count" + ", CASE WHEN message.ui_seen THEN 0 ELSE 1 END AS unseen" + @@ -103,6 +105,7 @@ public interface DaoMessage { ", (SELECT COUNT(a.id) FROM attachment a WHERE a.message = message.id) AS attachments" + " FROM message" + " JOIN account ON account.id = message.account" + + " LEFT JOIN identity ON identity.id = message.identity" + " JOIN folder ON folder.id = message.folder" + " WHERE message.account = :account" + " AND message.thread = :thread" + @@ -165,7 +168,7 @@ public interface DaoMessage { List getMessageWithoutPreview(); @Query("SELECT message.*" + - ", account.name AS accountName, account.color AS accountColor" + + ", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", (SELECT COUNT(m1.id) FROM message m1 WHERE m1.account = message.account AND m1.thread = message.thread AND NOT m1.ui_hide) AS count" + ", CASE WHEN message.ui_seen THEN 0 ELSE 1 END AS unseen" + @@ -173,12 +176,13 @@ public interface DaoMessage { ", (SELECT COUNT(a.id) FROM attachment a WHERE a.message = message.id) AS attachments" + " FROM message" + " JOIN account ON account.id = message.account" + + " LEFT JOIN identity ON identity.id = message.identity" + " JOIN folder ON folder.id = message.folder" + " WHERE message.id = :id") LiveData liveMessage(long id); @Query("SELECT message.*" + - ", account.name AS accountName, account.color AS accountColor" + + ", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" + ", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" + ", 1 AS count" + ", 1 AS unseen" + @@ -186,6 +190,7 @@ public interface DaoMessage { ", 0 AS attachments" + " FROM message" + " JOIN account ON account.id = message.account" + + " LEFT JOIN identity ON identity.id = message.identity" + " JOIN folder ON folder.id = message.folder" + " WHERE account.`synchronize`" + " AND folder.unified" + diff --git a/app/src/main/java/eu/faircode/email/EntityIdentity.java b/app/src/main/java/eu/faircode/email/EntityIdentity.java index d9c5525a4b..f93b0335f8 100644 --- a/app/src/main/java/eu/faircode/email/EntityIdentity.java +++ b/app/src/main/java/eu/faircode/email/EntityIdentity.java @@ -36,7 +36,8 @@ import static androidx.room.ForeignKey.CASCADE; @ForeignKey(childColumns = "account", entity = EntityAccount.class, parentColumns = "id", onDelete = CASCADE) }, indices = { - @Index(value = {"account"}) + @Index(value = {"account"}), + @Index(value = {"account", "email"}) } ) public class EntityIdentity { @@ -67,6 +68,7 @@ public class EntityIdentity { public Integer auth_type; @NonNull public Boolean primary; + public Integer color; @NonNull public Boolean synchronize; @NonNull @@ -88,6 +90,8 @@ public class EntityIdentity { json.put("password", ""); json.put("auth_type", auth_type); json.put("primary", primary); + if (color != null) + json.put("color", color); json.put("synchronize", false); json.put("store_sent", store_sent); // not state @@ -109,6 +113,8 @@ public class EntityIdentity { identity.password = json.getString("password"); identity.auth_type = json.getInt("auth_type"); identity.primary = json.getBoolean("primary"); + if (json.has("color")) + identity.color = json.getInt("color"); identity.synchronize = json.getBoolean("synchronize"); identity.store_sent = json.getBoolean("store_sent"); return identity; @@ -129,6 +135,7 @@ public class EntityIdentity { this.user.equals(other.user) && this.password.equals(other.password) && this.primary.equals(other.primary) && + (this.color == null ? other.color == null : this.color.equals(other.color)) && this.synchronize.equals(other.synchronize) && this.store_sent.equals(other.store_sent) && (this.state == null ? other.state == null : this.state.equals(other.state)) && diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java index 1ea5a4b9d8..10752e6de8 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java @@ -22,6 +22,8 @@ package eu.faircode.email; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; @@ -37,11 +39,14 @@ import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.ScrollView; import android.widget.Spinner; import android.widget.TextView; +import com.android.colorpicker.ColorPickerDialog; +import com.android.colorpicker.ColorPickerSwatch; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.textfield.TextInputLayout; @@ -61,6 +66,7 @@ import javax.mail.Transport; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.Group; +import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Observer; public class FragmentIdentity extends FragmentEx { @@ -80,6 +86,9 @@ public class FragmentIdentity extends FragmentEx { private EditText etPort; private EditText etUser; private TextInputLayout tilPassword; + private Button btnColor; + private View vwColor; + private ImageView ibColorDefault; private CheckBox cbSynchronize; private CheckBox cbPrimary; private CheckBox cbStoreSent; @@ -90,6 +99,7 @@ public class FragmentIdentity extends FragmentEx { private Group grpAdvanced; private long id = -1; + private int color = Color.TRANSPARENT; @Override public void onCreate(Bundle savedInstanceState) { @@ -131,6 +141,10 @@ public class FragmentIdentity extends FragmentEx { etUser = view.findViewById(R.id.etUser); tilPassword = view.findViewById(R.id.tilPassword); + btnColor = view.findViewById(R.id.btnColor); + vwColor = view.findViewById(R.id.vwColor); + ibColorDefault = view.findViewById(R.id.ibColorDefault); + cbSynchronize = view.findViewById(R.id.cbSynchronize); cbPrimary = view.findViewById(R.id.cbPrimary); cbStoreSent = view.findViewById(R.id.cbStoreSent); @@ -289,6 +303,37 @@ public class FragmentIdentity extends FragmentEx { } }); + vwColor.setBackgroundColor(color); + btnColor.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (Helper.isPro(getContext())) { + int[] colors = getContext().getResources().getIntArray(R.array.colorPicker); + ColorPickerDialog colorPickerDialog = new ColorPickerDialog(); + colorPickerDialog.initialize(R.string.title_account_color, colors, color, 4, colors.length); + colorPickerDialog.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener() { + @Override + public void onColorSelected(int color) { + setColor(color); + } + }); + colorPickerDialog.show(getFragmentManager(), "colorpicker"); + } else { + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + fragmentTransaction.hide(FragmentIdentity.this); + fragmentTransaction.add(R.id.content_frame, new FragmentPro()).addToBackStack("pro"); + fragmentTransaction.commit(); + } + } + }); + + ibColorDefault.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setColor(Color.TRANSPARENT); + } + }); + cbSynchronize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { @@ -318,6 +363,7 @@ public class FragmentIdentity extends FragmentEx { args.putString("port", etPort.getText().toString()); args.putString("user", etUser.getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString()); + args.putInt("color", color); args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("primary", cbPrimary.isChecked()); args.putBoolean("store_sent", cbStoreSent.isChecked()); @@ -336,6 +382,7 @@ public class FragmentIdentity extends FragmentEx { String port = args.getString("port"); String user = args.getString("user"); String password = args.getString("password"); + Integer color = args.getInt("color"); int auth_type = args.getInt("auth_type"); boolean synchronize = args.getBoolean("synchronize"); boolean primary = args.getBoolean("primary"); @@ -356,6 +403,8 @@ public class FragmentIdentity extends FragmentEx { if (TextUtils.isEmpty(replyto)) replyto = null; + if (Color.TRANSPARENT == color) + color = null; // Check SMTP server if (synchronize) { @@ -396,6 +445,7 @@ public class FragmentIdentity extends FragmentEx { identity.port = Integer.parseInt(port); identity.user = user; identity.password = password; + identity.color = color; identity.auth_type = auth_type; identity.synchronize = synchronize; identity.primary = (identity.synchronize && primary); @@ -504,6 +554,7 @@ public class FragmentIdentity extends FragmentEx { outState.putInt("provider", spProvider.getSelectedItemPosition()); outState.putString("password", tilPassword.getEditText().getText().toString()); outState.putInt("advanced", grpAdvanced.getVisibility()); + outState.putInt("color", color); } @Override @@ -535,6 +586,8 @@ public class FragmentIdentity extends FragmentEx { cbPrimary.setChecked(identity == null ? true : identity.primary); cbStoreSent.setChecked(identity == null ? false : identity.store_sent); + color = (identity == null || identity.color == null ? Color.TRANSPARENT : identity.color); + etName.requestFocus(); if (identity == null) @@ -552,10 +605,12 @@ public class FragmentIdentity extends FragmentEx { } else { tilPassword.getEditText().setText(savedInstanceState.getString("password")); grpAdvanced.setVisibility(savedInstanceState.getInt("advanced")); + color = savedInstanceState.getInt("color"); } Helper.setViewsEnabled(view, true); + setColor(color); cbPrimary.setEnabled(cbSynchronize.isChecked()); // Consider previous save/delete as cancelled @@ -629,4 +684,13 @@ public class FragmentIdentity extends FragmentEx { } }); } + + private void setColor(int color) { + FragmentIdentity.this.color = color; + + GradientDrawable border = new GradientDrawable(); + border.setColor(color); + border.setStroke(1, Helper.resolveColor(getContext(), R.attr.colorSeparator)); + vwColor.setBackground(border); + } } diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 9721d0e50d..8d6cfd7307 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -1870,9 +1870,15 @@ public class ServiceSynchronize extends LifecycleService { } if (message == null) { + Address[] to = helper.getTo(); + EntityIdentity identity = null; + if (to != null && to.length > 0) + identity = db.identity().getIdentity(folder.account, ((InternetAddress) to[0]).getAddress()); + message = new EntityMessage(); message.account = folder.account; message.folder = folder.id; + message.identity = (identity == null ? null : identity.id); message.uid = uid; if (!EntityFolder.ARCHIVE.equals(folder.type)) { diff --git a/app/src/main/res/layout/fragment_identity.xml b/app/src/main/res/layout/fragment_identity.xml index 8a31af1d0f..e6d28da289 100644 --- a/app/src/main/res/layout/fragment_identity.xml +++ b/app/src/main/res/layout/fragment_identity.xml @@ -300,6 +300,39 @@ android:inputType="textPassword" /> +