diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f2c0c083d..3bcd2565fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ ### [Caudipteryx](https://en.wikipedia.org/wiki/Caudipteryx) +### Next version + +* Added text contains filter rule condition +* Small improvements and minor bug fixes +* Updated translations + ### 1.1784 - 2021-12-08 * Added display option to override widths in original message view diff --git a/app/src/main/assets/CHANGELOG.md b/app/src/main/assets/CHANGELOG.md index 7f2c0c083d..3bcd2565fe 100644 --- a/app/src/main/assets/CHANGELOG.md +++ b/app/src/main/assets/CHANGELOG.md @@ -4,6 +4,12 @@ ### [Caudipteryx](https://en.wikipedia.org/wiki/Caudipteryx) +### Next version + +* Added text contains filter rule condition +* Small improvements and minor bug fixes +* Updated translations + ### 1.1784 - 2021-12-08 * Added display option to override widths in original message view diff --git a/app/src/main/java/eu/faircode/email/AdapterRule.java b/app/src/main/java/eu/faircode/email/AdapterRule.java index fe496a0211..b8a56946b3 100644 --- a/app/src/main/java/eu/faircode/email/AdapterRule.java +++ b/app/src/main/java/eu/faircode/email/AdapterRule.java @@ -126,6 +126,8 @@ public class AdapterRule extends RecyclerView.Adapter { condition.add(context.getString(R.string.title_rule_subject)); if (jcondition.has("header")) condition.add(context.getString(R.string.title_rule_header)); + if (jcondition.has("body")) + condition.add(context.getString(R.string.title_rule_body)); if (jcondition.has("date")) condition.add(context.getString(R.string.title_rule_time_abs)); if (jcondition.has("schedule")) @@ -319,7 +321,7 @@ public class AdapterRule extends RecyclerView.Adapter { if (message == null) continue; - if (rule.matches(context, message, null)) + if (rule.matches(context, message, null, null)) if (rule.execute(context, message)) applied++; diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index b4ef1a916b..4f29f42c21 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -2800,6 +2800,7 @@ class Core { List
headers = (EntityRule.needsHeaders(rules) ? helper.getAllHeaders() : null); + String body = parts.getHtml(context); try { db.beginTransaction(); @@ -2819,7 +2820,7 @@ class Core { attachment.id = db.attachment().insertAttachment(attachment); } - runRules(context, headers, account, folder, message, rules); + runRules(context, headers, body, account, folder, message, rules); reportNewMessage(context, account, folder, message); db.setTransactionSuccessful(); @@ -2827,7 +2828,6 @@ class Core { db.endTransaction(); } - String body = parts.getHtml(context); File file = message.getFile(context); Helper.writeText(file, body); String text = HtmlHelper.getFullText(body); @@ -3656,6 +3656,8 @@ class Core { List
headers = (EntityRule.needsHeaders(rules) ? helper.getAllHeaders() : null); + String body = + (EntityRule.needsBody(rules) ? helper.getMessageParts().getHtml(context) : null); if (message == null) { Long sent = helper.getSent(); @@ -3868,7 +3870,7 @@ class Core { attachment.id = db.attachment().insertAttachment(attachment); } - runRules(context, headers, account, folder, message, rules); + runRules(context, headers, body, account, folder, message, rules); if (message.blocklist != null && message.blocklist) { boolean use_blocklist = prefs.getBoolean("use_blocklist", false); @@ -3910,7 +3912,7 @@ class Core { EntityContact.received(context, account, folder, message); // Download small messages inline - if (download && !message.ui_hide) { + if (body != null || (download && !message.ui_hide)) { long maxSize; if (state == null || state.networkState.isUnmetered()) maxSize = MessageHelper.SMALL_MESSAGE_SIZE; @@ -3920,10 +3922,12 @@ class Core { maxSize = MessageHelper.SMALL_MESSAGE_SIZE; } - if ((message.size != null && message.size < maxSize) || + if (body != null || + (message.size != null && message.size < maxSize) || (MessageClassifier.isEnabled(context)) && folder.auto_classify_source) try { - String body = parts.getHtml(context); + if (body == null) + body = parts.getHtml(context); File file = message.getFile(context); Helper.writeText(file, body); String text = HtmlHelper.getFullText(body); @@ -4078,7 +4082,7 @@ class Core { db.message().updateMessage(message); if (process) - runRules(context, headers, account, folder, message, rules); + runRules(context, headers, body, account, folder, message, rules); db.setTransactionSuccessful(); } finally { @@ -4241,7 +4245,7 @@ class Core { } private static void runRules( - Context context, List
headers, + Context context, List
headers, String html, EntityAccount account, EntityFolder folder, EntityMessage message, List rules) { @@ -4254,7 +4258,7 @@ class Core { try { boolean executed = false; for (EntityRule rule : rules) - if (rule.matches(context, message, headers)) { + if (rule.matches(context, message, headers, html)) { rule.execute(context, message); executed = true; if (rule.stop) diff --git a/app/src/main/java/eu/faircode/email/EntityRule.java b/app/src/main/java/eu/faircode/email/EntityRule.java index bf675e421a..9b1ef48b9b 100644 --- a/app/src/main/java/eu/faircode/email/EntityRule.java +++ b/app/src/main/java/eu/faircode/email/EntityRule.java @@ -128,6 +128,10 @@ public class EntityRule { return needs(rules, "header"); } + static boolean needsBody(List rules) { + return needs(rules, "body"); + } + private static boolean needs(List rules, String what) { for (EntityRule rule : rules) try { @@ -141,7 +145,7 @@ public class EntityRule { return false; } - boolean matches(Context context, EntityMessage message, List
headers) throws MessagingException { + boolean matches(Context context, EntityMessage message, List
headers, String html) throws MessagingException { try { JSONObject jcondition = new JSONObject(condition); @@ -297,6 +301,29 @@ public class EntityRule { } } + // Body + JSONObject jbody = jcondition.optJSONObject("body"); + if (jbody != null) { + String value = jbody.getString("value"); + boolean regex = jbody.getBoolean("regex"); + + if (html == null && message.content) { + File file = message.getFile(context); + try { + html = Helper.readText(file); + } catch (IOException ex) { + Log.e(ex); + } + } + + if (html == null) + throw new IllegalArgumentException(context.getString(R.string.title_rule_no_body)); + + String text = HtmlHelper.getFullText(html); + if (!matches(context, message, value, text, regex)) + return false; + } + // Date JSONObject jdate = jcondition.optJSONObject("date"); if (jdate != null) { @@ -329,6 +356,7 @@ public class EntityRule { jsubject == null && !jcondition.optBoolean("attachments") && jheader == null && + jbody == null && jdate == null && jschedule == null) return false; diff --git a/app/src/main/java/eu/faircode/email/FragmentFolders.java b/app/src/main/java/eu/faircode/email/FragmentFolders.java index 8d1927b6cb..243409b4ea 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolders.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolders.java @@ -1070,7 +1070,7 @@ public class FragmentFolders extends FragmentBase { for (EntityRule rule : rules) { EntityLog.log(context, "Executing rules evaluating=" + rule.name); - if (rule.matches(context, message, null)) { + if (rule.matches(context, message, null, null)) { EntityLog.log(context, "Executing rules matches=" + rule.name); if (rule.execute(context, message)) { EntityLog.log(context, "Executing rules applied=" + rule.name); diff --git a/app/src/main/java/eu/faircode/email/FragmentRule.java b/app/src/main/java/eu/faircode/email/FragmentRule.java index b1a4269d55..223d113502 100644 --- a/app/src/main/java/eu/faircode/email/FragmentRule.java +++ b/app/src/main/java/eu/faircode/email/FragmentRule.java @@ -105,6 +105,9 @@ public class FragmentRule extends FragmentBase { private EditText etHeader; private CheckBox cbHeader; + private EditText etBody; + private CheckBox cbBody; + private TextView tvDateAfter; private TextView tvDateBefore; private Button btnDateAfter; @@ -240,6 +243,9 @@ public class FragmentRule extends FragmentBase { etHeader = view.findViewById(R.id.etHeader); cbHeader = view.findViewById(R.id.cbHeader); + etBody = view.findViewById(R.id.etBody); + cbBody = view.findViewById(R.id.cbBody); + tvDateAfter = view.findViewById(R.id.tvDateAfter); tvDateBefore = view.findViewById(R.id.tvDateBefore); btnDateAfter = view.findViewById(R.id.btnDateAfter); @@ -877,6 +883,7 @@ public class FragmentRule extends FragmentBase { JSONObject jrecipient = jcondition.optJSONObject("recipient"); JSONObject jsubject = jcondition.optJSONObject("subject"); JSONObject jheader = jcondition.optJSONObject("header"); + JSONObject jbody = jcondition.optJSONObject("body"); JSONObject jdate = jcondition.optJSONObject("date"); JSONObject jschedule = jcondition.optJSONObject("schedule"); @@ -905,6 +912,9 @@ public class FragmentRule extends FragmentBase { etHeader.setText(jheader == null ? null : jheader.getString("value")); cbHeader.setChecked(jheader != null && jheader.getBoolean("regex")); + etBody.setText(jbody == null ? null : jbody.getString("value")); + cbBody.setChecked(jbody != null && jbody.getBoolean("regex")); + long after = (jdate != null && jdate.has("after") ? jdate.getLong("after") : 0); long before = (jdate != null && jdate.has("before") ? jdate.getLong("before") : 0); @@ -1118,6 +1128,7 @@ public class FragmentRule extends FragmentBase { JSONObject jrecipient = jcondition.optJSONObject("recipient"); JSONObject jsubject = jcondition.optJSONObject("subject"); JSONObject jheader = jcondition.optJSONObject("header"); + JSONObject jbody = jcondition.optJSONObject("body"); JSONObject jdate = jcondition.optJSONObject("date"); JSONObject jschedule = jcondition.optJSONObject("schedule"); @@ -1126,6 +1137,7 @@ public class FragmentRule extends FragmentBase { jsubject == null && !jcondition.optBoolean("attachments") && jheader == null && + jbody == null && jdate == null && jschedule == null) throw new IllegalArgumentException(context.getString(R.string.title_rule_condition_missing)); @@ -1220,6 +1232,14 @@ public class FragmentRule extends FragmentBase { jcondition.put("header", jheader); } + String body = etBody.getText().toString(); + if (!TextUtils.isEmpty(body)) { + JSONObject jbody = new JSONObject(); + jbody.put("value", body); + jbody.put("regex", cbBody.isChecked()); + jcondition.put("body", jbody); + } + Object hafter = tvDateAfter.getTag(); Object hbefore = tvDateBefore.getTag(); @@ -1441,7 +1461,7 @@ public class FragmentRule extends FragmentBase { if (message == null) continue; - if (rule.matches(context, message, null)) + if (rule.matches(context, message, null, null)) if (rule.execute(context, message)) applied++; @@ -1501,7 +1521,7 @@ public class FragmentRule extends FragmentBase { if (message == null) continue; - if (rule.matches(context, message, null)) + if (rule.matches(context, message, null, null)) matching.add(message); if (matching.size() >= MAX_CHECK) diff --git a/app/src/main/res/layout/fragment_rule.xml b/app/src/main/res/layout/fragment_rule.xml index 8fc609771d..def6561e81 100644 --- a/app/src/main/res/layout/fragment_rule.xml +++ b/app/src/main/res/layout/fragment_rule.xml @@ -408,13 +408,65 @@ app:layout_constraintTop_toBottomOf="@+id/etHeader" /> + + + + + + + + + + Has attachments Mime type Header contains + Text contains Absolute time (received) between Received after Received before @@ -1505,6 +1506,7 @@ Check Header conditions cannot be checked + Message text not available Matching messages No matching messages diff --git a/metadata/en-US/changelogs/1784.txt b/metadata/en-US/changelogs/1784.txt index 7f2c0c083d..3bcd2565fe 100644 --- a/metadata/en-US/changelogs/1784.txt +++ b/metadata/en-US/changelogs/1784.txt @@ -4,6 +4,12 @@ ### [Caudipteryx](https://en.wikipedia.org/wiki/Caudipteryx) +### Next version + +* Added text contains filter rule condition +* Small improvements and minor bug fixes +* Updated translations + ### 1.1784 - 2021-12-08 * Added display option to override widths in original message view