diff --git a/FlowCrypt/schemas/com.flowcrypt.email.database.FlowCryptRoomDatabase/27.json b/FlowCrypt/schemas/com.flowcrypt.email.database.FlowCryptRoomDatabase/27.json new file mode 100644 index 0000000000..e506c24d8a --- /dev/null +++ b/FlowCrypt/schemas/com.flowcrypt.email.database.FlowCryptRoomDatabase/27.json @@ -0,0 +1,997 @@ +{ + "formatVersion": 1, + "database": { + "version": 27, + "identityHash": "eaedc306ac21cf0fe3b68066c95d72e8", + "entities": [ + { + "tableName": "accounts_aliases", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `account_type` TEXT NOT NULL, `send_as_email` TEXT NOT NULL, `display_name` TEXT DEFAULT NULL, `is_default` INTEGER DEFAULT 0, `verification_status` TEXT NOT NULL, FOREIGN KEY(`email`, `account_type`) REFERENCES `accounts`(`email`, `account_type`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountType", + "columnName": "account_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sendAsEmail", + "columnName": "send_as_email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "display_name", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isDefault", + "columnName": "is_default", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "verificationStatus", + "columnName": "verification_status", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "email_account_type_send_as_email_in_accounts_aliases", + "unique": true, + "columnNames": [ + "email", + "account_type", + "send_as_email" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_account_type_send_as_email_in_accounts_aliases` ON `${TABLE_NAME}` (`email`, `account_type`, `send_as_email`)" + } + ], + "foreignKeys": [ + { + "table": "accounts", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "email", + "account_type" + ], + "referencedColumns": [ + "email", + "account_type" + ] + } + ] + }, + { + "tableName": "accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `account_type` TEXT DEFAULT NULL, `display_name` TEXT DEFAULT NULL, `given_name` TEXT DEFAULT NULL, `family_name` TEXT DEFAULT NULL, `photo_url` TEXT DEFAULT NULL, `is_enabled` INTEGER DEFAULT 1, `is_active` INTEGER DEFAULT 0, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `imap_server` TEXT NOT NULL, `imap_port` INTEGER DEFAULT 143, `imap_use_ssl_tls` INTEGER DEFAULT 0, `imap_use_starttls` INTEGER DEFAULT 0, `imap_auth_mechanisms` TEXT, `smtp_server` TEXT NOT NULL, `smtp_port` INTEGER DEFAULT 25, `smtp_use_ssl_tls` INTEGER DEFAULT 0, `smtp_use_starttls` INTEGER DEFAULT 0, `smtp_auth_mechanisms` TEXT, `smtp_use_custom_sign` INTEGER DEFAULT 0, `smtp_username` TEXT DEFAULT NULL, `smtp_password` TEXT DEFAULT NULL, `contacts_loaded` INTEGER DEFAULT 0, `show_only_encrypted` INTEGER DEFAULT 0, `uuid` TEXT DEFAULT NULL, `client_configuration` TEXT DEFAULT NULL, `use_api` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountType", + "columnName": "account_type", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "displayName", + "columnName": "display_name", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "givenName", + "columnName": "given_name", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "photoUrl", + "columnName": "photo_url", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isEnabled", + "columnName": "is_enabled", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "1" + }, + { + "fieldPath": "isActive", + "columnName": "is_active", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imapServer", + "columnName": "imap_server", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imapPort", + "columnName": "imap_port", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "143" + }, + { + "fieldPath": "imapUseSslTls", + "columnName": "imap_use_ssl_tls", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "imapUseStarttls", + "columnName": "imap_use_starttls", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "imapAuthMechanisms", + "columnName": "imap_auth_mechanisms", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "smtpServer", + "columnName": "smtp_server", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "smtpPort", + "columnName": "smtp_port", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "25" + }, + { + "fieldPath": "smtpUseSslTls", + "columnName": "smtp_use_ssl_tls", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "smtpUseStarttls", + "columnName": "smtp_use_starttls", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "smtpAuthMechanisms", + "columnName": "smtp_auth_mechanisms", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "smtpUseCustomSign", + "columnName": "smtp_use_custom_sign", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "smtpUsername", + "columnName": "smtp_username", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "smtpPassword", + "columnName": "smtp_password", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "contactsLoaded", + "columnName": "contacts_loaded", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "showOnlyEncrypted", + "columnName": "show_only_encrypted", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "clientConfiguration", + "columnName": "client_configuration", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "useAPI", + "columnName": "use_api", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "email_account_type_in_accounts", + "unique": true, + "columnNames": [ + "email", + "account_type" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_account_type_in_accounts` ON `${TABLE_NAME}` (`email`, `account_type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "action_queue", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `action_type` TEXT NOT NULL, `action_json` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "actionType", + "columnName": "action_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "actionJson", + "columnName": "action_json", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "attachment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `folder` TEXT NOT NULL, `uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `encodedSize` INTEGER DEFAULT 0, `type` TEXT NOT NULL, `attachment_id` TEXT, `file_uri` TEXT, `forwarded_folder` TEXT, `forwarded_uid` INTEGER DEFAULT -1, `path` TEXT NOT NULL, FOREIGN KEY(`email`, `folder`, `uid`) REFERENCES `messages`(`email`, `folder`, `uid`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "folder", + "columnName": "folder", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encodedSize", + "columnName": "encodedSize", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachmentId", + "columnName": "attachment_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileUri", + "columnName": "file_uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "forwardedFolder", + "columnName": "forwarded_folder", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "forwardedUid", + "columnName": "forwarded_uid", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "-1" + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "email_uid_folder_path_in_attachment", + "unique": true, + "columnNames": [ + "email", + "uid", + "folder", + "path" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_uid_folder_path_in_attachment` ON `${TABLE_NAME}` (`email`, `uid`, `folder`, `path`)" + }, + { + "name": "email_folder_uid_in_attachment", + "unique": false, + "columnNames": [ + "email", + "folder", + "uid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `email_folder_uid_in_attachment` ON `${TABLE_NAME}` (`email`, `folder`, `uid`)" + } + ], + "foreignKeys": [ + { + "table": "messages", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "email", + "folder", + "uid" + ], + "referencedColumns": [ + "email", + "folder", + "uid" + ] + } + ] + }, + { + "tableName": "contacts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `name` TEXT DEFAULT NULL, `last_use` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "lastUse", + "columnName": "last_use", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "name_in_contacts", + "unique": false, + "columnNames": [ + "name" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `name_in_contacts` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "last_use_in_contacts", + "unique": false, + "columnNames": [ + "last_use" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `last_use_in_contacts` ON `${TABLE_NAME}` (`last_use`)" + }, + { + "name": "email_in_contacts", + "unique": true, + "columnNames": [ + "email" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_in_contacts` ON `${TABLE_NAME}` (`email`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "keys", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `fingerprint` TEXT NOT NULL, `account` TEXT NOT NULL, `account_type` TEXT DEFAULT NULL, `source` TEXT NOT NULL, `public_key` BLOB NOT NULL, `private_key` BLOB NOT NULL, `passphrase` TEXT DEFAULT NULL, `passphrase_type` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`account`, `account_type`) REFERENCES `accounts`(`email`, `account_type`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fingerprint", + "columnName": "fingerprint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountType", + "columnName": "account_type", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "public_key", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "storedPassphrase", + "columnName": "passphrase", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "passphraseType", + "columnName": "passphrase_type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "fingerprint_account_account_type_in_keys", + "unique": true, + "columnNames": [ + "fingerprint", + "account", + "account_type" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `fingerprint_account_account_type_in_keys` ON `${TABLE_NAME}` (`fingerprint`, `account`, `account_type`)" + } + ], + "foreignKeys": [ + { + "table": "accounts", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account", + "account_type" + ], + "referencedColumns": [ + "email", + "account_type" + ] + } + ] + }, + { + "tableName": "labels", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `account_type` TEXT DEFAULT NULL, `name` TEXT NOT NULL, `alias` TEXT DEFAULT NULL, `is_custom` INTEGER NOT NULL DEFAULT 0, `messages_total` INTEGER NOT NULL DEFAULT 0, `message_unread` INTEGER NOT NULL DEFAULT 0, `attributes` TEXT DEFAULT NULL, `next_page_token` TEXT DEFAULT NULL, `history_id` TEXT DEFAULT NULL, FOREIGN KEY(`email`, `account_type`) REFERENCES `accounts`(`email`, `account_type`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountType", + "columnName": "account_type", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "alias", + "columnName": "alias", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isCustom", + "columnName": "is_custom", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "messagesTotal", + "columnName": "messages_total", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "messagesUnread", + "columnName": "message_unread", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "nextPageToken", + "columnName": "next_page_token", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "historyId", + "columnName": "history_id", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "email_account_type_name_in_labels", + "unique": true, + "columnNames": [ + "email", + "account_type", + "name" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_account_type_name_in_labels` ON `${TABLE_NAME}` (`email`, `account_type`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "accounts", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "email", + "account_type" + ], + "referencedColumns": [ + "email", + "account_type" + ] + } + ] + }, + { + "tableName": "messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `folder` TEXT NOT NULL, `uid` INTEGER NOT NULL, `received_date` INTEGER DEFAULT NULL, `sent_date` INTEGER DEFAULT NULL, `from_address` TEXT DEFAULT NULL, `to_address` TEXT DEFAULT NULL, `cc_address` TEXT DEFAULT NULL, `subject` TEXT DEFAULT NULL, `flags` TEXT DEFAULT NULL, `raw_message_without_attachments` TEXT DEFAULT NULL, `is_message_has_attachments` INTEGER DEFAULT 0, `is_encrypted` INTEGER DEFAULT -1, `is_new` INTEGER DEFAULT -1, `state` INTEGER DEFAULT -1, `attachments_directory` TEXT, `error_msg` TEXT DEFAULT NULL, `reply_to` TEXT DEFAULT NULL, `thread_id` TEXT DEFAULT NULL, `history_id` TEXT DEFAULT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "folder", + "columnName": "folder", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "receivedDate", + "columnName": "received_date", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "sentDate", + "columnName": "sent_date", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "fromAddress", + "columnName": "from_address", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "toAddress", + "columnName": "to_address", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "ccAddress", + "columnName": "cc_address", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "flags", + "columnName": "flags", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "rawMessageWithoutAttachments", + "columnName": "raw_message_without_attachments", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "hasAttachments", + "columnName": "is_message_has_attachments", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "-1" + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "-1" + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "-1" + }, + { + "fieldPath": "attachmentsDirectory", + "columnName": "attachments_directory", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "errorMsg", + "columnName": "error_msg", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "replyTo", + "columnName": "reply_to", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "threadId", + "columnName": "thread_id", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "historyId", + "columnName": "history_id", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "email_in_messages", + "unique": false, + "columnNames": [ + "email" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `email_in_messages` ON `${TABLE_NAME}` (`email`)" + }, + { + "name": "email_uid_folder_in_messages", + "unique": true, + "columnNames": [ + "email", + "uid", + "folder" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_uid_folder_in_messages` ON `${TABLE_NAME}` (`email`, `uid`, `folder`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "public_keys", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `recipient` TEXT NOT NULL, `fingerprint` TEXT NOT NULL, `public_key` BLOB NOT NULL, FOREIGN KEY(`recipient`) REFERENCES `contacts`(`email`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recipient", + "columnName": "recipient", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fingerprint", + "columnName": "fingerprint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicKey", + "columnName": "public_key", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "recipient_fingerprint_in_public_keys", + "unique": true, + "columnNames": [ + "recipient", + "fingerprint" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `recipient_fingerprint_in_public_keys` ON `${TABLE_NAME}` (`recipient`, `fingerprint`)" + }, + { + "name": "fingerprint_in_public_keys", + "unique": false, + "columnNames": [ + "fingerprint" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `fingerprint_in_public_keys` ON `${TABLE_NAME}` (`fingerprint`)" + } + ], + "foreignKeys": [ + { + "table": "contacts", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "recipient" + ], + "referencedColumns": [ + "email" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'eaedc306ac21cf0fe3b68066c95d72e8')" + ] + } +} \ No newline at end of file diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/ContactsDao.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/ContactsDao.kt index be0d4a5255..5792f3e775 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/ContactsDao.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/ContactsDao.kt @@ -9,9 +9,7 @@ import android.database.Cursor import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Query -import androidx.room.Transaction import com.flowcrypt.email.database.entity.ContactEntity -import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys /** * This object describes a logic of work with [ContactEntity]. @@ -31,12 +29,8 @@ interface ContactsDao : BaseDao { @Query("SELECT * FROM contacts") fun getAllContactsLD(): LiveData> - @Transaction - @Query("SELECT * FROM contacts WHERE email IN (SELECT recipient FROM public_keys GROUP BY recipient)") - fun getAllContactsWithPgpLD1(): LiveData> - - @Query("SELECT * FROM contacts") - //@Query("SELECT * FROM contacts WHERE has_pgp = 1") + //fixed + @Query("SELECT contacts.* FROM contacts INNER JOIN public_keys ON contacts.email = public_keys.recipient GROUP BY contacts.email ORDER BY contacts._id") fun getAllContactsWithPgpLD(): LiveData> @Query("SELECT * FROM contacts") diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/PublicKeyEntity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/PublicKeyEntity.kt index 7fc421a9ff..ede6790155 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/PublicKeyEntity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/entity/PublicKeyEntity.kt @@ -56,7 +56,6 @@ class PublicKeyEntity( requireNotNull(parcel.createByteArray()) ) - override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeValue(id) parcel.writeString(recipient) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ContactsViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ContactsViewModel.kt index 82aeaaf76f..99bdd11525 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ContactsViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/ContactsViewModel.kt @@ -6,11 +6,14 @@ package com.flowcrypt.email.jetpack.viewmodel import android.app.Application +import android.content.Context +import android.widget.Toast import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope +import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.ApiRepository import com.flowcrypt.email.api.retrofit.FlowcryptApiRepository import com.flowcrypt.email.api.retrofit.response.attester.PubResponse @@ -241,7 +244,16 @@ class ContactsViewModel(application: Application) : AccountViewModel(application viewModelScope.launch { val contact = roomDatabase.contactsDao().getContactByEmailSuspend(pgpContact.email) if (contact == null) { - roomDatabase.contactsDao().insertSuspend(pgpContact.toContactEntity()) + val isInserted = roomDatabase.contactsDao().insertSuspend(pgpContact.toContactEntity()) > 0 + if (isInserted) { + roomDatabase.pubKeysDao().insertSuspend(pgpContact.toPubKey()) + } else { + val context: Context = getApplication() + Toast.makeText( + context, + context.getString(R.string.could_not_save_new_recipient), Toast.LENGTH_LONG + ).show() + } } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/model/PgpContact.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/model/PgpContact.kt index da20a2fc9e..7117eb9c50 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/model/PgpContact.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/model/PgpContact.kt @@ -8,6 +8,7 @@ package com.flowcrypt.email.model import android.os.Parcel import android.os.Parcelable import com.flowcrypt.email.database.entity.ContactEntity +import com.flowcrypt.email.database.entity.PublicKeyEntity import com.flowcrypt.email.security.model.PgpKeyDetails import java.util.ArrayList import javax.mail.internet.AddressException @@ -66,6 +67,14 @@ data class PgpContact constructor( ) } + fun toPubKey(): PublicKeyEntity { + return PublicKeyEntity( + recipient = email, + fingerprint = fingerprint!!, + publicKey = pubkey!!.toByteArray() + ) + } + companion object { @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SelectContactsActivity.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SelectContactsActivity.kt index 761021b377..4523aba626 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SelectContactsActivity.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SelectContactsActivity.kt @@ -5,6 +5,7 @@ package com.flowcrypt.email.ui.activity +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -22,7 +23,6 @@ import androidx.test.espresso.idling.CountingIdlingResource import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.database.entity.ContactEntity -import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys import com.flowcrypt.email.extensions.decrementSafely import com.flowcrypt.email.extensions.incrementSafely import com.flowcrypt.email.jetpack.viewmodel.ContactsViewModel @@ -41,7 +41,7 @@ import com.flowcrypt.email.util.UIUtil * E-mail: DenBond7@gmail.com */ class SelectContactsActivity : BaseBackStackActivity(), - ContactsRecyclerViewAdapter.OnContactClickListener, SearchView.OnQueryTextListener { + ContactsRecyclerViewAdapter.OnContactActionsListener, SearchView.OnQueryTextListener { private var progressBar: View? = null private var recyclerViewContacts: RecyclerView? = null @@ -65,7 +65,7 @@ class SelectContactsActivity : BaseBackStackActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - contactsRecyclerViewAdapter.onContactClickListener = this + contactsRecyclerViewAdapter.onContactActionsListener = this //todo-denbond7 need to fix this in the future. Not urgent //val isMultiply = intent.getBooleanExtra(KEY_EXTRA_IS_MULTIPLY, false) @@ -108,12 +108,14 @@ class SelectContactsActivity : BaseBackStackActivity(), return super.onPrepareOptionsMenu(menu) } - /*override fun onContactClick(contactEntity: ContactEntity) { + override fun onContactClick(contactEntity: ContactEntity) { val intent = Intent() intent.putExtra(KEY_EXTRA_PGP_CONTACT, contactEntity) setResult(Activity.RESULT_OK, intent) finish() - }*/ + } + + override fun onDeleteContact(contactEntity: ContactEntity) {} override fun onQueryTextSubmit(query: String): Boolean { searchPattern = query @@ -180,8 +182,4 @@ class SelectContactsActivity : BaseBackStackActivity(), return intent } } - - override fun onContactClick(contactWithPubKeys: RecipientWithPubKeys) { - TODO("Not yet implemented") - } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/ContactsListFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/ContactsListFragment.kt index fe21baad1f..a40889a5f5 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/ContactsListFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/ContactsListFragment.kt @@ -17,7 +17,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.response.base.Result -import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys +import com.flowcrypt.email.database.entity.ContactEntity +import com.flowcrypt.email.extensions.navController import com.flowcrypt.email.jetpack.viewmodel.ContactsViewModel import com.flowcrypt.email.ui.activity.ImportPgpContactActivity import com.flowcrypt.email.ui.activity.fragment.base.BaseFragment @@ -32,8 +33,7 @@ import com.flowcrypt.email.util.UIUtil * Time: 6:11 PM * E-mail: DenBond7@gmail.com */ -class ContactsListFragment : BaseFragment(), ContactsRecyclerViewAdapter.OnDeleteContactListener, - ContactsRecyclerViewAdapter.OnContactClickListener { +class ContactsListFragment : BaseFragment(), ContactsRecyclerViewAdapter.OnContactActionsListener { private var progressBar: View? = null private var recyclerViewContacts: RecyclerView? = null @@ -46,8 +46,7 @@ class ContactsListFragment : BaseFragment(), ContactsRecyclerViewAdapter.OnDelet override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - contactsRecyclerViewAdapter.onDeleteContactListener = this - contactsRecyclerViewAdapter.onContactClickListener = this + contactsRecyclerViewAdapter.onContactActionsListener = this } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -71,20 +70,20 @@ class ContactsListFragment : BaseFragment(), ContactsRecyclerViewAdapter.OnDelet } } - /*override fun onContactClick(contactEntity: ContactEntity) { + override fun onContactClick(contactEntity: ContactEntity) { navController?.navigate( ContactsListFragmentDirections .actionContactsListFragmentToPublicKeyDetailsFragment(contactEntity) ) - }*/ + } - /*override fun onDeleteContact(contactEntity: ContactEntity) { + override fun onDeleteContact(contactEntity: ContactEntity) { contactsViewModel.deleteContact(contactEntity) Toast.makeText( context, getString(R.string.the_contact_was_deleted, contactEntity.email), Toast.LENGTH_SHORT ).show() - }*/ + } private fun initViews(root: View) { this.progressBar = root.findViewById(R.id.progressBar) @@ -122,7 +121,7 @@ class ContactsListFragment : BaseFragment(), ContactsRecyclerViewAdapter.OnDelet if (it.data.isNullOrEmpty()) { UIUtil.exchangeViewVisibility(true, emptyView, recyclerViewContacts) } else { - //contactsRecyclerViewAdapter.swap(it.data) + contactsRecyclerViewAdapter.swap(it.data) UIUtil.exchangeViewVisibility(false, emptyView, recyclerViewContacts) } } @@ -136,12 +135,4 @@ class ContactsListFragment : BaseFragment(), ContactsRecyclerViewAdapter.OnDelet companion object { private const val REQUEST_CODE_START_IMPORT_PUB_KEY_ACTIVITY = 0 } - - override fun onContactClick(contactWithPubKeys: RecipientWithPubKeys) { - TODO("Not yet implemented") - } - - override fun onDeleteContact(contactWithPubKeys: RecipientWithPubKeys) { - TODO("Not yet implemented") - } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/PreviewImportPgpContactFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/PreviewImportPgpContactFragment.kt index d23b782c35..7d382ca051 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/PreviewImportPgpContactFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/PreviewImportPgpContactFragment.kt @@ -304,11 +304,12 @@ class PreviewImportPgpContactFragment : BaseFragment(), View.OnClickListener, emails.add(keyOwner) - /*if (weakRef.get() != null) { - val contact = FlowCryptRoomDatabase.getDatabase(weakRef.get()?.requireContext()!!) - .contactsDao().getContactByEmail(keyOwner)?.toPgpContact() + if (weakRef.get() != null) { + //todo-denbond7 fix me + val contact = null/*FlowCryptRoomDatabase.getDatabase(weakRef.get()?.requireContext()!!) + .contactsDao().getContactByEmail(keyOwner)?.toPgpContact()*/ return PublicKeyInfo(fingerprint, keyOwner, contact, pgpKeyDetails.publicKey) - }*/ + } } return null } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/adapter/ContactsRecyclerViewAdapter.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/adapter/ContactsRecyclerViewAdapter.kt index 9adbff7f46..9241947235 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/adapter/ContactsRecyclerViewAdapter.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/adapter/ContactsRecyclerViewAdapter.kt @@ -13,7 +13,7 @@ import android.widget.TextView import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.flowcrypt.email.R -import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys +import com.flowcrypt.email.database.entity.ContactEntity /** * This adapter describes logic to prepare show contacts from the database. @@ -26,10 +26,8 @@ import com.flowcrypt.email.database.entity.relation.RecipientWithPubKeys class ContactsRecyclerViewAdapter constructor(private val isDeleteEnabled: Boolean = true) : RecyclerView.Adapter() { - private val list: MutableList = mutableListOf() - var onDeleteContactListener: OnDeleteContactListener? = null - var onContactClickListener: OnContactClickListener? = null - + private val list: MutableList = mutableListOf() + var onContactActionsListener: OnContactActionsListener? = null override fun onCreateViewHolder( parent: ViewGroup, @@ -41,14 +39,14 @@ class ContactsRecyclerViewAdapter constructor(private val isDeleteEnabled: Boole } override fun onBindViewHolder(viewHolder: ContactsRecyclerViewAdapter.ViewHolder, position: Int) { - val contactWithPubKeys = list[position] + val contactEntity = list[position] - if (contactWithPubKeys.contact.name.isNullOrEmpty()) { + if (contactEntity.name.isNullOrEmpty()) { viewHolder.textViewName.visibility = View.GONE viewHolder.textViewEmail.visibility = View.GONE viewHolder.textViewOnlyEmail.visibility = View.VISIBLE - viewHolder.textViewOnlyEmail.text = contactWithPubKeys.contact.email + viewHolder.textViewOnlyEmail.text = contactEntity.email viewHolder.textViewEmail.text = null viewHolder.textViewName.text = null } else { @@ -56,22 +54,22 @@ class ContactsRecyclerViewAdapter constructor(private val isDeleteEnabled: Boole viewHolder.textViewEmail.visibility = View.VISIBLE viewHolder.textViewOnlyEmail.visibility = View.GONE - viewHolder.textViewEmail.text = contactWithPubKeys.contact.email - viewHolder.textViewName.text = contactWithPubKeys.contact.name + viewHolder.textViewEmail.text = contactEntity.email + viewHolder.textViewName.text = contactEntity.name viewHolder.textViewOnlyEmail.text = null } if (isDeleteEnabled) { viewHolder.imageButtonDeleteContact.visibility = View.VISIBLE viewHolder.imageButtonDeleteContact.setOnClickListener { - onDeleteContactListener?.onDeleteContact(contactWithPubKeys) + onContactActionsListener?.onDeleteContact(contactEntity) } } else { viewHolder.imageButtonDeleteContact.visibility = View.GONE } viewHolder.itemView.setOnClickListener { - onContactClickListener?.onContactClick(contactWithPubKeys) + onContactActionsListener?.onContactClick(contactEntity) } } @@ -79,7 +77,7 @@ class ContactsRecyclerViewAdapter constructor(private val isDeleteEnabled: Boole return list.size } - fun swap(newList: List) { + fun swap(newList: List) { val diffUtilCallback = DiffUtilCallback(this.list, newList) val productDiffResult = DiffUtil.calculateDiff(diffUtilCallback) @@ -88,12 +86,9 @@ class ContactsRecyclerViewAdapter constructor(private val isDeleteEnabled: Boole productDiffResult.dispatchUpdatesTo(this) } - interface OnDeleteContactListener { - fun onDeleteContact(contactWithPubKeys: RecipientWithPubKeys) - } - - interface OnContactClickListener { - fun onContactClick(contactWithPubKeys: RecipientWithPubKeys) + interface OnContactActionsListener { + fun onDeleteContact(contactEntity: ContactEntity) + fun onContactClick(contactEntity: ContactEntity) } /** @@ -107,13 +102,13 @@ class ContactsRecyclerViewAdapter constructor(private val isDeleteEnabled: Boole } inner class DiffUtilCallback( - private val oldList: List, - private val newList: List + private val oldList: List, + private val newList: List ) : DiffUtil.Callback() { override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldItem = oldList[oldItemPosition] val newItem = newList[newItemPosition] - return oldItem.publicKeys == newItem.publicKeys + return oldItem.email == newItem.email } override fun getOldListSize(): Int = oldList.size diff --git a/FlowCrypt/src/main/res/values/strings.xml b/FlowCrypt/src/main/res/values/strings.xml index 8f3812524c..ca124420da 100644 --- a/FlowCrypt/src/main/res/values/strings.xml +++ b/FlowCrypt/src/main/res/values/strings.xml @@ -535,4 +535,5 @@ Security threat!\n\nMessage is missing integrity checks (MDC). The sender should update their outdated software.\n\nDisplay the message at your own risk. This message contained a Public Key that was not valid.\n\n%1$s This message contained a block with type = %1$s that was not valid.\n\n%2$s + Couldn\'t save a new recipient