diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 668e74e9b3..85def28fdb 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -328,6 +328,7 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack draft.id = db.message().insertMessage(draft); EntityOperation.queue(ActivityView.this, draft, EntityOperation.ADD); + EntityOperation.process(ActivityView.this); startActivity(new Intent(ActivityView.this, ActivityCompose.class) .putExtra("id", draft.id)); diff --git a/app/src/main/java/eu/faircode/email/AdapterAttachment.java b/app/src/main/java/eu/faircode/email/AdapterAttachment.java index ebce2022f6..92cf8a8b7d 100644 --- a/app/src/main/java/eu/faircode/email/AdapterAttachment.java +++ b/app/src/main/java/eu/faircode/email/AdapterAttachment.java @@ -156,6 +156,7 @@ public class AdapterAttachment extends RecyclerView.Adapter queue = new ArrayList<>(); + static void queue(Context context, EntityMessage message, String name) { JSONArray jsonArray = new JSONArray(); queue(context, message, name, jsonArray); @@ -92,16 +97,26 @@ public class EntityOperation { operation.args = jsonArray.toString(); operation.id = dao.insertOperation(operation); + synchronized (queue) { + queue.add(new Intent(SEND.equals(name) + ? ServiceSynchronize.ACTION_PROCESS_OUTBOX + : ServiceSynchronize.ACTION_PROCESS_FOLDER) + .putExtra("folder", message.folder)); + } + Log.i(Helper.TAG, "Queued op=" + operation.id + "/" + name + " args=" + operation.args + " msg=" + message.folder + "/" + message.id + " uid=" + message.uid + " purged=" + purged); + } + public static void process(Context context) { + // Processing needs to be done after committing to the database LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); - lbm.sendBroadcast( - new Intent(SEND.equals(name) - ? ServiceSynchronize.ACTION_PROCESS_OUTBOX - : ServiceSynchronize.ACTION_PROCESS_FOLDER) - .putExtra("folder", message.folder)); + synchronized (queue) { + for (Intent intent : queue) + lbm.sendBroadcast(intent); + queue.clear(); + } } } diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index f367b279b7..a488b7b4b9 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; +import android.os.Handler; import android.provider.ContactsContract; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -195,16 +196,18 @@ public class FragmentCompose extends FragmentEx { adapter.setDropDownViewResource(R.layout.spinner_dropdown_item); spFrom.setAdapter(adapter); - // Select primary identity, also for saved drafts + // Select primary identity for (int pos = 0; pos < identities.size(); pos++) if (identities.get(pos).primary) { spFrom.setSelection(pos); break; } + + // Get might select another identity + getLoaderManager().restartLoader(ActivityCompose.LOADER_COMPOSE_GET, getArguments(), getLoaderCallbacks).forceLoad(); } }); - getLoaderManager().restartLoader(ActivityCompose.LOADER_COMPOSE_GET, getArguments(), getLoaderCallbacks).forceLoad(); return view; } @@ -369,8 +372,16 @@ public class FragmentCompose extends FragmentEx { result.putString("from", msg.to); result.putString("to", to); } else if ("forward".equals(action)) { + String to = null; + if (msg != null) + try { + Address[] reply = MessageHelper.decodeAddresses(msg.reply); + to = (reply.length == 0 ? msg.from : msg.reply); + } catch (Throwable ex) { + Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + } result.putString("from", msg.to); - result.putString("to", null); + result.putString("to", to); } } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); @@ -410,19 +421,23 @@ public class FragmentCompose extends FragmentEx { FragmentCompose.this.rid = rid; ArrayAdapter adapter = (ArrayAdapter) spFrom.getAdapter(); - if (adapter != null) + if (adapter != null) { + InternetAddress[] afrom = MessageHelper.decodeAddresses(from); for (int pos = 0; pos < adapter.getCount(); pos++) { EntityIdentity identity = (EntityIdentity) adapter.getItem(pos); - if (iid < 0 ? identity.primary : iid == identity.id) { + if (iid < 0 ? afrom.length > 0 && afrom[0].getAddress().equals(identity.email) : iid == identity.id) { spFrom.setSelection(pos); break; } } + } if (!once) { // Prevent changed fields from being overwritten once = true; + Handler handler = new Handler(); + etCc.setText(TextUtils.join(", ", MessageHelper.decodeAddresses(cc))); etBcc.setText(TextUtils.join(", ", MessageHelper.decodeAddresses(bcc))); @@ -431,21 +446,34 @@ public class FragmentCompose extends FragmentEx { etSubject.setText(subject); if (body != null) etBody.setText(Html.fromHtml(HtmlHelper.sanitize(getContext(), body, false))); + handler.post(new Runnable() { + @Override + public void run() { + etTo.requestFocus(); + } + }); } else if ("reply".equals(action) || "reply_all".equals(action)) { etTo.setText(TextUtils.join(", ", MessageHelper.decodeAddresses(to))); String text = String.format("

%s %s:

%s", Html.escapeHtml(new Date().toString()), - Html.escapeHtml(TextUtils.join(", ", MessageHelper.decodeAddresses(from))), + Html.escapeHtml(TextUtils.join(", ", MessageHelper.decodeAddresses(to))), HtmlHelper.sanitize(getContext(), body, true)); etSubject.setText(getContext().getString(R.string.title_subject_reply, subject)); etBody.setText(Html.fromHtml(text)); + handler.postDelayed(new Runnable() { + @Override + public void run() { + etBody.requestFocus(); + } + }, 500); } else if ("forward".equals(action)) { String text = String.format("

%s %s:

%s", Html.escapeHtml(new Date().toString()), - Html.escapeHtml(TextUtils.join(", ", MessageHelper.decodeAddresses(from))), + Html.escapeHtml(TextUtils.join(", ", MessageHelper.decodeAddresses(to))), HtmlHelper.sanitize(getContext(), body, true)); etSubject.setText(getContext().getString(R.string.title_subject_forward, subject)); etBody.setText(Html.fromHtml(text)); + etTo.requestFocus(); } } @@ -527,37 +555,47 @@ public class FragmentCompose extends FragmentEx { draft.id = message.insertMessage(draft); // Check data - if ("send".equals(action)) { - if (draft.identity == null) - throw new MessagingException(getContext().getString(R.string.title_from_missing)); - if (draft.to == null && draft.cc == null && draft.bcc == null) - throw new MessagingException(getContext().getString(R.string.title_to_missing)); + try { + db.beginTransaction(); - EntityOperation.queue(getContext(), draft, EntityOperation.DELETE); + if ("send".equals(action)) { + if (draft.identity == null) + throw new MessagingException(getContext().getString(R.string.title_from_missing)); + if (draft.to == null && draft.cc == null && draft.bcc == null) + throw new MessagingException(getContext().getString(R.string.title_to_missing)); - draft.id = null; - draft.folder = folder.getOutbox().id; - draft.ui_hide = false; - draft.id = db.message().insertMessage(draft); + EntityOperation.queue(getContext(), draft, EntityOperation.DELETE); - EntityOperation.queue(getContext(), draft, EntityOperation.SEND); - - } else if ("save".equals(action)) - EntityOperation.queue(getContext(), draft, EntityOperation.ADD); - - else if ("trash".equals(action)) { - EntityOperation.queue(getContext(), draft, EntityOperation.DELETE); - - EntityFolder trash = db.folder().getPrimaryFolder(EntityFolder.TYPE_TRASH); - if (trash != null) { draft.id = null; - draft.folder = trash.id; + draft.folder = folder.getOutbox().id; + draft.ui_hide = false; draft.id = db.message().insertMessage(draft); + EntityOperation.queue(getContext(), draft, EntityOperation.SEND); + + } else if ("save".equals(action)) EntityOperation.queue(getContext(), draft, EntityOperation.ADD); + + else if ("trash".equals(action)) { + EntityOperation.queue(getContext(), draft, EntityOperation.DELETE); + + EntityFolder trash = db.folder().getPrimaryFolder(EntityFolder.TYPE_TRASH); + if (trash != null) { + draft.id = null; + draft.folder = trash.id; + draft.id = db.message().insertMessage(draft); + + EntityOperation.queue(getContext(), draft, EntityOperation.ADD); + } } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } + EntityOperation.process(getContext()); + return null; } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessage.java b/app/src/main/java/eu/faircode/email/FragmentMessage.java index 838e52b87a..b74c27c3ae 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessage.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessage.java @@ -331,6 +331,7 @@ public class FragmentMessage extends FragmentEx { message.ui_seen = !message.ui_seen; db.message().updateMessage(message); EntityOperation.queue(getContext(), message, EntityOperation.SEEN, message.ui_seen); + EntityOperation.process(getContext()); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } @@ -381,6 +382,7 @@ public class FragmentMessage extends FragmentEx { db.message().updateMessage(message); EntityOperation.queue(getContext(), message, EntityOperation.DELETE); + EntityOperation.process(getContext()); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } @@ -401,6 +403,7 @@ public class FragmentMessage extends FragmentEx { EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TYPE_TRASH); EntityOperation.queue(getContext(), message, EntityOperation.MOVE, trash.id); + EntityOperation.process(getContext()); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } @@ -427,6 +430,7 @@ public class FragmentMessage extends FragmentEx { EntityFolder spam = db.folder().getFolderByType(message.account, EntityFolder.TYPE_JUNK); EntityOperation.queue(getContext(), message, EntityOperation.MOVE, spam.id); + EntityOperation.process(getContext()); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } @@ -455,6 +459,7 @@ public class FragmentMessage extends FragmentEx { EntityFolder archive = db.folder().getFolderByType(message.account, EntityFolder.TYPE_ARCHIVE); EntityOperation.queue(getContext(), message, EntityOperation.MOVE, archive.id); + EntityOperation.process(getContext()); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } @@ -546,6 +551,7 @@ public class FragmentMessage extends FragmentEx { db.message().updateMessage(message); EntityOperation.queue(getContext(), message, EntityOperation.MOVE, folder); + EntityOperation.process(getContext()); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index 4ee293e189..2c996491fd 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -192,9 +192,9 @@ public class MessageHelper { return jaddresses.toString(); } - static Address[] decodeAddresses(String json) { + static InternetAddress[] decodeAddresses(String json) { if (json == null) - return new Address[0]; + return new InternetAddress[0]; List
result = new ArrayList<>(); try { JSONArray jaddresses = new JSONArray(json); @@ -211,7 +211,7 @@ public class MessageHelper { } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); } - return result.toArray(new Address[0]); + return result.toArray(new InternetAddress[0]); } static String getFormattedAddresses(String json) { diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 8a13d8bca3..903f667ae8 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -242,7 +242,6 @@ public class ServiceSynchronize extends LifecycleService { return builder; } - private Notification.Builder getNotification(String action, Throwable ex) { // Build pending intent Intent intent = new Intent(this, ActivityView.class); @@ -579,8 +578,8 @@ public class ServiceSynchronize extends LifecycleService { try { long uid = ifolder.getUID(imessage); DB db = DB.getInstance(ServiceSynchronize.this); - db.message().deleteMessage(folder.id, uid); - Log.i(Helper.TAG, "Deleted uid=" + uid); + int count = db.message().deleteMessage(folder.id, uid); + Log.i(Helper.TAG, "Deleted uid=" + uid + " count=" + count); } catch (MessageRemovedException ex) { Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex)); } @@ -694,25 +693,27 @@ public class ServiceSynchronize extends LifecycleService { imessage.setFlag(Flags.Flag.SEEN, jargs.getBoolean(0)); } else if (EntityOperation.ADD.equals(op.name)) { - if (!folder.synchronize) { - // Local drafts - Log.w(Helper.TAG, "Folder synchronization disabled"); - return; - } - // Append message EntityMessage msg = message.getMessage(op.message); if (msg == null) return; + // Disconnect from remote to prevent deletion + Long uid = msg.uid; + if (msg.uid != null) { + msg.uid = null; + message.updateMessage(msg); + } + + // Execute append Properties props = MessageHelper.getSessionProperties(); Session isession = Session.getInstance(props, null); MimeMessage imessage = MessageHelper.from(msg, isession); ifolder.appendMessages(new Message[]{imessage}); // Drafts can be appended multiple times - if (msg.uid != null) { - Message previously = ifolder.getMessageByUID(msg.uid); + if (uid != null) { + Message previously = ifolder.getMessageByUID(uid); if (previously == null) throw new MessageRemovedException(); @@ -972,8 +973,8 @@ public class ServiceSynchronize extends LifecycleService { // Delete local messages not at remote Log.i(Helper.TAG, folder.name + " delete=" + uids.size()); for (Long uid : uids) { - Log.i(Helper.TAG, folder.name + " delete local uid=" + uid); - dao.deleteMessage(folder.id, uid); + int count = dao.deleteMessage(folder.id, uid); + Log.i(Helper.TAG, folder.name + " delete local uid=" + uid + " count=" + count); } Log.i(Helper.TAG, folder.name + " synced");