diff --git a/app/src/main/java/eu/faircode/email/AdapterRule.java b/app/src/main/java/eu/faircode/email/AdapterRule.java index ac1cdf4095..f3d684c2ec 100644 --- a/app/src/main/java/eu/faircode/email/AdapterRule.java +++ b/app/src/main/java/eu/faircode/email/AdapterRule.java @@ -168,10 +168,11 @@ public class AdapterRule extends RecyclerView.Adapter { JSONObject jaction = new JSONObject(rule.action); String to = null; + boolean resend = false; int type = jaction.getInt("type"); if (type == EntityRule.TYPE_SNOOZE) { int duration = jaction.optInt("duration", 0); - setAction(type, Integer.toString(duration)); + setAction(getAction(type), Integer.toString(duration)); } else if (type == EntityRule.TYPE_IMPORTANCE) { int importance = jaction.optInt("value"); @@ -183,15 +184,17 @@ public class AdapterRule extends RecyclerView.Adapter { else if (importance == EntityMessage.PRIORITIY_HIGH) value = context.getString(R.string.title_importance_high); - setAction(type, value); + setAction(getAction(type), value); } else if (type == EntityRule.TYPE_KEYWORD) { - setAction(type, jaction.optString("keyword")); + setAction(getAction(type), jaction.optString("keyword")); } else if (type == EntityRule.TYPE_ANSWER) { to = jaction.optString("to"); - if (!TextUtils.isEmpty(to)) - setAction(type, to); + if (!TextUtils.isEmpty(to)) { + resend = jaction.optBoolean("resend"); + setAction(resend ? R.string.title_rule_resend : getAction(type), to); + } } else - setAction(type, null); + setAction(getAction(type), null); if (type == EntityRule.TYPE_MOVE || type == EntityRule.TYPE_COPY || (type == EntityRule.TYPE_ANSWER && TextUtils.isEmpty(to))) { @@ -228,7 +231,7 @@ public class AdapterRule extends RecyclerView.Adapter { if (id != AdapterRule.this.getItemId(pos)) return; - setAction(args.getInt("type"), value); + setAction(getAction(args.getInt("type")), value); } @Override @@ -453,74 +456,58 @@ public class AdapterRule extends RecyclerView.Adapter { return true; } - private void setAction(int type, String value) { - int resid; + private void setAction(int resid, String value) { + if (TextUtils.isEmpty(value)) + tvAction.setText(resid); + else { + SpannableStringBuilder ssb = new SpannableStringBuilderEx(); + ssb.append(context.getString(resid)); + ssb.append(" \""); + int start = ssb.length(); + ssb.append(value); + ssb.setSpan(new StyleSpan(Typeface.ITALIC), start, ssb.length(), 0); + ssb.append("\""); + tvAction.setText(ssb); + } + } + + private int getAction(int type) { switch (type) { case EntityRule.TYPE_NOOP: - resid = R.string.title_rule_noop; - break; + return R.string.title_rule_noop; case EntityRule.TYPE_SEEN: - resid = R.string.title_rule_seen; - break; + return R.string.title_rule_seen; case EntityRule.TYPE_UNSEEN: - resid = R.string.title_rule_unseen; - break; + return R.string.title_rule_unseen; case EntityRule.TYPE_HIDE: - resid = R.string.title_rule_hide; - break; + return R.string.title_rule_hide; case EntityRule.TYPE_IGNORE: - resid = R.string.title_rule_ignore; - break; + return R.string.title_rule_ignore; case EntityRule.TYPE_SNOOZE: - resid = R.string.title_rule_snooze; - break; + return R.string.title_rule_snooze; case EntityRule.TYPE_FLAG: - resid = R.string.title_rule_flag; - break; + return R.string.title_rule_flag; case EntityRule.TYPE_IMPORTANCE: - resid = R.string.title_rule_importance; - break; + return R.string.title_rule_importance; case EntityRule.TYPE_KEYWORD: - resid = R.string.title_rule_keyword; - break; + return R.string.title_rule_keyword; case EntityRule.TYPE_MOVE: - resid = R.string.title_rule_move; - break; + return R.string.title_rule_move; case EntityRule.TYPE_COPY: - resid = R.string.title_rule_copy; - break; + return R.string.title_rule_copy; case EntityRule.TYPE_ANSWER: - resid = R.string.title_rule_answer; - break; + return R.string.title_rule_answer; case EntityRule.TYPE_TTS: - resid = R.string.title_rule_tts; - break; + return R.string.title_rule_tts; case EntityRule.TYPE_AUTOMATION: - resid = R.string.title_rule_automation; - break; + return R.string.title_rule_automation; case EntityRule.TYPE_DELETE: - resid = R.string.title_rule_delete; - break; + return R.string.title_rule_delete; case EntityRule.TYPE_SOUND: - resid = R.string.title_rule_sound; - break; + return R.string.title_rule_sound; default: throw new IllegalArgumentException("Unknown action type=" + type); } - - - if (TextUtils.isEmpty(value)) - tvAction.setText(resid); - else { - SpannableStringBuilder ssb = new SpannableStringBuilderEx(); - ssb.append(context.getString(resid)); - ssb.append(" \""); - int start = ssb.length(); - ssb.append(value); - ssb.setSpan(new StyleSpan(Typeface.ITALIC), start, ssb.length(), 0); - ssb.append("\""); - tvAction.setText(ssb); - } } } diff --git a/app/src/main/java/eu/faircode/email/EntityRule.java b/app/src/main/java/eu/faircode/email/EntityRule.java index 3ed104d85a..554f48c218 100644 --- a/app/src/main/java/eu/faircode/email/EntityRule.java +++ b/app/src/main/java/eu/faircode/email/EntityRule.java @@ -638,6 +638,7 @@ public class EntityRule { private boolean onActionAnswer(Context context, EntityMessage message, JSONObject jargs) { DB db = DB.getInstance(context); String to = jargs.optString("to"); + boolean resend = jargs.optBoolean("resend"); boolean attachments = jargs.optBoolean("attachments"); if (TextUtils.isEmpty(to) && @@ -661,6 +662,11 @@ public class EntityRule { EntityOperation.queue(context, message, EntityOperation.ATTACHMENT, attachment.id); } + if (resend && message.headers == null) { + complete = false; + EntityOperation.queue(context, message, EntityOperation.HEADERS); + } + if (!complete) { EntityOperation.queue(context, message, EntityOperation.RULE, this.id); return false; @@ -692,6 +698,7 @@ public class EntityRule { boolean original_text = jargs.optBoolean("original_text", true); boolean attachments = jargs.optBoolean("attachments"); String to = jargs.optString("to"); + boolean resend = jargs.optBoolean("resend"); boolean cc = jargs.optBoolean("cc"); boolean isReply = TextUtils.isEmpty(to); @@ -707,7 +714,7 @@ public class EntityRule { throw new IllegalArgumentException("Rule identity not found name=" + rule.name); EntityAnswer answer; - if (aid < 0) { + if (aid < 0 || resend) { if (isReply) throw new IllegalArgumentException("Rule template missing name=" + rule.name); @@ -753,7 +760,11 @@ public class EntityRule { reply.thread = message.thread; reply.to = (message.reply == null || message.reply.length == 0 ? message.from : message.reply); } else { - reply.wasforwardedfrom = message.msgid; + if (resend) { + reply.resend = true; + reply.headers = message.headers; + } else + reply.wasforwardedfrom = message.msgid; reply.thread = reply.msgid; // new thread reply.to = MessageHelper.parseAddresses(context, to); } @@ -761,12 +772,16 @@ public class EntityRule { reply.from = from; if (cc) reply.cc = message.cc; - reply.unsubscribe = "mailto:" + identity.email; + if (isReply) + reply.unsubscribe = "mailto:" + identity.email; reply.auto_submitted = true; - reply.subject = EntityMessage.getSubject(context, - message.language, - answer_subject ? answer.name : message.subject, - !isReply); + if (resend) + reply.subject = message.subject; + else + reply.subject = EntityMessage.getSubject(context, + message.language, + answer_subject ? answer.name : message.subject, + !isReply); reply.received = new Date().getTime(); reply.sender = MessageHelper.getSortKey(reply.from); @@ -775,29 +790,34 @@ public class EntityRule { reply.id = db.message().insertMessage(reply); - String body = answer.getHtml(message.from); + String body; + if (resend) + body = Helper.readText(message.getFile(context)); + else { + body = answer.getHtml(message.from); - if (original_text) { - Document msg = JsoupEx.parse(body); + if (original_text) { + Document msg = JsoupEx.parse(body); - Element div = msg.createElement("div"); + Element div = msg.createElement("div"); - Element p = message.getReplyHeader(context, msg, separate_reply, extended_reply); - div.appendChild(p); + Element p = message.getReplyHeader(context, msg, separate_reply, extended_reply); + div.appendChild(p); - Document answering = JsoupEx.parse(message.getFile(context)); - Element e = answering.body(); - if (quote) { - String style = e.attr("style"); - style = HtmlHelper.mergeStyles(style, HtmlHelper.getQuoteStyle(e)); - e.tagName("blockquote").attr("style", style); - } else - e.tagName("p"); - div.appendChild(e); + Document answering = JsoupEx.parse(message.getFile(context)); + Element e = answering.body(); + if (quote) { + String style = e.attr("style"); + style = HtmlHelper.mergeStyles(style, HtmlHelper.getQuoteStyle(e)); + e.tagName("blockquote").attr("style", style); + } else + e.tagName("p"); + div.appendChild(e); - msg.body().appendChild(div); + msg.body().appendChild(div); - body = msg.outerHtml(); + body = msg.outerHtml(); + } } File file = reply.getFile(context); @@ -812,7 +832,7 @@ public class EntityRule { reply.preview, null); - if (attachments) + if (attachments || resend) EntityAttachment.copy(context, message.id, reply.id); EntityOperation.queue(context, reply, EntityOperation.SEND); diff --git a/app/src/main/java/eu/faircode/email/FragmentRule.java b/app/src/main/java/eu/faircode/email/FragmentRule.java index 75db0cf238..dcddc0b544 100644 --- a/app/src/main/java/eu/faircode/email/FragmentRule.java +++ b/app/src/main/java/eu/faircode/email/FragmentRule.java @@ -32,7 +32,9 @@ import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.speech.tts.TextToSpeech; +import android.text.Editable; import android.text.TextUtils; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -145,6 +147,7 @@ public class FragmentRule extends FragmentBase { private CheckBox cbWithAttachments; private EditText etTo; private ImageButton ibTo; + private CheckBox cbResend; private CheckBox cbCc; private Button btnTtsSetup; @@ -297,6 +300,7 @@ public class FragmentRule extends FragmentBase { cbWithAttachments = view.findViewById(R.id.cbWithAttachments); etTo = view.findViewById(R.id.etTo); ibTo = view.findViewById(R.id.ibTo); + cbResend = view.findViewById(R.id.cbResend); cbCc = view.findViewById(R.id.cbCc); btnTtsSetup = view.findViewById(R.id.btnTtsSetup); @@ -617,6 +621,21 @@ public class FragmentRule extends FragmentBase { spIdent.setOnItemSelectedListener(onItemSelectedListener); spAnswer.setOnItemSelectedListener(onItemSelectedListener); + etTo.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + cbResend.setEnabled(!TextUtils.isEmpty(s.toString())); + } + }); + ibTo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -625,6 +644,17 @@ public class FragmentRule extends FragmentBase { } }); + cbResend.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + checked = (checked && compoundButton.isEnabled()); + spAnswer.setEnabled(!checked); + cbAnswerSubject.setEnabled(!checked); + cbOriginalText.setEnabled(!checked); + cbWithAttachments.setEnabled(!checked); + } + }); + btnTtsSetup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1146,6 +1176,7 @@ public class FragmentRule extends FragmentBase { cbWithAttachments.setChecked(jaction.optBoolean("attachments")); etTo.setText(jaction.optString("to")); + cbResend.setChecked(jaction.optBoolean("resend")); cbCc.setChecked(jaction.optBoolean("cc")); break; @@ -1488,6 +1519,7 @@ public class FragmentRule extends FragmentBase { jaction.put("original_text", cbOriginalText.isChecked()); jaction.put("attachments", cbWithAttachments.isChecked()); jaction.put("to", etTo.getText().toString().trim()); + jaction.put("resend", cbResend.isChecked()); jaction.put("cc", cbCc.isChecked()); break; diff --git a/app/src/main/res/layout/fragment_rule.xml b/app/src/main/res/layout/fragment_rule.xml index 8d32314dc5..a34722b143 100644 --- a/app/src/main/res/layout/fragment_rule.xml +++ b/app/src/main/res/layout/fragment_rule.xml @@ -902,6 +902,15 @@ app:layout_constraintTop_toTopOf="@id/etTo" app:srcCompat="@drawable/twotone_person_add_24" /> + + + app:layout_constraintTop_toBottomOf="@id/cbResend" /> + app:constraint_referenced_ids="tvAnswerIdentity,spIdent,tvAnswerTemplate,spAnswer,cbAnswerSubject,cbOriginalText,cbWithAttachments,tvTo,etTo,ibTo,cbResend,cbCc,tvAnswerRemark" /> Use template name as subject Include original message text Forward to + Resend Reply to CC addresses With attachments Only one reply will be sent for any conversation, to avoid reply loops