From 332fcb5557c921b8eaa24737899a2e48ba92f716 Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 21 Jan 2019 16:45:05 +0000 Subject: [PATCH] Improved reply/forward handling --- .../java/eu/faircode/email/ActivityView.java | 6 +-- .../eu/faircode/email/AdapterMessage.java | 4 +- .../java/eu/faircode/email/DaoMessage.java | 6 --- .../java/eu/faircode/email/EntityMessage.java | 48 +++---------------- .../eu/faircode/email/EntityOperation.java | 4 -- .../java/eu/faircode/email/EntityRule.java | 5 +- .../eu/faircode/email/FragmentCompose.java | 47 +++++++++++------- .../eu/faircode/email/FragmentWebView.java | 2 +- .../main/java/eu/faircode/email/Helper.java | 33 ++++++++++++- .../java/eu/faircode/email/HtmlHelper.java | 13 ----- .../main/java/eu/faircode/email/JobDaily.java | 44 ++++++++++------- .../java/eu/faircode/email/MessageHelper.java | 25 ++++------ .../eu/faircode/email/ServiceSynchronize.java | 29 ++++++----- .../eu/faircode/email/ViewModelBrowse.java | 2 +- 14 files changed, 132 insertions(+), 136 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index b334013e9b..309da7e6bc 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -1215,7 +1215,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB if (encrypted == null) { EntityMessage message = db.message().getMessage(id); - String body = message.read(context); + String body = Helper.readText(EntityMessage.getFile(context, message.id)); // https://tools.ietf.org/html/rfc4880#section-6.2 int begin = body.indexOf(PGP_BEGIN_MESSAGE); @@ -1252,7 +1252,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB // Write decrypted body EntityMessage m = db.message().getMessage(id); - m.write(context, decrypted.toString()); + Helper.writeText(EntityMessage.getFile(context, m.id), decrypted.toString()); db.message().setMessageStored(id, new Date().getTime()); @@ -1275,7 +1275,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB // Write decrypted body EntityMessage m = db.message().getMessage(id); - m.write(context, parts.getHtml(context)); + Helper.writeText(EntityMessage.getFile(context, m.id), parts.getHtml(context)); // Remove previously decrypted attachments for (EntityAttachment a : attachments) diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index b872d95455..10e3482b36 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -1115,7 +1115,7 @@ public class AdapterMessage extends RecyclerView.Adapter 0) body = EntityAnswer.getAnswerText(db, answer, null) + body; } else { - result.draft.thread = ref.thread; - if ("reply".equals(action) || "reply_all".equals(action)) { - result.draft.replying = ref.id; + result.draft.references = (ref.references == null ? "" : ref.references + " ") + ref.msgid; + result.draft.inreplyto = ref.msgid; + result.draft.thread = ref.thread; result.draft.to = (ref.reply == null || ref.reply.length == 0 ? ref.from : ref.reply); result.draft.from = ref.to; @@ -1499,7 +1499,7 @@ public class FragmentCompose extends FragmentBase { } } else if ("forward".equals(action)) { - result.draft.forwarding = ref.id; + result.draft.thread = result.draft.msgid; // new thread result.draft.from = ref.to; } @@ -1553,10 +1553,19 @@ public class FragmentCompose extends FragmentBase { result.draft.received = new Date().getTime(); result.draft.id = db.message().insertMessage(result.draft); - result.draft.write(context, body == null ? "" : body); + Helper.writeText(EntityMessage.getFile(context, result.draft.id), body); db.message().setMessageContent(result.draft.id, true, HtmlHelper.getPreview(body)); + // Write reference text + if (ref != null && ref.content) { + String refBody = String.format("

%s %s:

\n
%s
", + Html.escapeHtml(new Date(ref.received).toString()), + Html.escapeHtml(MessageHelper.formatAddresses(ref.from)), + Helper.readText(EntityMessage.getFile(context, ref.id))); + Helper.writeText(EntityMessage.getRefFile(context, result.draft.id), refBody); + } + if ("new".equals(action)) { ArrayList uris = args.getParcelableArrayList("attachments"); if (uris != null) @@ -1841,7 +1850,7 @@ public class FragmentCompose extends FragmentBase { !MessageHelper.equal(draft.bcc, abcc) || (draft.subject == null ? subject != null : !draft.subject.equals(subject)) || last_available != available || - !body.equals(draft.read(context))); + !body.equals(Helper.readText(EntityMessage.getFile(context, draft.id)))); last_available = available; @@ -1857,7 +1866,7 @@ public class FragmentCompose extends FragmentBase { draft.subject = subject; draft.received = new Date().getTime(); db.message().updateMessage(draft); - draft.write(context, body); + Helper.writeText(EntityMessage.getFile(context, draft.id), body); db.message().setMessageContent(draft.id, true, HtmlHelper.getPreview(body)); } @@ -1908,13 +1917,19 @@ public class FragmentCompose extends FragmentBase { // Delete draft (cannot move to outbox) EntityOperation.queue(context, db, draft, EntityOperation.DELETE); + File refDraftFile = EntityMessage.getRefFile(context, draft.id); + // Copy message to outbox draft.id = null; draft.folder = db.folder().getOutbox().id; draft.uid = null; draft.ui_hide = false; draft.id = db.message().insertMessage(draft); - draft.write(getContext(), body); + Helper.writeText(EntityMessage.getFile(context, draft.id), body); + if (refDraftFile.exists()) { + File refFile = EntityMessage.getRefFile(context, draft.id); + refDraftFile.renameTo(refFile); + } // Move attachments for (EntityAttachment attachment : attachments) @@ -2044,10 +2059,6 @@ public class FragmentCompose extends FragmentBase { private void showDraft(EntityMessage draft) { Bundle args = new Bundle(); args.putLong("id", draft.id); - if (draft.replying != null) - args.putLong("reference", draft.replying); - else if (draft.forwarding != null) - args.putLong("reference", draft.forwarding); args.putBoolean("show_images", show_images); new SimpleTask() { @@ -2066,21 +2077,21 @@ public class FragmentCompose extends FragmentBase { @Override protected Spanned[] onExecute(final Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - final long reference = args.getLong("reference", -1); + final long id = args.getLong("id"); final boolean show_images = args.getBoolean("show_images", false); - String body = EntityMessage.read(context, id); + String body = Helper.readText(EntityMessage.getFile(context, id)); Spanned spannedBody = Html.fromHtml(body, cidGetter, null); - String quote = (reference < 0 ? null : HtmlHelper.getQuote(context, reference, true)); Spanned spannedReference = null; - if (quote != null) { + File refFile = EntityMessage.getRefFile(context, id); + if (refFile.exists()) { + String quote = Helper.readText(refFile); Spanned spannedQuote = Html.fromHtml(quote, new Html.ImageGetter() { @Override public Drawable getDrawable(String source) { - Drawable image = HtmlHelper.decodeImage(source, context, reference, show_images); + Drawable image = HtmlHelper.decodeImage(source, context, id, show_images); float width = context.getResources().getDisplayMetrics().widthPixels - Helper.dp2pixels(context, 12); // margins; diff --git a/app/src/main/java/eu/faircode/email/FragmentWebView.java b/app/src/main/java/eu/faircode/email/FragmentWebView.java index e6990395d1..1eb304c9e4 100644 --- a/app/src/main/java/eu/faircode/email/FragmentWebView.java +++ b/app/src/main/java/eu/faircode/email/FragmentWebView.java @@ -141,7 +141,7 @@ public class FragmentWebView extends FragmentBase { protected String onExecute(Context context, Bundle args) throws Throwable { long id = args.getLong("id"); - String html = EntityMessage.read(context, id); + String html = Helper.readText(EntityMessage.getFile(context, id)); Document doc = Jsoup.parse(html); for (Element img : doc.select("img")) diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index 0cd060ff4f..eb216d6529 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -57,9 +57,12 @@ import com.sun.mail.imap.IMAPStore; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -328,7 +331,7 @@ public class Helper { draft.subject = context.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME + " debug info"; draft.received = new Date().getTime(); draft.id = db.message().insertMessage(draft); - draft.write(context, body); + writeText(EntityMessage.getFile(context, draft.id), body); db.message().setMessageContent(draft.id, true, HtmlHelper.getPreview(body)); attachSettings(context, draft.id, 1); @@ -600,6 +603,34 @@ public class Helper { return TextUtils.join("@", a); } + static void writeText(File file, String content) throws IOException { + BufferedWriter out = null; + try { + out = new BufferedWriter(new FileWriter(file)); + out.write(content == null ? "" : content); + } finally { + if (out != null) + out.close(); + } + } + + static String readText(File file) throws IOException { + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(file)); + StringBuilder body = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + body.append(line); + body.append('\n'); + } + return body.toString(); + } finally { + if (in != null) + in.close(); + } + } + static void copy(File src, File dst) throws IOException { InputStream in = new BufferedInputStream(new FileInputStream(src)); try { diff --git a/app/src/main/java/eu/faircode/email/HtmlHelper.java b/app/src/main/java/eu/faircode/email/HtmlHelper.java index 12eba65247..400a88a4d4 100644 --- a/app/src/main/java/eu/faircode/email/HtmlHelper.java +++ b/app/src/main/java/eu/faircode/email/HtmlHelper.java @@ -24,7 +24,6 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.text.Html; import android.text.TextUtils; import android.util.Base64; @@ -47,7 +46,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Arrays; -import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -246,17 +244,6 @@ public class HtmlHelper { } } - static String getQuote(Context context, long id, boolean sanitize) throws IOException { - EntityMessage message = DB.getInstance(context).message().getMessage(id); - if (message == null) - return null; - String html = EntityMessage.read(context, id); - return String.format("

%s %s:

\n
%s
", - Html.escapeHtml(new Date(message.received).toString()), - Html.escapeHtml(MessageHelper.formatAddresses(message.from)), - sanitize ? sanitize(html, true) : getBody(html)); - } - static String getPreview(String body) { String text = (body == null ? null : Jsoup.parse(body).text()); return (text == null ? null : text.substring(0, Math.min(text.length(), PREVIEW_SIZE))); diff --git a/app/src/main/java/eu/faircode/email/JobDaily.java b/app/src/main/java/eu/faircode/email/JobDaily.java index e0fd698275..57aa7f9e82 100644 --- a/app/src/main/java/eu/faircode/email/JobDaily.java +++ b/app/src/main/java/eu/faircode/email/JobDaily.java @@ -36,7 +36,6 @@ public class JobDaily extends JobService { private ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); private static final long CLEANUP_INTERVAL = 4 * 3600 * 1000L; // milliseconds - private static final long FILE_DELETE_THRESHOLD = 30 * 60 * 1000L; // milliseconds private static final long CACHE_IMAGE_DURATION = 3 * 24 * 3600 * 1000L; // milliseconds private static final long KEEP_LOG_DURATION = 24 * 3600 * 1000L; // milliseconds @@ -102,29 +101,40 @@ public class JobDaily extends JobService { Log.i("Cleanup message files"); File[] messages = new File(context.getFilesDir(), "messages").listFiles(); if (messages != null) - for (File file : messages) - if (file.isFile() && (now - file.lastModified()) > FILE_DELETE_THRESHOLD) { - long id = Long.parseLong(file.getName()); - if (db.message().countMessage(id) == 0) { - Log.i("Cleanup message id=" + id); - if (!file.delete()) - Log.w("Error deleting " + file); - } + for (File file : messages) { + long id = Long.parseLong(file.getName()); + if (db.message().countMessage(id) == 0) { + Log.i("Cleanup message id=" + id); + if (!file.delete()) + Log.w("Error deleting " + file); + } + } + + // Cleanup message files + Log.i("Cleanup reference files"); + File[] references = new File(context.getFilesDir(), "references").listFiles(); + if (references != null) + for (File file : references) { + long id = Long.parseLong(file.getName()); + if (db.message().countMessage(id) == 0) { + Log.i("Cleanup message id=" + id); + if (!file.delete()) + Log.w("Error deleting " + file); } + } // Cleanup attachment files Log.i("Cleanup attachment files"); File[] attachments = new File(context.getFilesDir(), "attachments").listFiles(); if (attachments != null) - for (File file : attachments) - if (file.isFile() && (now - file.lastModified()) > FILE_DELETE_THRESHOLD) { - long id = Long.parseLong(file.getName()); - if (db.attachment().countAttachment(id) == 0) { - Log.i("Cleanup attachment id=" + id); - if (!file.delete()) - Log.w("Error deleting " + file); - } + for (File file : attachments) { + long id = Long.parseLong(file.getName()); + if (db.attachment().countAttachment(id) == 0) { + Log.i("Cleanup attachment id=" + id); + if (!file.delete()) + Log.w("Error deleting " + file); } + } // Cleanup cached images Log.i("Cleanup cached image files"); diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index b244687383..26989d74e1 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -196,14 +196,10 @@ public class MessageHelper { DB db = DB.getInstance(context); MimeMessageEx imessage = new MimeMessageEx(isession, message.msgid); - EntityMessage replying = null; - if (message.replying != null) - replying = db.message().getMessage(message.replying); - - if (replying != null) { - imessage.addHeader("In-Reply-To", replying.msgid); - imessage.addHeader("References", (replying.references == null ? "" : replying.references + " ") + replying.msgid); - } + if (message.references != null) + imessage.addHeader("References", message.references); + if (message.inreplyto != null) + imessage.addHeader("In-Reply-To", message.inreplyto); imessage.addHeader("X-FairEmail-ID", message.msgid); imessage.addHeader("X-FairEmail-Thread", message.thread); @@ -300,13 +296,8 @@ public class MessageHelper { static void build(Context context, EntityMessage message, MimeMessage imessage) throws IOException, MessagingException { DB db = DB.getInstance(context); - String html = message.read(context); - if (message.replying != null || message.forwarding != null) - html += HtmlHelper.getQuote(context, - message.replying == null ? message.forwarding : message.replying, false); - StringBuilder body = new StringBuilder(); - body.append(html); + body.append(Helper.readText(EntityMessage.getFile(context, message.id))); if (Helper.isPro(context) && message.identity != null) { EntityIdentity identity = db.identity().getIdentity(message.identity); @@ -314,6 +305,10 @@ public class MessageHelper { body.append(identity.signature); } + File refFile = EntityMessage.getRefFile(context, message.id); + if (refFile.exists()) + body.append(Helper.readText(refFile)); + String plainContent = HtmlHelper.getText(body.toString()); StringBuilder htmlContent = new StringBuilder(); @@ -338,7 +333,7 @@ public class MessageHelper { alternativePart.addBodyPart(htmlPart); List cids = new ArrayList<>(); - for (Element element : Jsoup.parse(html).select("img")) { + for (Element element : Jsoup.parse(body.toString()).select("img")) { String src = element.attr("src"); if (src.startsWith("cid:")) cids.add("<" + src.substring(4) + ">"); diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 55412da257..281abc781c 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -668,7 +668,7 @@ public class ServiceSynchronize extends LifecycleService { if (message.content) try { - String body = message.read(this); + String body = Helper.readText(EntityMessage.getFile(this, message.id)); StringBuilder sb = new StringBuilder(); if (!TextUtils.isEmpty(message.subject)) sb.append(message.subject).append("
"); @@ -1793,10 +1793,10 @@ public class ServiceSynchronize extends LifecycleService { Long sid = null; try { // Append replied/forwarded text - String body = message.read(this); - if (message.replying != null || message.forwarding != null) - body += HtmlHelper.getQuote(this, - message.replying == null ? message.forwarding : message.replying, false); + String body = Helper.readText(EntityMessage.getFile(this, message.id)); + File refFile = EntityMessage.getRefFile(this, message.id); + if (refFile.exists()) + body += Helper.readText(refFile); EntityFolder sent = db.folder().getFolderByType(ident.account, EntityFolder.SENT); if (sent != null) { @@ -1811,7 +1811,7 @@ public class ServiceSynchronize extends LifecycleService { message.ui_browsed = true; // prevent deleting on sync message.error = null; message.id = db.message().insertMessage(message); - message.write(this, body); + Helper.writeText(EntityMessage.getFile(this, message.id), body); sid = message.id; message.id = id; @@ -1835,7 +1835,7 @@ public class ServiceSynchronize extends LifecycleService { db.message().setMessageSeen(message.id, true); db.message().setMessageUiSeen(message.id, true); db.message().setMessageError(message.id, null); - message.write(this, body); + Helper.writeText(EntityMessage.getFile(this, message.id), body); } else { db.message().setMessageSent(sid, imessage.getSentDate().getTime()); db.message().setMessageUiHide(sid, false); @@ -1853,9 +1853,14 @@ public class ServiceSynchronize extends LifecycleService { db.endTransaction(); } - if (message.replying != null) { - EntityMessage replying = db.message().getMessage(message.replying); - EntityOperation.queue(this, db, replying, EntityOperation.ANSWERED, true); + if (refFile.exists()) + refFile.delete(); + + if (message.inreplyto != null) { + List replieds = db.message().getMessageByMsgId(message.account, message.inreplyto); + for (EntityMessage replied : replieds) + if (replied.uid != null) + EntityOperation.queue(this, db, replied, EntityOperation.ANSWERED, true); } db.identity().setIdentityError(ident.id, null); @@ -1967,7 +1972,7 @@ public class ServiceSynchronize extends LifecycleService { MessageHelper.MessageParts parts = helper.getMessageParts(); String body = parts.getHtml(this); String preview = HtmlHelper.getPreview(body); - message.write(this, body); + Helper.writeText(EntityMessage.getFile(this, message.id), body); db.message().setMessageContent(message.id, true, preview); db.message().setMessageWarning(message.id, parts.getWarnings(message.warning)); } @@ -2677,7 +2682,7 @@ public class ServiceSynchronize extends LifecycleService { if (!message.content) { if (!metered || (message.size != null && message.size < maxSize)) { String body = parts.getHtml(context); - message.write(context, body); + Helper.writeText(EntityMessage.getFile(context, message.id), body); db.message().setMessageContent(message.id, true, HtmlHelper.getPreview(body)); db.message().setMessageWarning(message.id, parts.getWarnings(message.warning)); Log.i(folder.name + " downloaded message id=" + message.id + " size=" + message.size); diff --git a/app/src/main/java/eu/faircode/email/ViewModelBrowse.java b/app/src/main/java/eu/faircode/email/ViewModelBrowse.java index c355592793..8f902415b1 100644 --- a/app/src/main/java/eu/faircode/email/ViewModelBrowse.java +++ b/app/src/main/java/eu/faircode/email/ViewModelBrowse.java @@ -117,7 +117,7 @@ public class ViewModelBrowse extends ViewModel { String body = null; if (message.content) try { - body = message.read(state.context); + body = Helper.readText(EntityMessage.getFile(state.context, message.id)); } catch (IOException ex) { Log.e(ex); }