From 6fdc1cae503df08b9e96259852b517a659032949 Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 29 Apr 2019 10:14:55 +0200 Subject: [PATCH] Show selected folders in navigation menu --- app/schemas/eu.faircode.email.DB/75.json | 1729 +++++++++++++++++ .../java/eu/faircode/email/ActivitySetup.java | 149 +- .../java/eu/faircode/email/ActivityView.java | 389 ++-- .../eu/faircode/email/AdapterMessage.java | 6 +- .../eu/faircode/email/AdapterNavAccount.java | 206 ++ .../eu/faircode/email/AdapterNavFolder.java | 225 +++ .../eu/faircode/email/AdapterNavMenu.java | 200 ++ app/src/main/java/eu/faircode/email/DB.java | 9 +- .../java/eu/faircode/email/DaoAccount.java | 10 +- .../java/eu/faircode/email/DaoFolder.java | 15 +- .../java/eu/faircode/email/DaoOperation.java | 3 + .../java/eu/faircode/email/DrawerAdapter.java | 91 - .../java/eu/faircode/email/DrawerItem.java | 95 - .../java/eu/faircode/email/EntityFolder.java | 2 + .../eu/faircode/email/FragmentFolder.java | 8 +- .../java/eu/faircode/email/NavMenuItem.java | 65 + .../eu/faircode/email/TupleAccountEx.java | 4 - .../eu/faircode/email/TupleFolderNav.java | 41 + .../res/layout-w600dp-land/activity_view.xml | 21 +- app/src/main/res/layout/activity_setup.xml | 12 +- app/src/main/res/layout/activity_view.xml | 22 +- app/src/main/res/layout/fragment_folder.xml | 11 +- app/src/main/res/layout/include_nav.xml | 87 + app/src/main/res/layout/item_drawer.xml | 34 - .../main/res/layout/item_drawer_expander.xml | 16 - .../main/res/layout/item_drawer_separator.xml | 16 - app/src/main/res/layout/item_nav.xml | 40 + app/src/main/res/values/strings.xml | 1 + 28 files changed, 2939 insertions(+), 568 deletions(-) create mode 100644 app/schemas/eu.faircode.email.DB/75.json create mode 100644 app/src/main/java/eu/faircode/email/AdapterNavAccount.java create mode 100644 app/src/main/java/eu/faircode/email/AdapterNavFolder.java create mode 100644 app/src/main/java/eu/faircode/email/AdapterNavMenu.java delete mode 100644 app/src/main/java/eu/faircode/email/DrawerAdapter.java delete mode 100644 app/src/main/java/eu/faircode/email/DrawerItem.java create mode 100644 app/src/main/java/eu/faircode/email/NavMenuItem.java create mode 100644 app/src/main/java/eu/faircode/email/TupleFolderNav.java create mode 100644 app/src/main/res/layout/include_nav.xml delete mode 100644 app/src/main/res/layout/item_drawer.xml delete mode 100644 app/src/main/res/layout/item_drawer_expander.xml delete mode 100644 app/src/main/res/layout/item_drawer_separator.xml create mode 100644 app/src/main/res/layout/item_nav.xml diff --git a/app/schemas/eu.faircode.email.DB/75.json b/app/schemas/eu.faircode.email.DB/75.json new file mode 100644 index 0000000000..fcc6d85ed2 --- /dev/null +++ b/app/schemas/eu.faircode.email.DB/75.json @@ -0,0 +1,1729 @@ +{ + "formatVersion": 1, + "database": { + "version": 75, + "identityHash": "a9cfd9de68f49c64fe190745124c41ff", + "entities": [ + { + "tableName": "identity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `email` TEXT NOT NULL, `account` INTEGER NOT NULL, `display` TEXT, `color` INTEGER, `signature` TEXT, `auth_type` 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, `realm` TEXT, `use_ip` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `primary` INTEGER NOT NULL, `sender_extra` INTEGER NOT NULL, `replyto` TEXT, `bcc` TEXT, `plain_only` INTEGER NOT NULL, `encrypt` INTEGER NOT NULL, `delivery_receipt` INTEGER NOT NULL, `read_receipt` INTEGER NOT NULL, `store_sent` INTEGER NOT NULL, `sent_folder` INTEGER, `tbd` INTEGER, `state` TEXT, `error` TEXT, `last_connected` INTEGER, 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": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "display", + "columnName": "display", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "auth_type", + "columnName": "auth_type", + "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": "realm", + "columnName": "realm", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "use_ip", + "columnName": "use_ip", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "primary", + "columnName": "primary", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender_extra", + "columnName": "sender_extra", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "replyto", + "columnName": "replyto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bcc", + "columnName": "bcc", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "plain_only", + "columnName": "plain_only", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encrypt", + "columnName": "encrypt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "delivery_receipt", + "columnName": "delivery_receipt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "read_receipt", + "columnName": "read_receipt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "store_sent", + "columnName": "store_sent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sent_folder", + "columnName": "sent_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbd", + "columnName": "tbd", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_connected", + "columnName": "last_connected", + "affinity": "INTEGER", + "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, `auth_type` INTEGER NOT NULL, `pop` 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, `realm` TEXT, `name` TEXT, `signature` TEXT, `color` INTEGER, `order` INTEGER, `synchronize` INTEGER NOT NULL, `ondemand` INTEGER NOT NULL, `primary` INTEGER NOT NULL, `notify` INTEGER NOT NULL, `browse` INTEGER NOT NULL, `swipe_left` INTEGER, `swipe_right` INTEGER, `poll_interval` INTEGER NOT NULL, `prefix` TEXT, `created` INTEGER, `tbd` INTEGER, `state` TEXT, `warning` TEXT, `error` TEXT, `last_connected` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "auth_type", + "columnName": "auth_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pop", + "columnName": "pop", + "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": "realm", + "columnName": "realm", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "signature", + "columnName": "signature", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ondemand", + "columnName": "ondemand", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "primary", + "columnName": "primary", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "browse", + "columnName": "browse", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "swipe_left", + "columnName": "swipe_left", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swipe_right", + "columnName": "swipe_right", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll_interval", + "columnName": "poll_interval", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "prefix", + "columnName": "prefix", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbd", + "columnName": "tbd", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "warning", + "columnName": "warning", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_connected", + "columnName": "last_connected", + "affinity": "INTEGER", + "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, `parent` INTEGER, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `level` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `poll` INTEGER NOT NULL, `download` INTEGER NOT NULL, `subscribed` INTEGER, `sync_days` INTEGER NOT NULL, `keep_days` INTEGER NOT NULL, `display` TEXT, `hide` INTEGER NOT NULL, `collapsed` INTEGER NOT NULL, `unified` INTEGER NOT NULL, `navigation` INTEGER NOT NULL, `notify` INTEGER NOT NULL, `total` INTEGER, `keywords` TEXT, `initialize` INTEGER NOT NULL, `tbc` INTEGER, `tbd` INTEGER, `state` TEXT, `sync_state` TEXT, `error` TEXT, `last_sync` INTEGER, 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": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "level", + "columnName": "level", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synchronize", + "columnName": "synchronize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "download", + "columnName": "download", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subscribed", + "columnName": "subscribed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sync_days", + "columnName": "sync_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keep_days", + "columnName": "keep_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "display", + "columnName": "display", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hide", + "columnName": "hide", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "collapsed", + "columnName": "collapsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unified", + "columnName": "unified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "navigation", + "columnName": "navigation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "keywords", + "columnName": "keywords", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "initialize", + "columnName": "initialize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tbc", + "columnName": "tbc", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbd", + "columnName": "tbd", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sync_state", + "columnName": "sync_state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_sync", + "columnName": "last_sync", + "affinity": "INTEGER", + "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 NOT NULL, `folder` INTEGER NOT NULL, `identity` INTEGER, `extra` TEXT, `replying` INTEGER, `forwarding` INTEGER, `uid` INTEGER, `msgid` TEXT, `references` TEXT, `deliveredto` TEXT, `inreplyto` TEXT, `thread` TEXT, `receipt_request` INTEGER, `receipt_to` TEXT, `dkim` INTEGER, `spf` INTEGER, `dmarc` INTEGER, `avatar` TEXT, `sender` TEXT, `from` TEXT, `to` TEXT, `cc` TEXT, `bcc` TEXT, `reply` TEXT, `list_post` TEXT, `headers` TEXT, `raw` INTEGER, `subject` TEXT, `size` INTEGER, `content` INTEGER NOT NULL, `preview` TEXT, `sent` INTEGER, `received` INTEGER NOT NULL, `stored` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `answered` INTEGER NOT NULL, `flagged` INTEGER NOT NULL, `flags` TEXT, `keywords` TEXT, `ui_seen` INTEGER NOT NULL, `ui_answered` INTEGER NOT NULL, `ui_flagged` INTEGER NOT NULL, `ui_hide` INTEGER NOT NULL, `ui_found` INTEGER NOT NULL, `ui_ignored` INTEGER NOT NULL, `ui_browsed` INTEGER NOT NULL, `ui_snoozed` INTEGER, `revision` INTEGER, `revisions` INTEGER, `warning` TEXT, `error` TEXT, `last_attempt` INTEGER, 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 SET NULL , FOREIGN KEY(`replying`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`forwarding`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": true + }, + { + "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": "forwarding", + "columnName": "forwarding", + "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": "receipt_request", + "columnName": "receipt_request", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "receipt_to", + "columnName": "receipt_to", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dkim", + "columnName": "dkim", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "spf", + "columnName": "spf", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dmarc", + "columnName": "dmarc", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "avatar", + "columnName": "avatar", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sender", + "columnName": "sender", + "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": "list_post", + "columnName": "list_post", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "headers", + "columnName": "headers", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "raw", + "columnName": "raw", + "affinity": "INTEGER", + "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": "answered", + "columnName": "answered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flagged", + "columnName": "flagged", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flags", + "columnName": "flags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "keywords", + "columnName": "keywords", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ui_seen", + "columnName": "ui_seen", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_answered", + "columnName": "ui_answered", + "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": "ui_browsed", + "columnName": "ui_browsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ui_snoozed", + "columnName": "ui_snoozed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "revision", + "columnName": "revision", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "revisions", + "columnName": "revisions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "warning", + "columnName": "warning", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "last_attempt", + "columnName": "last_attempt", + "affinity": "INTEGER", + "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_folder_uid", + "unique": true, + "columnNames": [ + "folder", + "uid" + ], + "createSql": "CREATE UNIQUE INDEX `index_message_folder_uid` ON `${TABLE_NAME}` (`folder`, `uid`)" + }, + { + "name": "index_message_msgid", + "unique": false, + "columnNames": [ + "msgid" + ], + "createSql": "CREATE INDEX `index_message_msgid` ON `${TABLE_NAME}` (`msgid`)" + }, + { + "name": "index_message_thread", + "unique": false, + "columnNames": [ + "thread" + ], + "createSql": "CREATE INDEX `index_message_thread` ON `${TABLE_NAME}` (`thread`)" + }, + { + "name": "index_message_sender", + "unique": false, + "columnNames": [ + "sender" + ], + "createSql": "CREATE INDEX `index_message_sender` ON `${TABLE_NAME}` (`sender`)" + }, + { + "name": "index_message_received", + "unique": false, + "columnNames": [ + "received" + ], + "createSql": "CREATE INDEX `index_message_received` ON `${TABLE_NAME}` (`received`)" + }, + { + "name": "index_message_subject", + "unique": false, + "columnNames": [ + "subject" + ], + "createSql": "CREATE INDEX `index_message_subject` ON `${TABLE_NAME}` (`subject`)" + }, + { + "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_flagged", + "unique": false, + "columnNames": [ + "ui_flagged" + ], + "createSql": "CREATE INDEX `index_message_ui_flagged` ON `${TABLE_NAME}` (`ui_flagged`)" + }, + { + "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`)" + }, + { + "name": "index_message_ui_browsed", + "unique": false, + "columnNames": [ + "ui_browsed" + ], + "createSql": "CREATE INDEX `index_message_ui_browsed` ON `${TABLE_NAME}` (`ui_browsed`)" + }, + { + "name": "index_message_ui_snoozed", + "unique": false, + "columnNames": [ + "ui_snoozed" + ], + "createSql": "CREATE INDEX `index_message_ui_snoozed` ON `${TABLE_NAME}` (`ui_snoozed`)" + } + ], + "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": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "identity" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "message", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "replying" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "message", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "forwarding" + ], + "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, `disposition` TEXT, `cid` TEXT, `encryption` INTEGER, `size` INTEGER, `progress` INTEGER, `available` INTEGER NOT NULL, `error` TEXT, 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": "disposition", + "columnName": "disposition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cid", + "columnName": "cid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryption", + "columnName": "encryption", + "affinity": "INTEGER", + "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 + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + } + ], + "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, `account` INTEGER, `folder` INTEGER NOT NULL, `message` INTEGER, `name` TEXT NOT NULL, `args` TEXT NOT NULL, `created` INTEGER NOT NULL, `error` TEXT, 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": "account", + "columnName": "account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folder", + "columnName": "folder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "args", + "columnName": "args", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "TEXT", + "notNull": false + } + ], + "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`)" + }, + { + "name": "index_operation_name", + "unique": false, + "columnNames": [ + "name" + ], + "createSql": "CREATE INDEX `index_operation_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [ + { + "table": "folder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "folder" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "message", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "message" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "contact", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` INTEGER NOT NULL, `type` INTEGER NOT NULL, `email` TEXT NOT NULL, `name` TEXT, `avatar` TEXT, `times_contacted` INTEGER NOT NULL, `first_contacted` INTEGER NOT NULL, `last_contacted` INTEGER NOT NULL, `state` INTEGER NOT NULL, 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": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatar", + "columnName": "avatar", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "times_contacted", + "columnName": "times_contacted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "first_contacted", + "columnName": "first_contacted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "last_contacted", + "columnName": "last_contacted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_contact_account_type_email", + "unique": true, + "columnNames": [ + "account", + "type", + "email" + ], + "createSql": "CREATE UNIQUE INDEX `index_contact_account_type_email` ON `${TABLE_NAME}` (`account`, `type`, `email`)" + }, + { + "name": "index_contact_email", + "unique": false, + "columnNames": [ + "email" + ], + "createSql": "CREATE INDEX `index_contact_email` ON `${TABLE_NAME}` (`email`)" + }, + { + "name": "index_contact_name", + "unique": false, + "columnNames": [ + "name" + ], + "createSql": "CREATE INDEX `index_contact_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_contact_avatar", + "unique": false, + "columnNames": [ + "avatar" + ], + "createSql": "CREATE INDEX `index_contact_avatar` ON `${TABLE_NAME}` (`avatar`)" + }, + { + "name": "index_contact_times_contacted", + "unique": false, + "columnNames": [ + "times_contacted" + ], + "createSql": "CREATE INDEX `index_contact_times_contacted` ON `${TABLE_NAME}` (`times_contacted`)" + }, + { + "name": "index_contact_last_contacted", + "unique": false, + "columnNames": [ + "last_contacted" + ], + "createSql": "CREATE INDEX `index_contact_last_contacted` ON `${TABLE_NAME}` (`last_contacted`)" + }, + { + "name": "index_contact_state", + "unique": false, + "columnNames": [ + "state" + ], + "createSql": "CREATE INDEX `index_contact_state` ON `${TABLE_NAME}` (`state`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "answer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `hide` INTEGER NOT NULL, `text` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hide", + "columnName": "hide", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "rule", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `folder` INTEGER NOT NULL, `name` TEXT NOT NULL, `order` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `stop` INTEGER NOT NULL, `condition` TEXT NOT NULL, `action` TEXT NOT NULL, FOREIGN KEY(`folder`) REFERENCES `folder`(`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": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stop", + "columnName": "stop", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_rule_folder", + "unique": false, + "columnNames": [ + "folder" + ], + "createSql": "CREATE INDEX `index_rule_folder` ON `${TABLE_NAME}` (`folder`)" + }, + { + "name": "index_rule_order", + "unique": false, + "columnNames": [ + "order" + ], + "createSql": "CREATE INDEX `index_rule_order` ON `${TABLE_NAME}` (`order`)" + } + ], + "foreignKeys": [ + { + "table": "folder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "folder" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "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, \"a9cfd9de68f49c64fe190745124c41ff\")" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index 0a17ae5417..edc7339b67 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -37,8 +37,6 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; import android.widget.RadioGroup; import android.widget.TextView; @@ -52,6 +50,8 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Observer; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.textfield.TextInputLayout; @@ -88,8 +88,7 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On private DrawerLayout drawerLayout; private ActionBarDrawerToggle drawerToggle; private ConstraintLayout drawerContainer; - private ListView drawerMenu; - private DrawerAdapter drawerArray; + private RecyclerView rvMenu; private boolean hasAccount; private String password; @@ -134,82 +133,98 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On drawerLayout.addDrawerListener(drawerToggle); drawerContainer = findViewById(R.id.drawer_container); - drawerMenu = drawerContainer.findViewById(R.id.drawer_menu); + rvMenu = drawerContainer.findViewById(R.id.rvMenu); + rvMenu.setLayoutManager(new LinearLayoutManager(this)); + final AdapterNavMenu adapter = new AdapterNavMenu(this, this); + rvMenu.setAdapter(adapter); - drawerArray = new DrawerAdapter(this, false); - drawerMenu.setAdapter(drawerArray); + PackageManager pm = getPackageManager(); + final List menus = new ArrayList<>(); - drawerMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() { + menus.add(new NavMenuItem(R.drawable.baseline_archive_24, R.string.title_setup_export, new Runnable() { @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - DrawerItem item = drawerArray.getItem(position); - if (item == null) - return; - - switch (item.getMenuId()) { - case R.string.title_setup_export: - onMenuExport(); - break; - case R.string.title_setup_import: - onMenuImport(); - break; - case R.string.title_setup_theme: - onMenuTheme(); - break; - case R.string.title_setup_notifications: - onManageNotifications(); - break; - case R.string.title_setup_advanced: - onMenuOptions(); - break; - case R.string.menu_contacts: - onMenuContacts(); - break; - case R.string.menu_legend: - onMenuLegend(); - break; - case R.string.menu_faq: - onMenuFAQ(); - break; - case R.string.menu_privacy: - onMenuPrivacy(); - break; - case R.string.menu_about: - onMenuAbout(); - break; - } - + public void run() { drawerLayout.closeDrawer(drawerContainer); + onMenuExport(); } - }); - - List items = new ArrayList<>(); + })); - PackageManager pm = getPackageManager(); - if (getIntentExport().resolveActivity(pm) != null) - items.add(new DrawerItem(-1, R.drawable.baseline_archive_24, R.string.title_setup_export)); - if (getIntentImport().resolveActivity(pm) != null) - items.add(new DrawerItem(-2, R.drawable.baseline_unarchive_24, R.string.title_setup_import)); + menus.add(new NavMenuItem(R.drawable.baseline_unarchive_24, R.string.title_setup_import, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuImport(); + } + })); - items.add(new DrawerItem(-3)); + menus.add(new NavMenuItem(R.drawable.baseline_palette_24, R.string.title_setup_theme, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuTheme(); + } + })); if (getIntentNotifications(this).resolveActivity(pm) != null) - items.add(new DrawerItem(-4, R.drawable.baseline_notifications_24, R.string.title_setup_notifications)); + menus.add(new NavMenuItem(R.drawable.baseline_notifications_24, R.string.title_setup_notifications, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onManageNotifications(); + } + })); - items.add(new DrawerItem(-8, R.drawable.baseline_palette_24, R.string.title_setup_theme)); - items.add(new DrawerItem(-9, R.drawable.baseline_settings_applications_24, R.string.title_setup_advanced)); - items.add(new DrawerItem(-10, R.drawable.baseline_person_24, R.string.menu_contacts)); + menus.add(new NavMenuItem(R.drawable.baseline_settings_applications_24, R.string.title_setup_advanced, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuOptions(); + } + })); - items.add(new DrawerItem(-11)); + menus.add(new NavMenuItem(R.drawable.baseline_person_24, R.string.menu_contacts, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuContacts(); + } + })); - items.add(new DrawerItem(-12, R.drawable.baseline_help_24, R.string.menu_legend)); - if (Helper.getIntentFAQ().resolveActivity(getPackageManager()) != null) - items.add(new DrawerItem(-13, R.drawable.baseline_question_answer_24, R.string.menu_faq)); - if (Helper.getIntentPrivacy().resolveActivity(getPackageManager()) != null) - items.add(new DrawerItem(-14, R.drawable.baseline_account_box_24, R.string.menu_privacy)); - items.add(new DrawerItem(-15, R.drawable.baseline_info_24, R.string.menu_about)); + menus.add(new NavMenuItem(R.drawable.baseline_help_24, R.string.menu_legend, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuLegend(); + } + })); + + if (Helper.getIntentFAQ().resolveActivity(pm) != null) + menus.add(new NavMenuItem(R.drawable.baseline_question_answer_24, R.string.menu_faq, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuFAQ(); + } + })); + + if (Helper.getIntentPrivacy().resolveActivity(pm) != null) + menus.add(new NavMenuItem(R.drawable.baseline_account_box_24, R.string.menu_privacy, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuPrivacy(); + } + })); + + menus.add(new NavMenuItem(R.drawable.baseline_info_24, R.string.menu_about, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuAbout(); + } + })); - drawerArray.set(items); + adapter.set(menus); getSupportFragmentManager().addOnBackStackChangedListener(this); diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index bcb6b876fb..75a31f8eb3 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -44,13 +44,12 @@ import android.view.View; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.widget.AdapterView; -import android.widget.ListView; +import android.widget.ImageView; +import android.widget.ScrollView; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Group; import androidx.documentfile.provider.DocumentFile; import androidx.drawerlayout.widget.DrawerLayout; @@ -61,6 +60,8 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Observer; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; @@ -84,11 +85,9 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.URL; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.Objects; import java.util.Properties; import javax.mail.Session; @@ -102,9 +101,13 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB private Group grpPane; private DrawerLayout drawerLayout; private ActionBarDrawerToggle drawerToggle; - private ConstraintLayout drawerContainer; - private ListView drawerMenu; - private DrawerAdapter drawerArray; + private ScrollView drawerContainer; + private RecyclerView rvAccount; + private RecyclerView rvFolder; + private RecyclerView rvMenu; + private ImageView ivExpander; + private RecyclerView rvMenuExtra1; + private RecyclerView rvMenuExtra2; private long message = -1; private long attachment = -1; @@ -112,8 +115,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB private WebView printWebView = null; private OpenPgpServiceConnection pgpService; - private NumberFormat nf = NumberFormat.getNumberInstance(); - static final int REQUEST_UNIFIED = 1; static final int REQUEST_WHY = 2; static final int REQUEST_THREAD = 3; @@ -151,7 +152,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); startup = prefs.getString("startup", "unified"); view = LayoutInflater.from(this).inflate(R.layout.activity_view, null); @@ -178,216 +179,216 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB drawerLayout.addDrawerListener(drawerToggle); drawerContainer = findViewById(R.id.drawer_container); - drawerMenu = drawerContainer.findViewById(R.id.drawer_menu); - boolean minimal = prefs.getBoolean("minimal", false); - drawerArray = new DrawerAdapter(this, minimal); + rvAccount = drawerContainer.findViewById(R.id.rvAccount); + rvAccount.setLayoutManager(new LinearLayoutManager(this)); + final AdapterNavAccount aadapter = new AdapterNavAccount(this, this); + rvAccount.setAdapter(aadapter); - drawerMenu.setAdapter(drawerArray); + rvFolder = drawerContainer.findViewById(R.id.rvFolder); + rvFolder.setLayoutManager(new LinearLayoutManager(this)); + final AdapterNavFolder fadapter = new AdapterNavFolder(this, this); + rvFolder.setAdapter(fadapter); - drawerMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - DrawerItem item = drawerArray.getItem(position); - if (item == null) - return; - Log.i("Navigation id=" + item.getId() + " menu=" + item.getMenuId()); + rvMenu = drawerContainer.findViewById(R.id.rvMenu); + rvMenu.setLayoutManager(new LinearLayoutManager(this)); + final AdapterNavMenu madapter = new AdapterNavMenu(this, this); + rvMenu.setAdapter(madapter); - switch (item.getMenuId()) { - case R.string.menu_operations: - onMenuOperations(); - break; - case R.string.menu_answers: - onMenuAnswers(); - break; - case R.string.menu_setup: - onMenuSetup(); - break; - case R.string.title_legend_expander: - onMenuCollapse(); - return; - case R.string.menu_legend: - onMenuLegend(); - break; - case R.string.menu_faq: - onMenuFAQ(); - break; - case R.string.menu_privacy: - onMenuPrivacy(); - break; - case R.string.menu_about: - onMenuAbout(); - break; - case R.string.menu_pro: - onMenuPro(); - break; - case R.string.menu_invite: - onMenuInvite(); - break; - case R.string.menu_rate: - onMenuRate(); - break; - case R.string.menu_other: - onMenuOtherApps(); - break; - default: - long account = item.getId(); - if (account > 0) - onMenuFolders(account); - else - onMenuOutbox(); - } + ivExpander = drawerContainer.findViewById(R.id.ivExpander); - drawerLayout.closeDrawer(drawerContainer); - } - }); + rvMenuExtra1 = drawerContainer.findViewById(R.id.rvMenuExtra1); + rvMenuExtra1.setLayoutManager(new LinearLayoutManager(this)); + final AdapterNavMenu e1adapter = new AdapterNavMenu(this, this); + rvMenuExtra1.setAdapter(e1adapter); - drawerMenu.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - DrawerItem item = drawerArray.getItem(position); - if (item == null) - return false; - - switch (item.getMenuId()) { - case R.string.menu_operations: - onShowLog(); - break; - case R.string.menu_setup: - onReset(); - break; - case R.string.menu_faq: - onDebugInfo(); - break; - case R.string.menu_privacy: - onCleanup(); - break; - case R.string.menu_about: - if (Helper.isPlayStoreInstall(ActivityView.this)) - return false; - checkUpdate(true); - break; - default: - long account = item.getId(); - if (account < 0) - return false; - else - onMenuInbox(account); - } + rvMenuExtra2 = drawerContainer.findViewById(R.id.rvMenuExtra2); + rvMenuExtra2.setLayoutManager(new LinearLayoutManager(this)); + final AdapterNavMenu e2adapter = new AdapterNavMenu(this, this); + rvMenuExtra2.setAdapter(e2adapter); - drawerLayout.closeDrawer(drawerContainer); - return true; + boolean minimal = prefs.getBoolean("minimal", false); + rvMenuExtra1.setVisibility(minimal ? View.GONE : View.VISIBLE); + rvMenuExtra2.setVisibility(minimal ? View.GONE : View.VISIBLE); + ivExpander.setImageLevel(minimal ? 1 /* more */ : 0 /* less */); + + ivExpander.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean minimal = !prefs.getBoolean("minimal", false); + prefs.edit().putBoolean("minimal", minimal).apply(); + rvMenuExtra1.setVisibility(minimal ? View.GONE : View.VISIBLE); + rvMenuExtra2.setVisibility(minimal ? View.GONE : View.VISIBLE); + ivExpander.setImageLevel(minimal ? 1 /* more */ : 0 /* less */); } }); getSupportFragmentManager().addOnBackStackChangedListener(this); - DB db = DB.getInstance(this); - - db.account().liveAccountsEx(false).observe(this, new Observer>() { - private List last = new ArrayList<>(); + final List menus = new ArrayList<>(); + final NavMenuItem navOperations = new NavMenuItem(R.drawable.baseline_list_24, R.string.menu_operations, new Runnable() { @Override - public void onChanged(@Nullable List accounts) { - if (accounts == null) - accounts = new ArrayList<>(); - - boolean changed = false; - if (last.size() == accounts.size()) { - for (int i = 0; i < accounts.size(); i++) { - TupleAccountEx other = last.get(i); - TupleAccountEx account = accounts.get(i); - if (!account.id.equals(other.id) || - !Objects.equals(account.name, other.name) || - !Objects.equals(account.color, other.color) || - !Objects.equals(account.state, other.state) || - account.unseen != other.unseen || - account.unsent != other.unsent || - account.operations != other.operations) { - changed = true; - break; - } - } - } else - changed = true; - - if (!changed) - return; - last = accounts; - - List items = new ArrayList<>(); - - int unsent = 0; - int pending = 0; - for (TupleAccountEx account : accounts) { - String title; - if (account.unseen > 0) - title = getString(R.string.title_name_count, account.name, nf.format(account.unseen)); - else - title = account.name; - items.add(new DrawerItem(account.id, - "connected".equals(account.state) - ? account.primary ? R.drawable.baseline_folder_special_24 : R.drawable.baseline_folder_24 - : R.drawable.baseline_folder_open_24, - title, account.color, account.unseen > 0)); - unsent += account.unsent; - pending += account.operations; - } + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuOperations(); + } + }, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onShowLog(); + } + }); - items.add(new DrawerItem(-1)); + menus.add(navOperations); - String outbox; - if (unsent > 0) - outbox = getString(R.string.title_name_count, getString(R.string.title_folder_outbox), nf.format(unsent)); - else - outbox = getString(R.string.title_folder_outbox); - items.add(new DrawerItem(-2, R.drawable.baseline_send_24, outbox, null, unsent > 0)); + menus.add(new NavMenuItem(R.drawable.baseline_reply_24, R.string.menu_answers, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuAnswers(); + } + })); - String operations; - if (pending == 0) - operations = getString(R.string.menu_operations); - else - operations = getString(R.string.title_name_count, - getString(R.string.menu_operations), - nf.format(pending)); - items.add(new DrawerItem(-3, R.string.menu_operations, R.drawable.baseline_list_24, operations, pending > 0)); + menus.add(new NavMenuItem(R.drawable.baseline_settings_applications_24, R.string.menu_setup, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuSetup(); + } + }, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onReset(); + } + })); - items.add(new DrawerItem(-4, R.drawable.baseline_reply_24, R.string.menu_answers)); + madapter.set(menus); - items.add(new DrawerItem(-5, R.drawable.baseline_settings_applications_24, R.string.menu_setup)); - items.add(new DrawerItem(-6)); - items.add(new DrawerItem(-7, R.string.title_legend_expander)); - items.add(new DrawerItem(-8, R.drawable.baseline_help_24, R.string.menu_legend).setCollapsible()); + List extra1 = new ArrayList<>(); - if (Helper.getIntentFAQ().resolveActivity(getPackageManager()) != null) - items.add(new DrawerItem(-9, R.drawable.baseline_question_answer_24, R.string.menu_faq).setCollapsible()); + extra1.add(new NavMenuItem(R.drawable.baseline_help_24, R.string.menu_legend, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuLegend(); + } + })); + + if (Helper.getIntentFAQ().resolveActivity(getPackageManager()) != null) + extra1.add(new NavMenuItem(R.drawable.baseline_question_answer_24, R.string.menu_faq, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuFAQ(); + } + }, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onDebugInfo(); + } + })); + + if (Helper.getIntentPrivacy().resolveActivity(getPackageManager()) != null) + extra1.add(new NavMenuItem(R.drawable.baseline_account_box_24, R.string.menu_privacy, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuPrivacy(); + } + }, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onCleanup(); + } + })); - if (Helper.getIntentPrivacy().resolveActivity(getPackageManager()) != null) - items.add(new DrawerItem(-10, R.drawable.baseline_account_box_24, R.string.menu_privacy).setCollapsible()); + extra1.add(new NavMenuItem(R.drawable.baseline_info_24, R.string.menu_about, new Runnable() { + @Override + public void run() { + onMenuAbout(); + } + }, new Runnable() { + @Override + public void run() { + if (Helper.isPlayStoreInstall(ActivityView.this)) { + drawerLayout.closeDrawer(drawerContainer); + checkUpdate(true); + } + } + })); - items.add(new DrawerItem(-11, R.drawable.baseline_info_24, R.string.menu_about).setCollapsible()); + e1adapter.set(extra1); - boolean pro = (getIntentPro() == null || getIntentPro().resolveActivity(getPackageManager()) != null); - boolean invite = (getIntentInvite().resolveActivity(getPackageManager()) != null); - boolean rate = (getIntentRate().resolveActivity(getPackageManager()) != null); - boolean other = (getIntentOtherApps().resolveActivity(getPackageManager()) != null); + List extra2 = new ArrayList<>(); - if (pro || invite || rate || other) - items.add(new DrawerItem(-12).setCollapsible()); + if (getIntentPro() == null || getIntentPro().resolveActivity(getPackageManager()) != null) + extra2.add(new NavMenuItem(R.drawable.baseline_monetization_on_24, R.string.menu_pro, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuPro(); + } + })); + + if ((getIntentInvite().resolveActivity(getPackageManager()) != null)) + extra2.add(new NavMenuItem(R.drawable.baseline_people_24, R.string.menu_invite, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuInvite(); + } + })); + + if (getIntentRate().resolveActivity(getPackageManager()) != null) + extra2.add(new NavMenuItem(R.drawable.baseline_star_24, R.string.menu_rate, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuRate(); + } + })); + + if (getIntentOtherApps().resolveActivity(getPackageManager()) != null) + extra2.add(new NavMenuItem(R.drawable.baseline_get_app_24, R.string.menu_other, new Runnable() { + @Override + public void run() { + drawerLayout.closeDrawer(drawerContainer); + onMenuOtherApps(); + } + })); - if (pro) - items.add(new DrawerItem(-13, R.drawable.baseline_monetization_on_24, R.string.menu_pro).setCollapsible()); + e2adapter.set(extra2); - if (invite) - items.add(new DrawerItem(-14, R.drawable.baseline_people_24, R.string.menu_invite).setCollapsible()); + DB db = DB.getInstance(this); - if (rate) - items.add(new DrawerItem(-15, R.drawable.baseline_star_24, R.string.menu_rate).setCollapsible()); + db.account().liveAccountsEx(false).observe(this, new Observer>() { + @Override + public void onChanged(@Nullable List accounts) { + if (accounts == null) + accounts = new ArrayList<>(); + aadapter.set(accounts); + } + }); - if (other) - items.add(new DrawerItem(-16, R.drawable.baseline_get_app_24, R.string.menu_other).setCollapsible()); + db.folder().liveNavigation().observe(this, new Observer>() { + @Override + public void onChanged(List folders) { + if (folders == null) + folders = new ArrayList<>(); + fadapter.set(folders); + } + }); - drawerArray.set(items); + db.operation().liveCount().observe(this, new Observer() { + @Override + public void onChanged(Integer count) { + navOperations.setCount(count); + madapter.notifyDataSetChanged(); } }); @@ -961,8 +962,8 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean minimal = !prefs.getBoolean("minimal", false); prefs.edit().putBoolean("minimal", minimal).apply(); - drawerArray.set(minimal); - drawerArray.notifyDataSetChanged(); + //drawerArray.set(minimal); + //drawerArray.notifyDataSetChanged(); } private void onMenuLegend() { diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 61efdf8d7e..4e2be672ca 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -919,10 +919,10 @@ public class AdapterMessage extends RecyclerView.Adapter. + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.RecyclerView; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class AdapterNavAccount extends RecyclerView.Adapter { + private Context context; + private LifecycleOwner owner; + private LayoutInflater inflater; + + private List items = new ArrayList<>(); + + private NumberFormat nf = NumberFormat.getNumberInstance(); + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private View view; + private ImageView ivItem; + private TextView tvItem; + + ViewHolder(View itemView) { + super(itemView); + + view = itemView.findViewById(R.id.clItem); + ivItem = itemView.findViewById(R.id.ivItem); + tvItem = itemView.findViewById(R.id.tvItem); + } + + private void wire() { + view.setOnClickListener(this); + } + + private void unwire() { + view.setOnClickListener(null); + } + + private void bindTo(TupleAccountEx account) { + ivItem.setImageResource("connected".equals(account.state) + ? account.primary ? R.drawable.baseline_folder_special_24 : R.drawable.baseline_folder_24 + : R.drawable.baseline_folder_open_24); + if (account.color == null) + ivItem.clearColorFilter(); + else + ivItem.setColorFilter(account.color); + + if (account.unseen == 0) + tvItem.setText(account.name); + else + tvItem.setText(context.getString(R.string.title_name_count, + account.name, nf.format(account.unseen))); + + tvItem.setTextColor(Helper.resolveColor(context, + account.unseen == 0 ? android.R.attr.textColorSecondary : R.attr.colorUnread)); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + TupleAccountEx account = items.get(pos); + if (account == null) + return; + + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + lbm.sendBroadcast( + new Intent(ActivityView.ACTION_VIEW_FOLDERS) + .putExtra("id", account.id)); + } + } + + AdapterNavAccount(Context context, LifecycleOwner owner) { + this.context = context; + this.owner = owner; + this.inflater = LayoutInflater.from(context); + setHasStableIds(true); + } + + public void set(@NonNull List accounts) { + Log.i("Set nav accounts=" + accounts.size()); + + DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, accounts), false); + + items = accounts; + + diff.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.i("Inserted @" + position + " #" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.i("Removed @" + position + " #" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.i("Moved " + fromPosition + ">" + toPosition); + } + + @Override + public void onChanged(int position, int count, Object payload) { + Log.i("Changed @" + position + " #" + count); + } + }); + diff.dispatchUpdatesTo(this); + } + + private class DiffCallback extends DiffUtil.Callback { + private List prev = new ArrayList<>(); + private List next = new ArrayList<>(); + + DiffCallback(List prev, List next) { + this.prev.addAll(prev); + this.next.addAll(next); + } + + @Override + public int getOldListSize() { + return prev.size(); + } + + @Override + public int getNewListSize() { + return next.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + TupleAccountEx a1 = prev.get(oldItemPosition); + TupleAccountEx a2 = next.get(newItemPosition); + return a1.id.equals(a2.id); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + TupleAccountEx a1 = prev.get(oldItemPosition); + TupleAccountEx a2 = next.get(newItemPosition); + return Objects.equals(a1.name, a2.name) && + Objects.equals(a1.color, a2.color) && + a1.unseen == a2.unseen && + Objects.equals(a1.state, a2.state); + } + } + + @Override + public long getItemId(int position) { + return items.get(position).id; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.item_nav, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + TupleAccountEx account = items.get(position); + holder.bindTo(account); + holder.wire(); + } +} diff --git a/app/src/main/java/eu/faircode/email/AdapterNavFolder.java b/app/src/main/java/eu/faircode/email/AdapterNavFolder.java new file mode 100644 index 0000000000..e1c389aebe --- /dev/null +++ b/app/src/main/java/eu/faircode/email/AdapterNavFolder.java @@ -0,0 +1,225 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.RecyclerView; + +import java.text.Collator; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +public class AdapterNavFolder extends RecyclerView.Adapter { + private Context context; + private LifecycleOwner owner; + private LayoutInflater inflater; + + private List items = new ArrayList<>(); + + private NumberFormat nf = NumberFormat.getNumberInstance(); + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private View view; + private ImageView ivItem; + private TextView tvItem; + + ViewHolder(View itemView) { + super(itemView); + + view = itemView.findViewById(R.id.clItem); + ivItem = itemView.findViewById(R.id.ivItem); + tvItem = itemView.findViewById(R.id.tvItem); + } + + private void wire() { + view.setOnClickListener(this); + } + + private void unwire() { + view.setOnClickListener(null); + } + + private void bindTo(TupleFolderNav folder) { + ivItem.setImageResource("connected".equals(folder.state) + ? R.drawable.baseline_folder_24 + : R.drawable.baseline_folder_open_24); + if (folder.color == null) + ivItem.clearColorFilter(); + else + ivItem.setColorFilter(folder.color); + + int count = (EntityFolder.OUTBOX.equals(folder.type) ? folder.operations : folder.unseen); + + if (count == 0) + tvItem.setText(folder.getDisplayName(context)); + else + tvItem.setText(context.getString(R.string.title_name_count, + folder.getDisplayName(context), nf.format(count))); + + tvItem.setTextColor(Helper.resolveColor(context, + count == 0 ? android.R.attr.textColorSecondary : R.attr.colorUnread)); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + TupleFolderNav folder = items.get(pos); + if (folder == null) + return; + + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + lbm.sendBroadcast( + new Intent(ActivityView.ACTION_VIEW_MESSAGES) + .putExtra("account", folder.account) + .putExtra("folder", folder.id)); + } + } + + AdapterNavFolder(Context context, LifecycleOwner owner) { + this.context = context; + this.owner = owner; + this.inflater = LayoutInflater.from(context); + setHasStableIds(true); + } + + public void set(@NonNull List folders) { + Log.i("Set nav folders=" + folders.size()); + + final Collator collator = Collator.getInstance(Locale.getDefault()); + collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc + + Collections.sort(folders, new Comparator() { + @Override + public int compare(EntityFolder f1, EntityFolder f2) { + int o = Boolean.compare(EntityFolder.OUTBOX.equals(f1.type), EntityFolder.OUTBOX.equals(f2.type)); + if (o != 0) + return o; + + String name1 = f1.getDisplayName(context); + String name2 = f2.getDisplayName(context); + return collator.compare(name1, name2); + } + }); + + DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, folders), false); + + items = folders; + + diff.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.i("Inserted @" + position + " #" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.i("Removed @" + position + " #" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.i("Moved " + fromPosition + ">" + toPosition); + } + + @Override + public void onChanged(int position, int count, Object payload) { + Log.i("Changed @" + position + " #" + count); + } + }); + diff.dispatchUpdatesTo(this); + } + + private class DiffCallback extends DiffUtil.Callback { + private List prev = new ArrayList<>(); + private List next = new ArrayList<>(); + + DiffCallback(List prev, List next) { + this.prev.addAll(prev); + this.next.addAll(next); + } + + @Override + public int getOldListSize() { + return prev.size(); + } + + @Override + public int getNewListSize() { + return next.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + TupleFolderNav f1 = prev.get(oldItemPosition); + TupleFolderNav f2 = next.get(newItemPosition); + return f1.id.equals(f2.id); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + TupleFolderNav f1 = prev.get(oldItemPosition); + TupleFolderNav f2 = next.get(newItemPosition); + return f1.equals(f2); + } + } + + @Override + public long getItemId(int position) { + return items.get(position).id; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.item_nav, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + TupleFolderNav folder = items.get(position); + holder.bindTo(folder); + holder.wire(); + } +} diff --git a/app/src/main/java/eu/faircode/email/AdapterNavMenu.java b/app/src/main/java/eu/faircode/email/AdapterNavMenu.java new file mode 100644 index 0000000000..473c3ccebe --- /dev/null +++ b/app/src/main/java/eu/faircode/email/AdapterNavMenu.java @@ -0,0 +1,200 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.RecyclerView; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +public class AdapterNavMenu extends RecyclerView.Adapter { + private Context context; + private LifecycleOwner owner; + private LayoutInflater inflater; + + private List items = new ArrayList<>(); + + private NumberFormat nf = NumberFormat.getNumberInstance(); + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + private View view; + private ImageView ivItem; + private TextView tvItem; + + ViewHolder(View itemView) { + super(itemView); + + view = itemView.findViewById(R.id.clItem); + ivItem = itemView.findViewById(R.id.ivItem); + tvItem = itemView.findViewById(R.id.tvItem); + } + + private void wire() { + view.setOnClickListener(this); + view.setOnLongClickListener(this); + } + + private void unwire() { + view.setOnClickListener(null); + view.setOnLongClickListener(null); + } + + private void bindTo(NavMenuItem menu) { + ivItem.setImageResource(menu.getIcon()); + + if (menu.getCount() == null) + tvItem.setText(menu.getTitle()); + else + tvItem.setText(context.getString(R.string.title_name_count, + context.getString(menu.getTitle()), nf.format(menu.getCount()))); + + tvItem.setTextColor(Helper.resolveColor(context, + menu.getCount() == null ? android.R.attr.textColorSecondary : R.attr.colorUnread)); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + NavMenuItem menu = items.get(pos); + menu.onClick(); + } + + @Override + public boolean onLongClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return false; + + NavMenuItem menu = items.get(pos); + return menu.onLongClick(); + } + } + + AdapterNavMenu(Context context, LifecycleOwner owner) { + this.context = context; + this.owner = owner; + this.inflater = LayoutInflater.from(context); + setHasStableIds(true); + } + + public void set(@NonNull List menus) { + Log.i("Set nav menus=" + menus.size()); + + DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, menus), false); + + items = menus; + + diff.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.i("Inserted @" + position + " #" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.i("Removed @" + position + " #" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.i("Moved " + fromPosition + ">" + toPosition); + } + + @Override + public void onChanged(int position, int count, Object payload) { + Log.i("Changed @" + position + " #" + count); + } + }); + diff.dispatchUpdatesTo(this); + } + + private class DiffCallback extends DiffUtil.Callback { + private List prev = new ArrayList<>(); + private List next = new ArrayList<>(); + + DiffCallback(List prev, List next) { + this.prev.addAll(prev); + this.next.addAll(next); + } + + @Override + public int getOldListSize() { + return prev.size(); + } + + @Override + public int getNewListSize() { + return next.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + NavMenuItem m1 = prev.get(oldItemPosition); + NavMenuItem m2 = next.get(newItemPosition); + return m1.getTitle() == m2.getTitle(); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + NavMenuItem m1 = prev.get(oldItemPosition); + NavMenuItem m2 = next.get(newItemPosition); + return m1.equals(m2); + } + } + + @Override + public long getItemId(int position) { + return items.get(position).getTitle(); + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.item_nav, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + NavMenuItem menu = items.get(position); + holder.bindTo(menu); + holder.wire(); + } +} diff --git a/app/src/main/java/eu/faircode/email/DB.java b/app/src/main/java/eu/faircode/email/DB.java index 07e847bc4f..4dd73c3d18 100644 --- a/app/src/main/java/eu/faircode/email/DB.java +++ b/app/src/main/java/eu/faircode/email/DB.java @@ -51,7 +51,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory; // https://developer.android.com/topic/libraries/architecture/room.html @Database( - version = 74, + version = 75, entities = { EntityIdentity.class, EntityAccount.class, @@ -766,6 +766,13 @@ public abstract class DB extends RoomDatabase { db.execSQL("ALTER TABLE `folder` ADD COLUMN `subscribed` INTEGER"); } }) + .addMigrations(new Migration(74, 75) { + @Override + public void migrate(SupportSQLiteDatabase db) { + Log.i("DB migration from version " + startVersion + " to " + endVersion); + db.execSQL("ALTER TABLE `folder` ADD COLUMN `navigation` INTEGER NOT NULL DEFAULT 0"); + } + }) .build(); } diff --git a/app/src/main/java/eu/faircode/email/DaoAccount.java b/app/src/main/java/eu/faircode/email/DaoAccount.java index 4332474e31..8b328932af 100644 --- a/app/src/main/java/eu/faircode/email/DaoAccount.java +++ b/app/src/main/java/eu/faircode/email/DaoAccount.java @@ -43,7 +43,7 @@ public interface DaoAccount { @Query("SELECT * FROM account WHERE synchronize") LiveData> liveSynchronizingAccounts(); - @Query("SELECT account.*, COUNT(operation.id) AS operations" + + @Query("SELECT account.*" + ", (SELECT COUNT(message.id)" + " FROM message" + " JOIN folder ON folder.id = message.folder" + @@ -58,16 +58,8 @@ public interface DaoAccount { " FROM identity" + " WHERE identity.account = account.id" + " AND identity.synchronize) AS identities" + - ", (SELECT COUNT(message.id)" + - " FROM message" + - " JOIN folder ON folder.id = message.folder" + - " WHERE message.account = account.id" + - " AND folder.type = '" + EntityFolder.OUTBOX + "'" + - " AND NOT ui_seen" + - " AND NOT ui_hide) AS unsent" + ", CASE WHEN drafts.id IS NULL THEN 0 ELSE 1 END AS drafts" + " FROM account" + - " LEFT JOIN operation ON operation.account = account.id" + " LEFT JOIN folder AS drafts ON drafts.account = account.id AND drafts.type = '" + EntityFolder.DRAFTS + "'" + " WHERE :all OR account.synchronize" + " GROUP BY account.id" + diff --git a/app/src/main/java/eu/faircode/email/DaoFolder.java b/app/src/main/java/eu/faircode/email/DaoFolder.java index 29b4afd6bd..3f40f6ed3d 100644 --- a/app/src/main/java/eu/faircode/email/DaoFolder.java +++ b/app/src/main/java/eu/faircode/email/DaoFolder.java @@ -82,6 +82,18 @@ public interface DaoFolder { " GROUP BY folder.id") LiveData> liveUnified(); + @Query("SELECT folder.*" + + ", account.color" + + ", SUM(CASE WHEN message.ui_seen = 0 THEN 1 ELSE 0 END) AS unseen" + + ", (SELECT COUNT(*) FROM operation WHERE operation.folder = folder.id) AS operations" + + " FROM folder" + + " LEFT JOIN account ON account.id = folder.account" + + " LEFT JOIN message ON message.folder = folder.id AND NOT message.ui_hide" + + " WHERE account.id IS NULL" + + " OR (account.`synchronize` AND folder.navigation)" + + " GROUP BY folder.id") + LiveData> liveNavigation(); + @Query("SELECT folder.* FROM folder" + " JOIN account ON account.id = folder.account" + " WHERE account.synchronize" + @@ -180,6 +192,7 @@ public interface DaoFolder { @Query("UPDATE folder" + " SET display = :display" + ", unified = :unified" + + ", navigation = :navigation" + ", notify = :notify" + ", hide = :hide" + ", synchronize = :synchronize" + @@ -190,7 +203,7 @@ public interface DaoFolder { " WHERE id = :id") int setFolderProperties( long id, - String display, boolean unified, boolean notify, boolean hide, + String display, boolean unified, boolean navigation, boolean notify, boolean hide, boolean synchronize, boolean poll, boolean download, int sync_days, int keep_days); diff --git a/app/src/main/java/eu/faircode/email/DaoOperation.java b/app/src/main/java/eu/faircode/email/DaoOperation.java index bf6b1b3972..25d9cff5dc 100644 --- a/app/src/main/java/eu/faircode/email/DaoOperation.java +++ b/app/src/main/java/eu/faircode/email/DaoOperation.java @@ -65,6 +65,9 @@ public interface DaoOperation { @Query(GET_OPS_FOLDER) LiveData> liveOperations(long folder); + @Query("SELECT COUNT(operation.id) FROM operation") + LiveData liveCount(); + @Query("SELECT COUNT(operation.id) FROM operation" + " WHERE operation.name = '" + EntityOperation.SEND + "'") LiveData liveUnsent(); diff --git a/app/src/main/java/eu/faircode/email/DrawerAdapter.java b/app/src/main/java/eu/faircode/email/DrawerAdapter.java deleted file mode 100644 index dadcc1e8cd..0000000000 --- a/app/src/main/java/eu/faircode/email/DrawerAdapter.java +++ /dev/null @@ -1,91 +0,0 @@ -package eu.faircode.email; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.ArrayList; -import java.util.List; - -public class DrawerAdapter extends ArrayAdapter { - private boolean collapsed; - private List items = new ArrayList<>(); - - DrawerAdapter(@NonNull Context context, boolean collapsed) { - super(context, -1); - this.collapsed = collapsed; - } - - @NonNull - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - DrawerItem item = getItem(position); - - View row = item.isCollapsible() && collapsed - ? new View(getContext()) - : LayoutInflater.from(getContext()).inflate(item.getLayout(), null); - - ImageView iv = row.findViewById(R.id.ivItem); - TextView tv = row.findViewById(R.id.tvItem); - ImageView expander = row.findViewById(R.id.ivExpander); - - if (iv != null) { - iv.setImageResource(item.getIcon()); - if (item.getColor() != null) - iv.setColorFilter(item.getColor()); - } - - if (tv != null) { - tv.setText(item.getTitle(getContext())); - - tv.setTextColor(Helper.resolveColor(getContext(), - item.getHighlight() ? R.attr.colorUnread : android.R.attr.textColorSecondary)); - } - - if (expander != null) - expander.setImageLevel(collapsed ? 1 /* more */ : 0 /* less */); - - return row; - } - - void set(boolean collapsed) { - this.collapsed = collapsed; - notifyDataSetChanged(); - } - - void set(List items) { - this.items = items; - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return items.size(); - } - - @Nullable - @Override - public DrawerItem getItem(int position) { - if (position < items.size()) - return items.get(position); - else - return null; - } - - @Override - public boolean hasStableIds() { - return true; - } - - @Override - public long getItemId(int position) { - DrawerItem item = getItem(position); - return (item == null ? 0 : item.getId()); - } -} diff --git a/app/src/main/java/eu/faircode/email/DrawerItem.java b/app/src/main/java/eu/faircode/email/DrawerItem.java deleted file mode 100644 index 19399774c1..0000000000 --- a/app/src/main/java/eu/faircode/email/DrawerItem.java +++ /dev/null @@ -1,95 +0,0 @@ -package eu.faircode.email; - -import android.content.Context; - -public class DrawerItem { - private long id; - private int menu; - private int layout; - private int icon; - private Integer color; - private int resid; - private String title; - private boolean highlight; - private boolean collapsible = false; - - DrawerItem(long id) { - this.id = id; - this.layout = R.layout.item_drawer_separator; - } - - DrawerItem(long id, int resid) { - this.id = id; - this.menu = resid; - this.layout = R.layout.item_drawer_expander; - } - - DrawerItem(long id, int icon, int resid) { - this.id = id; - this.menu = resid; - this.layout = R.layout.item_drawer; - this.icon = icon; - this.resid = resid; - } - - DrawerItem(long id, int menu, int icon, String title, boolean highlight) { - this.id = id; - this.menu = menu; - this.layout = R.layout.item_drawer; - this.icon = icon; - this.title = title; - this.highlight = highlight; - } - - DrawerItem(long id, int icon, String title, Integer color, boolean highlight) { - this.id = id; - this.layout = R.layout.item_drawer; - this.icon = icon; - this.color = color; - this.title = title; - this.highlight = highlight; - } - - DrawerItem setCollapsible() { - this.collapsible = true; - return this; - } - - int getLayout() { - return this.layout; - } - - long getId() { - return this.id; - } - - boolean isEnabled() { - return (this.layout != R.layout.item_drawer_separator); - } - - int getMenuId() { - return this.menu; - } - - int getIcon() { - return this.icon; - } - - Integer getColor() { - return this.color; - } - - String getTitle(Context context) { - if (this.title == null && resid > 0) - this.title = context.getString(resid); - return this.title; - } - - boolean getHighlight() { - return this.highlight; - } - - boolean isCollapsible() { - return this.collapsible; - } -} diff --git a/app/src/main/java/eu/faircode/email/EntityFolder.java b/app/src/main/java/eu/faircode/email/EntityFolder.java index 496901e21b..758ca95406 100644 --- a/app/src/main/java/eu/faircode/email/EntityFolder.java +++ b/app/src/main/java/eu/faircode/email/EntityFolder.java @@ -91,6 +91,8 @@ public class EntityFolder implements Serializable { @NonNull public Boolean unified = false; @NonNull + public Boolean navigation = false; + @NonNull public Boolean notify = false; public Integer total; // messages on server diff --git a/app/src/main/java/eu/faircode/email/FragmentFolder.java b/app/src/main/java/eu/faircode/email/FragmentFolder.java index a28f28add6..84b6b28bf5 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolder.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolder.java @@ -50,6 +50,7 @@ public class FragmentFolder extends FragmentBase { private EditText etDisplay; private CheckBox cbHide; private CheckBox cbUnified; + private CheckBox cbNavigation; private CheckBox cbNotify; private CheckBox cbSynchronize; private CheckBox cbPoll; @@ -90,6 +91,7 @@ public class FragmentFolder extends FragmentBase { etDisplay = view.findViewById(R.id.etDisplay); cbHide = view.findViewById(R.id.cbHide); cbUnified = view.findViewById(R.id.cbUnified); + cbNavigation = view.findViewById(R.id.cbNavigation); cbNotify = view.findViewById(R.id.cbNotify); cbSynchronize = view.findViewById(R.id.cbSynchronize); cbPoll = view.findViewById(R.id.cbPoll); @@ -151,6 +153,7 @@ public class FragmentFolder extends FragmentBase { args.putString("display", etDisplay.getText().toString()); args.putBoolean("hide", cbHide.isChecked()); args.putBoolean("unified", cbUnified.isChecked()); + args.putBoolean("navigation", cbNavigation.isChecked()); args.putBoolean("notify", cbNotify.getVisibility() == View.VISIBLE && cbNotify.isChecked()); args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("poll", cbPoll.isChecked()); @@ -185,6 +188,7 @@ public class FragmentFolder extends FragmentBase { String display = args.getString("display"); boolean hide = args.getBoolean("hide"); boolean unified = args.getBoolean("unified"); + boolean navigation = args.getBoolean("navigation"); boolean notify = args.getBoolean("notify"); boolean synchronize = args.getBoolean("synchronize"); boolean poll = args.getBoolean("poll"); @@ -220,6 +224,7 @@ public class FragmentFolder extends FragmentBase { create.hide = hide; create.type = EntityFolder.USER; create.unified = unified; + create.navigation = navigation; create.notify = notify; create.synchronize = synchronize; create.poll = poll; @@ -234,7 +239,7 @@ public class FragmentFolder extends FragmentBase { Log.i("Updating folder=" + name); db.folder().setFolderProperties(id, - display, unified, notify, hide, + display, unified, navigation, notify, hide, synchronize, poll, download, sync_days, keep_days); db.folder().setFolderError(id, null); @@ -414,6 +419,7 @@ public class FragmentFolder extends FragmentBase { etDisplay.setHint(folder == null ? null : folder.name); cbHide.setChecked(folder == null ? false : folder.hide); cbUnified.setChecked(folder == null ? false : folder.unified); + cbNavigation.setChecked(folder == null ? false : folder.navigation); cbNotify.setChecked(folder == null ? false : folder.notify); cbSynchronize.setChecked(folder == null || folder.synchronize); cbPoll.setChecked(folder == null ? false : folder.poll); diff --git a/app/src/main/java/eu/faircode/email/NavMenuItem.java b/app/src/main/java/eu/faircode/email/NavMenuItem.java new file mode 100644 index 0000000000..50f877b7e9 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/NavMenuItem.java @@ -0,0 +1,65 @@ +package eu.faircode.email; + +import androidx.annotation.Nullable; + +import java.util.Objects; + +public class NavMenuItem { + private int icon; + private int title; + private Integer count = null; + private Runnable click; + private Runnable longClick; + + NavMenuItem(int icon, int title, Runnable click) { + this.icon = icon; + this.title = title; + this.click = click; + } + + NavMenuItem(int icon, int title, Runnable click, Runnable longClick) { + this.icon = icon; + this.title = title; + this.click = click; + this.longClick = longClick; + } + + void setCount(Integer count) { + if (count != null && count == 0) + count = null; + this.count = count; + } + + int getIcon() { + return this.icon; + } + + int getTitle() { + return this.title; + } + + Integer getCount() { + return this.count; + } + + void onClick() { + click.run(); + } + + boolean onLongClick() { + if (longClick != null) + longClick.run(); + return (longClick != null); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof NavMenuItem) { + NavMenuItem other = (NavMenuItem) obj; + return (this.icon == other.icon && + this.title == other.title && + Objects.equals(this.count, other.count)); + } else + return false; + } +} diff --git a/app/src/main/java/eu/faircode/email/TupleAccountEx.java b/app/src/main/java/eu/faircode/email/TupleAccountEx.java index fcf8cf18f7..21aad75e1c 100644 --- a/app/src/main/java/eu/faircode/email/TupleAccountEx.java +++ b/app/src/main/java/eu/faircode/email/TupleAccountEx.java @@ -21,8 +21,6 @@ package eu.faircode.email; public class TupleAccountEx extends EntityAccount { public int unseen; - public int unsent; - public int operations; public int identities; // synchronizing public boolean drafts; @@ -43,8 +41,6 @@ public class TupleAccountEx extends EntityAccount { TupleAccountEx other = (TupleAccountEx) obj; return (super.equals(obj) && this.unseen == other.unseen && - this.unsent == other.unsent && - this.operations == other.operations && this.identities == other.identities && this.drafts == other.drafts); } else diff --git a/app/src/main/java/eu/faircode/email/TupleFolderNav.java b/app/src/main/java/eu/faircode/email/TupleFolderNav.java new file mode 100644 index 0000000000..233a6089f6 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/TupleFolderNav.java @@ -0,0 +1,41 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import java.io.Serializable; +import java.util.Objects; + +public class TupleFolderNav extends EntityFolder implements Serializable { + public Integer color; // account + public int unseen; + public int operations; + + @Override + public boolean equals(Object obj) { + if (obj instanceof TupleFolderNav) { + TupleFolderNav other = (TupleFolderNav) obj; + return (super.equals(obj) && + Objects.equals(this.color, other.color) && + this.unseen == other.unseen && + this.operations == other.operations); + } else + return false; + } +} diff --git a/app/src/main/res/layout-w600dp-land/activity_view.xml b/app/src/main/res/layout-w600dp-land/activity_view.xml index e1c6e9ffb2..d3ee12401c 100644 --- a/app/src/main/res/layout-w600dp-land/activity_view.xml +++ b/app/src/main/res/layout-w600dp-land/activity_view.xml @@ -43,22 +43,17 @@ app:constraint_referenced_ids="content_separator,content_pane" /> - + android:background="?attr/colorDrawerBackground" + android:orientation="vertical"> - - - \ No newline at end of file + android:layout_height="wrap_content" /> + + diff --git a/app/src/main/res/layout/activity_setup.xml b/app/src/main/res/layout/activity_setup.xml index b1141b381c..3e92977915 100644 --- a/app/src/main/res/layout/activity_setup.xml +++ b/app/src/main/res/layout/activity_setup.xml @@ -18,15 +18,11 @@ android:layout_gravity="start" android:background="?attr/colorDrawerBackground"> - - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_view.xml b/app/src/main/res/layout/activity_view.xml index ec2d972a5a..09f6b24302 100644 --- a/app/src/main/res/layout/activity_view.xml +++ b/app/src/main/res/layout/activity_view.xml @@ -1,5 +1,4 @@ - + android:background="?attr/colorDrawerBackground" + android:orientation="vertical"> - - - \ No newline at end of file + android:layout_height="wrap_content" /> + + diff --git a/app/src/main/res/layout/fragment_folder.xml b/app/src/main/res/layout/fragment_folder.xml index 523057ad29..e439f83f35 100644 --- a/app/src/main/res/layout/fragment_folder.xml +++ b/app/src/main/res/layout/fragment_folder.xml @@ -76,6 +76,15 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbHide" /> + + + app:layout_constraintTop_toBottomOf="@id/cbNavigation" /> + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_drawer.xml b/app/src/main/res/layout/item_drawer.xml deleted file mode 100644 index 9e9b4c34f1..0000000000 --- a/app/src/main/res/layout/item_drawer.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_drawer_expander.xml b/app/src/main/res/layout/item_drawer_expander.xml deleted file mode 100644 index 417636a373..0000000000 --- a/app/src/main/res/layout/item_drawer_expander.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_drawer_separator.xml b/app/src/main/res/layout/item_drawer_separator.xml deleted file mode 100644 index 60981e1c8a..0000000000 --- a/app/src/main/res/layout/item_drawer_separator.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_nav.xml b/app/src/main/res/layout/item_nav.xml new file mode 100644 index 0000000000..94f8fcb57a --- /dev/null +++ b/app/src/main/res/layout/item_nav.xml @@ -0,0 +1,40 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 99abcf6a80..61f24f674c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -314,6 +314,7 @@ Show hidden folders Hide folder Show in unified inbox + Show in navigation menu Synchronize (receive messages) Check periodically instead of continuous synchronize Automatically download message texts and attachments