From 633482ade78d26a9ab22d0fd5269dec198d79acd Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 12 Aug 2018 15:31:43 +0000 Subject: [PATCH] Fixes, improvements --- app/schemas/eu.faircode.email.DB/1.json | 11 +-- .../java/eu/faircode/email/ActivityMain.java | 2 +- .../java/eu/faircode/email/ActivityView.java | 39 ++++---- .../eu/faircode/email/AdapterMessage.java | 9 +- .../java/eu/faircode/email/DaoMessage.java | 12 +-- .../java/eu/faircode/email/EntityMessage.java | 2 +- .../eu/faircode/email/FragmentAccount.java | 81 +++++++++-------- .../eu/faircode/email/FragmentAccounts.java | 3 +- .../eu/faircode/email/FragmentCompose.java | 8 +- .../eu/faircode/email/FragmentFolder.java | 9 +- .../eu/faircode/email/FragmentFolders.java | 7 +- .../eu/faircode/email/FragmentIdentities.java | 3 +- .../eu/faircode/email/FragmentIdentity.java | 89 ++++++++++--------- .../eu/faircode/email/FragmentMessage.java | 56 +++++++----- .../eu/faircode/email/FragmentMessages.java | 23 +++-- .../java/eu/faircode/email/FragmentSetup.java | 4 +- .../eu/faircode/email/ServiceSynchronize.java | 67 ++++++++------ 17 files changed, 242 insertions(+), 183 deletions(-) diff --git a/app/schemas/eu.faircode.email.DB/1.json b/app/schemas/eu.faircode.email.DB/1.json index cfa71823bf..9d99db6fe9 100644 --- a/app/schemas/eu.faircode.email.DB/1.json +++ b/app/schemas/eu.faircode.email.DB/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "262ca4c3e0dbf6673b00b8b19fc219de", + "identityHash": "984985d3928b4abdec0802b65e820b2b", "entities": [ { "tableName": "identity", @@ -479,12 +479,13 @@ "createSql": "CREATE UNIQUE INDEX `index_message_folder_uid` ON `${TABLE_NAME}` (`folder`, `uid`)" }, { - "name": "index_message_msgid", + "name": "index_message_msgid_folder", "unique": true, "columnNames": [ - "msgid" + "msgid", + "folder" ], - "createSql": "CREATE UNIQUE INDEX `index_message_msgid` ON `${TABLE_NAME}` (`msgid`)" + "createSql": "CREATE UNIQUE INDEX `index_message_msgid_folder` ON `${TABLE_NAME}` (`msgid`, `folder`)" }, { "name": "index_message_thread", @@ -751,7 +752,7 @@ ], "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, \"262ca4c3e0dbf6673b00b8b19fc219de\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"984985d3928b4abdec0802b65e820b2b\")" ] } } \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/ActivityMain.java b/app/src/main/java/eu/faircode/email/ActivityMain.java index 6ad9f654a2..c44ddca3c2 100644 --- a/app/src/main/java/eu/faircode/email/ActivityMain.java +++ b/app/src/main/java/eu/faircode/email/ActivityMain.java @@ -27,7 +27,7 @@ public class ActivityMain extends AppCompatActivity implements FragmentManager.O DB.getInstance(this).account().liveAccounts(true).observe(this, new Observer>() { @Override public void onChanged(@Nullable List accounts) { - if (accounts.size() == 0) + if (accounts == null || accounts.size() == 0) startActivity(new Intent(ActivityMain.this, ActivitySetup.class)); else { startActivity(new Intent(ActivityMain.this, ActivityView.class)); diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 82830eddce..57bfd30245 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -129,18 +129,20 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack public void onChanged(@Nullable List accounts) { ArrayAdapterDrawer drawerArray = new ArrayAdapterDrawer(ActivityView.this, R.layout.item_drawer); - final Collator collator = Collator.getInstance(Locale.getDefault()); - collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc - - Collections.sort(accounts, new Comparator() { - @Override - public int compare(EntityAccount a1, EntityAccount a2) { - return collator.compare(a1.name, a2.name); - } - }); + if (accounts != null) { + final Collator collator = Collator.getInstance(Locale.getDefault()); + collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc + + Collections.sort(accounts, new Comparator() { + @Override + public int compare(EntityAccount a1, EntityAccount a2) { + return collator.compare(a1.name, a2.name); + } + }); - for (EntityAccount account : accounts) - drawerArray.add(new DrawerItem(-1, R.drawable.baseline_folder_24, account.name, account.id)); + for (EntityAccount account : accounts) + drawerArray.add(new DrawerItem(-1, R.drawable.baseline_folder_24, account.name, account.id)); + } drawerArray.add(new DrawerItem(ActivityView.this, R.drawable.baseline_settings_applications_24, R.string.menu_setup)); if (getIntentFAQ().resolveActivity(getPackageManager()) != null) @@ -474,16 +476,13 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack EntityMessage message = db.message().getMessage(id); EntityFolder folder = db.folder().getFolder(message.folder); - if (!EntityFolder.OUTBOX.equals(folder.type) && - !EntityFolder.ARCHIVE.equals(folder.type)) { - if (!message.seen && !message.ui_seen) { - message.ui_seen = !message.ui_seen; - db.message().updateMessage(message); - - if (message.uid != null) - EntityOperation.queue(db, message, EntityOperation.SEEN, message.ui_seen); + if (!EntityFolder.OUTBOX.equals(folder.type)) + for (EntityMessage tmessage : db.message().getMessageByThread(message.account, message.thread)) { + tmessage.ui_seen = true; + db.message().updateMessage(tmessage); + + EntityOperation.queue(db, tmessage, EntityOperation.SEEN, tmessage.ui_seen); } - } db.setTransactionSuccessful(); } finally { diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index e9d22f6560..aa94c03405 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -129,10 +129,13 @@ public class AdapterMessage extends PagedListAdapter operations) { String text = message.error + "\n" + message.id + " " + df.format(new Date(message.received)) + - "\n" + (message.ui_hide ? "HIDDEN " : " ") + message.uid + "/" + message.id + + "\n" + (message.ui_hide ? "HIDDEN " : "") + + "seen=" + message.seen + "/" + message.ui_seen + "/" + message.unseen + + " " + message.uid + "/" + message.id + "\n" + message.msgid; - for (EntityOperation op : operations) - text += "\n" + op.name + " " + df.format(new Date(op.created)); + if (operations != null) + for (EntityOperation op : operations) + text += "\n" + op.name + " " + df.format(new Date(op.created)); tvError.setText(text); tvError.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/eu/faircode/email/DaoMessage.java b/app/src/main/java/eu/faircode/email/DaoMessage.java index 9e348c1fc3..254ac3081b 100644 --- a/app/src/main/java/eu/faircode/email/DaoMessage.java +++ b/app/src/main/java/eu/faircode/email/DaoMessage.java @@ -25,7 +25,6 @@ import androidx.lifecycle.LiveData; import androidx.paging.DataSource; import androidx.room.Dao; import androidx.room.Insert; -import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Update; @@ -56,6 +55,7 @@ public interface DaoMessage { ", MAX(CASE WHEN folder.id = :folder THEN message.id ELSE 0 END) as dummy" + " FROM message" + " JOIN folder ON folder.id = message.folder" + + " LEFT JOIN folder f ON f.id = :folder" + " WHERE (NOT message.ui_hide OR :debug)" + " GROUP BY CASE WHEN message.thread IS NULL THEN message.id ELSE message.thread END, message.subject" + " HAVING SUM(CASE WHEN folder.id = :folder THEN 1 ELSE 0 END) > 0" + @@ -83,9 +83,12 @@ public interface DaoMessage { @Query("SELECT * FROM message WHERE msgid = :msgid") EntityMessage getMessageByMsgId(String msgid); + @Query("SELECT * FROM message WHERE account = :account AND thread = :thread") + List getMessageByThread(long account, String thread); + @Query("SELECT message.*, folder.name as folderName, folder.type as folderType" + - ", (SELECT COUNT(m.id) FROM message m WHERE m.account = message.account AND m.thread = message.thread AND NOT m.ui_hide) AS count" + - ", (SELECT COUNT(m.id) FROM message m WHERE m.account = message.account AND m.thread = message.thread AND NOT m.ui_hide AND NOT m.ui_seen) AS unseen" + + ", (SELECT COUNT(m1.id) FROM message m1 WHERE m1.account = message.account AND m1.thread = message.thread AND NOT m1.ui_hide) AS count" + + ", (SELECT COUNT(m2.id) FROM message m2 WHERE m2.account = message.account AND m2.thread = message.thread AND NOT m2.ui_hide AND NOT m2.ui_seen) AS unseen" + ", (SELECT COUNT(a.id) FROM attachment a WHERE a.message = message.id) AS attachments" + " FROM message" + " JOIN folder ON folder.id = message.folder" + @@ -98,8 +101,7 @@ public interface DaoMessage { @Query("SELECT uid FROM message WHERE folder = :folder AND received >= :received AND NOT uid IS NULL") List getUids(long folder, long received); - // in case of duplicate message IDs - @Insert(onConflict = OnConflictStrategy.REPLACE) + @Insert long insertMessage(EntityMessage message); @Update diff --git a/app/src/main/java/eu/faircode/email/EntityMessage.java b/app/src/main/java/eu/faircode/email/EntityMessage.java index dd56c520c2..77213c68a7 100644 --- a/app/src/main/java/eu/faircode/email/EntityMessage.java +++ b/app/src/main/java/eu/faircode/email/EntityMessage.java @@ -45,7 +45,7 @@ import static androidx.room.ForeignKey.CASCADE; @Index(value = {"identity"}), @Index(value = {"replying"}), @Index(value = {"folder", "uid"}, unique = true), - @Index(value = {"msgid"}, unique = true), + @Index(value = {"msgid", "folder"}, unique = true), @Index(value = {"thread"}), @Index(value = {"received"}), @Index(value = {"ui_seen"}), diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index f12a120b43..e77358e91d 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -321,6 +321,9 @@ public class FragmentAccount extends FragmentEx { @Override protected void onException(Bundle args, Throwable ex) { + Helper.setViewsEnabled(view, true); + btnCheck.setEnabled(true); + pbCheck.setVisibility(View.GONE); grpFolders.setVisibility(View.GONE); btnSave.setVisibility(View.GONE); Toast.makeText(getContext(), Helper.formatThrowable(ex), Toast.LENGTH_LONG).show(); @@ -372,48 +375,48 @@ public class FragmentAccount extends FragmentEx { new SimpleTask() { @Override protected Void onLoad(Context context, Bundle args) throws Throwable { + String name = args.getString("name"); + String host = args.getString("host"); + String port = args.getString("port"); + String user = args.getString("user"); + String password = args.getString("password"); + boolean synchronize = args.getBoolean("synchronize"); + EntityFolder drafts = (EntityFolder) args.getSerializable("drafts"); + EntityFolder sent = (EntityFolder) args.getSerializable("sent"); + EntityFolder all = (EntityFolder) args.getSerializable("all"); + EntityFolder trash = (EntityFolder) args.getSerializable("trash"); + EntityFolder junk = (EntityFolder) args.getSerializable("junk"); + + if (TextUtils.isEmpty(host)) + throw new Throwable(getContext().getString(R.string.title_no_host)); + if (TextUtils.isEmpty(port)) + throw new Throwable(getContext().getString(R.string.title_no_port)); + if (TextUtils.isEmpty(user)) + throw new Throwable(getContext().getString(R.string.title_no_user)); + if (TextUtils.isEmpty(password)) + throw new Throwable(getContext().getString(R.string.title_no_password)); + if (drafts == null) + throw new Throwable(getContext().getString(R.string.title_no_drafts)); + + // Check IMAP server + Session isession = Session.getInstance(MessageHelper.getSessionProperties(), null); + IMAPStore istore = null; try { - ServiceSynchronize.stop(getContext(), "folder"); - - String name = args.getString("name"); - String host = args.getString("host"); - String port = args.getString("port"); - String user = args.getString("user"); - String password = args.getString("password"); - boolean synchronize = args.getBoolean("synchronize"); - EntityFolder drafts = (EntityFolder) args.getSerializable("drafts"); - EntityFolder sent = (EntityFolder) args.getSerializable("sent"); - EntityFolder all = (EntityFolder) args.getSerializable("all"); - EntityFolder trash = (EntityFolder) args.getSerializable("trash"); - EntityFolder junk = (EntityFolder) args.getSerializable("junk"); - - if (TextUtils.isEmpty(host)) - throw new Throwable(getContext().getString(R.string.title_no_host)); - if (TextUtils.isEmpty(port)) - throw new Throwable(getContext().getString(R.string.title_no_port)); - if (TextUtils.isEmpty(user)) - throw new Throwable(getContext().getString(R.string.title_no_user)); - if (TextUtils.isEmpty(password)) - throw new Throwable(getContext().getString(R.string.title_no_password)); - if (drafts == null) - throw new Throwable(getContext().getString(R.string.title_no_drafts)); - - // Check IMAP server - Session isession = Session.getInstance(MessageHelper.getSessionProperties(), null); - IMAPStore istore = null; - try { - istore = (IMAPStore) isession.getStore("imaps"); - istore.connect(host, Integer.parseInt(port), user, password); + istore = (IMAPStore) isession.getStore("imaps"); + istore.connect(host, Integer.parseInt(port), user, password); - if (!istore.hasCapability("IDLE")) - throw new MessagingException(getContext().getString(R.string.title_no_idle)); - } finally { - if (istore != null) - istore.close(); - } + if (!istore.hasCapability("IDLE")) + throw new MessagingException(getContext().getString(R.string.title_no_idle)); + } finally { + if (istore != null) + istore.close(); + } - if (TextUtils.isEmpty(name)) - name = host + "/" + user; + if (TextUtils.isEmpty(name)) + name = host + "/" + user; + + try { + ServiceSynchronize.stop(getContext(), "account"); DB db = DB.getInstance(getContext()); try { diff --git a/app/src/main/java/eu/faircode/email/FragmentAccounts.java b/app/src/main/java/eu/faircode/email/FragmentAccounts.java index 0024e0fdf7..7f03884022 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccounts.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccounts.java @@ -93,7 +93,8 @@ public class FragmentAccounts extends FragmentEx { DB.getInstance(getContext()).account().liveAccounts().observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List accounts) { - adapter.set(accounts); + if (accounts != null) + adapter.set(accounts); pbWait.setVisibility(View.GONE); grpReady.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index 29398a67c5..9664085840 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -635,6 +635,9 @@ public class FragmentCompose extends FragmentEx { db.identity().liveIdentities(true).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable final List identities) { + if (identities == null) + return; + Log.i(Helper.TAG, "Set identities=" + identities.size()); // Sort identities @@ -693,8 +696,9 @@ public class FragmentCompose extends FragmentEx { new Observer>() { @Override public void onChanged(@Nullable List attachments) { - adapter.set(attachments); - grpAttachments.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE); + if (attachments != null) + adapter.set(attachments); + grpAttachments.setVisibility(attachments != null && attachments.size() > 0 ? View.VISIBLE : View.GONE); } }); diff --git a/app/src/main/java/eu/faircode/email/FragmentFolder.java b/app/src/main/java/eu/faircode/email/FragmentFolder.java index 5b5dc6829d..d42f7d2eac 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolder.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolder.java @@ -145,11 +145,14 @@ public class FragmentFolder extends FragmentEx { DB.getInstance(getContext()).folder().liveFolder(id).observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(@Nullable EntityFolder folder) { - if (folder != null) { - cbSynchronize.setChecked(folder.synchronize); - etAfter.setText(Integer.toString(folder.after)); + if (folder == null) { + getFragmentManager().popBackStack(); + return; } + cbSynchronize.setChecked(folder.synchronize); + etAfter.setText(Integer.toString(folder.after)); + pbWait.setVisibility(View.GONE); Helper.setViewsEnabled(view, true); btnSave.setEnabled(true); diff --git a/app/src/main/java/eu/faircode/email/FragmentFolders.java b/app/src/main/java/eu/faircode/email/FragmentFolders.java index b212ab786e..7d0a43c8ca 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolders.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolders.java @@ -92,7 +92,7 @@ public class FragmentFolders extends FragmentEx { db.account().liveAccount(account).observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(@Nullable EntityAccount account) { - setSubtitle(account.name); + setSubtitle(account == null ? null : account.name); } }); @@ -100,6 +100,11 @@ public class FragmentFolders extends FragmentEx { db.folder().liveFolders(account).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List folders) { + if (folders == null) { + getFragmentManager().popBackStack(); + return; + } + adapter.set(folders); pbWait.setVisibility(View.GONE); diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentities.java b/app/src/main/java/eu/faircode/email/FragmentIdentities.java index 1996260569..69b7d3255e 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentities.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentities.java @@ -93,7 +93,8 @@ public class FragmentIdentities extends FragmentEx { DB.getInstance(getContext()).identity().liveIdentities().observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List identities) { - adapter.set(identities); + if (identities != null) + adapter.set(identities); pbWait.setVisibility(View.GONE); grpReady.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java index 3b3df122e2..782efd35d3 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java @@ -198,50 +198,50 @@ public class FragmentIdentity extends FragmentEx { new SimpleTask() { @Override protected Void onLoad(Context context, Bundle args) throws Throwable { - try { - ServiceSynchronize.stop(getContext(), "account"); - - long id = args.getLong("id"); - String name = args.getString("name"); - String email = args.getString("email"); - String replyto = args.getString("replyto"); - long account = args.getLong("account"); - String host = args.getString("host"); - boolean starttls = args.getBoolean("starttls"); - String port = args.getString("port"); - String user = args.getString("user"); - String password = args.getString("password"); - boolean synchronize = args.getBoolean("synchronize"); - - if (TextUtils.isEmpty(name)) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_name)); - if (TextUtils.isEmpty(email)) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_email)); - if (account < 0) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_account)); - if (TextUtils.isEmpty(host)) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_host)); - if (TextUtils.isEmpty(port)) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_port)); - if (TextUtils.isEmpty(user)) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_user)); - if (TextUtils.isEmpty(password)) - throw new IllegalArgumentException(getContext().getString(R.string.title_no_password)); - - if (TextUtils.isEmpty(replyto)) - replyto = null; - - // Check SMTP server - if (synchronize) { - Properties props = MessageHelper.getSessionProperties(); - Session isession = Session.getInstance(props, null); - Transport itransport = isession.getTransport(starttls ? "smtp" : "smtps"); - try { - itransport.connect(host, Integer.parseInt(port), user, password); - } finally { - itransport.close(); - } + long id = args.getLong("id"); + String name = args.getString("name"); + String email = args.getString("email"); + String replyto = args.getString("replyto"); + long account = args.getLong("account"); + String host = args.getString("host"); + boolean starttls = args.getBoolean("starttls"); + String port = args.getString("port"); + String user = args.getString("user"); + String password = args.getString("password"); + boolean synchronize = args.getBoolean("synchronize"); + + if (TextUtils.isEmpty(name)) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_name)); + if (TextUtils.isEmpty(email)) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_email)); + if (account < 0) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_account)); + if (TextUtils.isEmpty(host)) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_host)); + if (TextUtils.isEmpty(port)) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_port)); + if (TextUtils.isEmpty(user)) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_user)); + if (TextUtils.isEmpty(password)) + throw new IllegalArgumentException(getContext().getString(R.string.title_no_password)); + + if (TextUtils.isEmpty(replyto)) + replyto = null; + + // Check SMTP server + if (synchronize) { + Properties props = MessageHelper.getSessionProperties(); + Session isession = Session.getInstance(props, null); + Transport itransport = isession.getTransport(starttls ? "smtp" : "smtps"); + try { + itransport.connect(host, Integer.parseInt(port), user, password); + } finally { + itransport.close(); } + } + + try { + ServiceSynchronize.stop(getContext(), "identity"); DB db = DB.getInstance(getContext()); try { @@ -388,6 +388,9 @@ public class FragmentIdentity extends FragmentEx { db.account().liveAccounts().observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(List accounts) { + if (accounts == null) + return; + EntityAccount unselected = new EntityAccount(); unselected.id = -1L; unselected.name = ""; diff --git a/app/src/main/java/eu/faircode/email/FragmentMessage.java b/app/src/main/java/eu/faircode/email/FragmentMessage.java index 588bd354bc..83e2c67b04 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessage.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessage.java @@ -60,6 +60,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.PopupMenu; import androidx.browser.customtabs.CustomTabsIntent; import androidx.constraintlayout.widget.Group; +import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Observer; import androidx.recyclerview.widget.LinearLayoutManager; @@ -279,8 +280,9 @@ public class FragmentMessage extends FragmentEx { new Observer>() { @Override public void onChanged(@Nullable List attachments) { - adapter.set(attachments); - grpAttachments.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE); + if (attachments != null) + adapter.set(attachments); + grpAttachments.setVisibility(attachments != null && attachments.size() > 0 ? View.VISIBLE : View.GONE); } }); @@ -313,21 +315,22 @@ public class FragmentMessage extends FragmentEx { boolean hasJunk = false; boolean hasArchive = false; boolean hasUser = false; - for (EntityFolder folder : folders) { - if (EntityFolder.TRASH.equals(folder.type)) - hasTrash = true; - else if (EntityFolder.JUNK.equals(folder.type)) - hasJunk = true; - else if (EntityFolder.ARCHIVE.equals(folder.type)) - hasArchive = true; - else if (EntityFolder.USER.equals(folder.type)) - hasUser = true; - } + if (folders != null) + for (EntityFolder folder : folders) { + if (EntityFolder.TRASH.equals(folder.type)) + hasTrash = true; + else if (EntityFolder.JUNK.equals(folder.type)) + hasJunk = true; + else if (EntityFolder.ARCHIVE.equals(folder.type)) + hasArchive = true; + else if (EntityFolder.USER.equals(folder.type)) + hasUser = true; + } bottom_navigation.setTag(inTrash || !hasTrash); top_navigation.getMenu().findItem(R.id.action_thread).setVisible(message.count > 1); - top_navigation.getMenu().findItem(R.id.action_seen).setVisible(!inOutbox && !inArchive); + top_navigation.getMenu().findItem(R.id.action_seen).setVisible(!inOutbox); top_navigation.getMenu().findItem(R.id.action_edit).setVisible(inTrash); top_navigation.getMenu().findItem(R.id.action_forward).setVisible(!inOutbox); top_navigation.getMenu().findItem(R.id.action_reply_all).setVisible(!inOutbox && message.cc != null); @@ -335,7 +338,7 @@ public class FragmentMessage extends FragmentEx { bottom_navigation.getMenu().findItem(R.id.action_spam).setVisible(!inOutbox && !inArchive && !inJunk && hasJunk); bottom_navigation.getMenu().findItem(R.id.action_trash).setVisible(!inOutbox && !inArchive && hasTrash); - bottom_navigation.getMenu().findItem(R.id.action_move).setVisible(!inOutbox && !inArchive && (!inInbox || hasUser)); + bottom_navigation.getMenu().findItem(R.id.action_move).setVisible(!inOutbox && (!inInbox || hasUser)); bottom_navigation.getMenu().findItem(R.id.action_archive).setVisible(!inOutbox && !inArchive && hasArchive); bottom_navigation.getMenu().findItem(R.id.action_reply).setVisible(!inOutbox); bottom_navigation.setVisibility(View.VISIBLE); @@ -372,6 +375,8 @@ public class FragmentMessage extends FragmentEx { } private void onActionThread(long id) { + getFragmentManager().popBackStack("thread", FragmentManager.POP_BACK_STACK_INCLUSIVE); + Bundle args = new Bundle(); args.putLong("thread", id); // message ID @@ -402,11 +407,12 @@ public class FragmentMessage extends FragmentEx { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - message.ui_seen = !message.ui_seen; - db.message().updateMessage(message); + for (EntityMessage tmessage : db.message().getMessageByThread(message.account, message.thread)) { + tmessage.ui_seen = !message.ui_seen; + db.message().updateMessage(tmessage); - if (message.uid != null) - EntityOperation.queue(db, message, EntityOperation.SEEN, message.ui_seen); + EntityOperation.queue(db, tmessage, EntityOperation.SEEN, tmessage.ui_seen); + } db.setTransactionSuccessful(); } finally { @@ -459,12 +465,17 @@ public class FragmentMessage extends FragmentEx { draft.uid = null; draft.id = db.message().insertMessage(draft); + EntityOperation.queue(db, draft, EntityOperation.ADD); + db.setTransactionSuccessful(); - return null; } finally { db.endTransaction(); } + + EntityOperation.process(context); + + return null; } @Override @@ -749,8 +760,11 @@ public class FragmentMessage extends FragmentEx { db.beginTransaction(); EntityMessage message = db.message().getMessage(id); - message.ui_hide = true; - db.message().updateMessage(message); + EntityFolder folder = db.folder().getFolder(message.folder); + if (!EntityFolder.ARCHIVE.equals(folder.type)) { + message.ui_hide = true; + db.message().updateMessage(message); + } EntityOperation.queue(db, message, EntityOperation.MOVE, target); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index cbdc9b65ef..bed8f5f939 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -137,6 +137,11 @@ public class FragmentMessages extends FragmentEx { messages.observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable PagedList messages) { + if (messages == null) { + getFragmentManager().popBackStack(); + return; + } + Log.i(Helper.TAG, "Submit messages=" + messages.size()); adapter.submitList(messages); @@ -161,17 +166,19 @@ public class FragmentMessages extends FragmentEx { DB db = DB.getInstance(context); - Long account; - if (thread < 0) - if (folder < 0) - return db.folder().getPrimaryDrafts().account; - else + Long account = null; + if (thread < 0) { + if (folder >= 0) account = db.folder().getFolder(folder).account; - else + } else account = db.message().getMessage(thread).account; - if (account == null) // outbox - account = db.folder().getPrimaryDrafts().account; + if (account == null) { + // outbox + EntityFolder primary = db.folder().getPrimaryDrafts(); + if (primary != null) + account = primary.account; + } return account; } diff --git a/app/src/main/java/eu/faircode/email/FragmentSetup.java b/app/src/main/java/eu/faircode/email/FragmentSetup.java index 072bf1665a..1dab8b0646 100644 --- a/app/src/main/java/eu/faircode/email/FragmentSetup.java +++ b/app/src/main/java/eu/faircode/email/FragmentSetup.java @@ -196,14 +196,14 @@ public class FragmentSetup extends FragmentEx { db.account().liveAccounts(true).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List accounts) { - tvAccountDone.setVisibility(accounts.size() > 0 ? View.VISIBLE : View.INVISIBLE); + tvAccountDone.setVisibility(accounts != null && accounts.size() > 0 ? View.VISIBLE : View.INVISIBLE); } }); db.identity().liveIdentities(true).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(@Nullable List identities) { - tvIdentityDone.setVisibility(identities.size() > 0 ? View.VISIBLE : View.INVISIBLE); + tvIdentityDone.setVisibility(identities != null && identities.size() > 0 ? View.VISIBLE : View.INVISIBLE); } }); } diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 896d4155f2..8ddf426a6a 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -134,21 +134,22 @@ public class ServiceSynchronize extends LifecycleService { @Override public void onChanged(@Nullable TupleAccountStats stats) { - if (stats != null) { - NotificationManager nm = getSystemService(NotificationManager.class); - nm.notify(NOTIFICATION_SYNCHRONIZE, - getNotificationService(stats.accounts, stats.operations, stats.unsent).build()); - - if (stats.unseen > 0) { - if (stats.unseen > prev_unseen) { - nm.cancel(NOTIFICATION_UNSEEN); - nm.notify(NOTIFICATION_UNSEEN, getNotificationUnseen(stats.unseen).build()); - } - } else + if (stats == null) + return; + + NotificationManager nm = getSystemService(NotificationManager.class); + nm.notify(NOTIFICATION_SYNCHRONIZE, + getNotificationService(stats.accounts, stats.operations, stats.unsent).build()); + + if (stats.unseen > 0) { + if (stats.unseen > prev_unseen) { nm.cancel(NOTIFICATION_UNSEEN); + nm.notify(NOTIFICATION_UNSEEN, getNotificationUnseen(stats.unseen).build()); + } + } else + nm.cancel(NOTIFICATION_UNSEEN); - prev_unseen = stats.unseen; - } + prev_unseen = stats.unseen; } }); } @@ -762,7 +763,7 @@ public class ServiceSynchronize extends LifecycleService { JSONArray jargs = new JSONArray(op.args); if (EntityOperation.SEEN.equals(op.name)) - doSeen(folder, ifolder, message, jargs); + doSeen(folder, ifolder, message, jargs, db); else if (EntityOperation.ADD.equals(op.name)) doAdd(folder, ifolder, message, db); @@ -821,18 +822,22 @@ public class ServiceSynchronize extends LifecycleService { } } - private void doSeen(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs) throws MessagingException, JSONException { + private void doSeen(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, JSONArray jargs, DB db) throws MessagingException, JSONException { // Mark message (un)seen if (message.uid == null) { Log.w(Helper.TAG, folder.name + " local op seen id=" + message.id); return; } + boolean seen = jargs.getBoolean(0); Message imessage = ifolder.getMessageByUID(message.uid); if (imessage == null) throw new MessageRemovedException(); - imessage.setFlag(Flags.Flag.SEEN, jargs.getBoolean(0)); + imessage.setFlag(Flags.Flag.SEEN, seen); + + message.seen = seen; + db.message().updateMessage(message); } private void doAdd(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, DB db) throws MessagingException { @@ -873,8 +878,10 @@ public class ServiceSynchronize extends LifecycleService { for (EntityAttachment attachment : attachments) attachment.content = db.attachment().getContent(attachment.id); - imessage.setFlag(Flags.Flag.DELETED, true); - ifolder.expunge(); + if (!EntityFolder.ARCHIVE.equals(folder.type)) { + imessage.setFlag(Flags.Flag.DELETED, true); + ifolder.expunge(); + } MimeMessageEx icopy = MessageHelper.from(message, attachments, isession); Folder itarget = istore.getFolder(target.name); @@ -1177,6 +1184,7 @@ public class ServiceSynchronize extends LifecycleService { DB db = DB.getInstance(this); try { + int result = 0; db.beginTransaction(); // Find message by uid (fast, no headers required) @@ -1192,10 +1200,14 @@ public class ServiceSynchronize extends LifecycleService { String msgid = imessage.getMessageID(); message = db.message().getMessageByMsgId(msgid); if (message != null) { - Log.i(Helper.TAG, folder.name + " found as id=" + message.id + " uid=" + message.uid + " msgid=" + msgid); - message.folder = folder.id; - message.uid = uid; - db.message().updateMessage(message); + if (message.folder == folder.id || EntityFolder.OUTBOX.equals(folder.type)) { + Log.i(Helper.TAG, folder.name + " found as id=" + message.id + " uid=" + message.uid + " msgid=" + msgid); + message.folder = folder.id; + message.uid = uid; + db.message().updateMessage(message); + result = -1; + } else + message = null; } } @@ -1204,16 +1216,17 @@ public class ServiceSynchronize extends LifecycleService { message.seen = seen; message.ui_seen = seen; db.message().updateMessage(message); - Log.v(Helper.TAG, folder.name + " updated id=" + message.id + " uid=" + message.uid); - return -1; - } else { + Log.v(Helper.TAG, folder.name + " updated id=" + message.id + " uid=" + message.uid + " seen=" + seen); + result = -1; + } else Log.v(Helper.TAG, folder.name + " unchanged id=" + message.id + " uid=" + message.uid); - return 0; - } } db.setTransactionSuccessful(); + if (message != null) + return result; + } finally { db.endTransaction(); }