diff --git a/app/src/main/java/eu/faircode/email/AdapterContact.java b/app/src/main/java/eu/faircode/email/AdapterContact.java index 2c98f0b3ef..111c892aa2 100644 --- a/app/src/main/java/eu/faircode/email/AdapterContact.java +++ b/app/src/main/java/eu/faircode/email/AdapterContact.java @@ -126,6 +126,9 @@ public class AdapterContact extends RecyclerView.Adapter identities = Core.getIdentities(folder.account, context); if (identities != null) { for (Address sender : message.from) { for (EntityIdentity identity : identities) if (identity.similarAddress(sender)) { + if (type == TYPE_JUNK) + return; type = TYPE_TO; break; } @@ -143,6 +153,8 @@ public class EntityContact implements Serializable { return; if (type == TYPE_FROM && !suggest_received) return; + if (type == TYPE_JUNK && !auto_junk) + return; List
addresses = new ArrayList<>(); if (type == TYPE_FROM) { @@ -156,8 +168,29 @@ public class EntityContact implements Serializable { addresses.addAll(Arrays.asList(message.to)); if (message.cc != null) addresses.addAll(Arrays.asList(message.cc)); + } else if (type == TYPE_JUNK) { + if (message.from != null) { + DB db = DB.getInstance(context); + for (Address from : message.from) { + String email = ((InternetAddress) from).getAddress(); + if (TextUtils.isEmpty(email)) + continue; + EntityContact nojunk = db.contact().getContact(message.account, TYPE_NO_JUNK, email); + if (nojunk != null) + return; + } + addresses.addAll(Arrays.asList(message.from)); + } } + update(context, folder.account, addresses.toArray(new Address[0]), type, message.received); + } + + public static void update(Context context, long account, Address[] addresses, int type, long time) { + if (addresses == null) + return; + + DB db = DB.getInstance(context); for (Address address : addresses) { String email = ((InternetAddress) address).getAddress(); String name = ((InternetAddress) address).getPersonal(); @@ -171,17 +204,17 @@ public class EntityContact implements Serializable { try { db.beginTransaction(); - EntityContact contact = db.contact().getContact(folder.account, type, email); + EntityContact contact = db.contact().getContact(account, type, email); if (contact == null) { contact = new EntityContact(); - contact.account = folder.account; + contact.account = account; contact.type = type; contact.email = email; contact.name = name; contact.avatar = (avatar == null ? null : avatar.toString()); contact.times_contacted = 1; - contact.first_contacted = message.received; - contact.last_contacted = message.received; + contact.first_contacted = time; + contact.last_contacted = time; contact.id = db.contact().insertContact(contact); Log.i("Inserted contact=" + contact + " type=" + type); } else { @@ -189,8 +222,8 @@ public class EntityContact implements Serializable { contact.name = name; contact.avatar = (avatar == null ? null : avatar.toString()); contact.times_contacted++; - contact.first_contacted = Math.min(contact.first_contacted, message.received); - contact.last_contacted = message.received; + contact.first_contacted = Math.min(contact.first_contacted, time); + contact.last_contacted = time; db.contact().updateContact(contact); Log.i("Updated contact=" + contact + " type=" + type); } @@ -202,6 +235,19 @@ public class EntityContact implements Serializable { } } + public static void delete(Context context, long account, Address[] addresses, int type) { + if (addresses == null) + return; + + DB db = DB.getInstance(context); + for (Address address : addresses) { + String email = ((InternetAddress) address).getAddress(); + if (TextUtils.isEmpty(email)) + continue; + db.contact().deleteContact(account, type, email); + } + } + public JSONObject toJSON() throws JSONException { JSONObject json = new JSONObject(); json.put("id", id); diff --git a/app/src/main/java/eu/faircode/email/EntityOperation.java b/app/src/main/java/eu/faircode/email/EntityOperation.java index efeef2d439..1b1c29c625 100644 --- a/app/src/main/java/eu/faircode/email/EntityOperation.java +++ b/app/src/main/java/eu/faircode/email/EntityOperation.java @@ -246,13 +246,19 @@ public class EntityOperation { EntityMessage.snooze(context, message.id, null); } - if (EntityFolder.JUNK.equals(source.type) && EntityFolder.INBOX.equals(target.type)) { + if (EntityFolder.JUNK.equals(source.type)) { List rules = db.rule().getRules(target.id); for (EntityRule rule : rules) if (rule.isBlockingSender(message, source)) db.rule().deleteRule(rule.id); + + EntityContact.delete(context, message.account, message.from, EntityContact.TYPE_JUNK); + EntityContact.update(context, message.account, message.from, EntityContact.TYPE_NO_JUNK, message.received); } + if (EntityFolder.JUNK.equals(target.type)) + EntityContact.delete(context, message.account, message.from, EntityContact.TYPE_NO_JUNK); + // Create copy without uid in target folder // Message with same msgid can be in archive if (message.uid != null && diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogJunk.java b/app/src/main/java/eu/faircode/email/FragmentDialogJunk.java index 57caff8e08..40812c640b 100644 --- a/app/src/main/java/eu/faircode/email/FragmentDialogJunk.java +++ b/app/src/main/java/eu/faircode/email/FragmentDialogJunk.java @@ -64,6 +64,7 @@ public class FragmentDialogJunk extends FragmentDialogBase { final ImageButton ibMore = view.findViewById(R.id.ibMore); final TextView tvMore = view.findViewById(R.id.tvMore); final Button btnEditRules = view.findViewById(R.id.btnEditRules); + final CheckBox cbJunkAuto = view.findViewById(R.id.cbJunkAuto); final CheckBox cbJunkFilter = view.findViewById(R.id.cbJunkFilter); final ImageButton ibInfoFilter = view.findViewById(R.id.ibInfoFilter); final CheckBox cbBlocklist = view.findViewById(R.id.cbBlocklist); @@ -73,6 +74,7 @@ public class FragmentDialogJunk extends FragmentDialogBase { final Group grpMore = view.findViewById(R.id.grpMore); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + boolean auto_junk = prefs.getBoolean("auto_junk", false); boolean check_blocklist = prefs.getBoolean("check_blocklist", false); boolean use_blocklist = prefs.getBoolean("use_blocklist", false); @@ -88,7 +90,7 @@ public class FragmentDialogJunk extends FragmentDialogBase { cbBlockSender.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - cbBlockDomain.setEnabled(isChecked); + cbBlockDomain.setEnabled(isChecked && ActivityBilling.isPro(context)); } }); @@ -156,6 +158,13 @@ public class FragmentDialogJunk extends FragmentDialogBase { } }); + cbJunkAuto.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean("auto_junk", isChecked).apply(); + } + }); + cbJunkFilter.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @@ -241,9 +250,10 @@ public class FragmentDialogJunk extends FragmentDialogBase { tvMessage.setText(inJunk ? getString(R.string.title_folder_junk) : getString(R.string.title_ask_spam_who, from)); - cbBlockSender.setEnabled(canBlock && ActivityBilling.isPro(context)); + cbBlockSender.setEnabled(canBlock); cbBlockDomain.setEnabled(false); ibMore.setImageLevel(1); + cbJunkAuto.setChecked(auto_junk); cbBlocklist.setChecked(check_blocklist && use_blocklist); tvBlocklist.setText(TextUtils.join(", ", DnsBlockList.getNamesEnabled(context))); grpInJunk.setVisibility(inJunk ? View.GONE : View.VISIBLE); @@ -256,7 +266,7 @@ public class FragmentDialogJunk extends FragmentDialogBase { } @Override - protected Boolean onExecute(Context context, Bundle args) throws Throwable { + protected Boolean onExecute(Context context, Bundle args) { long aid = args.getLong("account"); long fid = args.getLong("folder"); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index fa49b551d6..381c6cfbd4 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -7827,6 +7827,10 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. if (message == null) return null; + EntityAccount account = db.account().getAccount(message.account); + if (account == null) + return null; + EntityFolder junk = db.folder().getFolderByType(message.account, EntityFolder.JUNK); if (junk == null) throw new IllegalArgumentException(context.getString(R.string.title_no_junk_folder)); @@ -7834,7 +7838,12 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. if (!message.folder.equals(junk.id)) EntityOperation.queue(context, message, EntityOperation.MOVE, junk.id); - if (block_sender || block_domain) { + if (block_sender) + EntityContact.update(context, + message.account, message.from, + EntityContact.TYPE_JUNK, message.received); + + if (block_domain) { EntityRule rule = EntityRule.blockSender(context, message, junk, block_domain, whitelist); if (rule != null) { if (message.folder.equals(junk.id)) { diff --git a/app/src/main/res/layout/dialog_junk.xml b/app/src/main/res/layout/dialog_junk.xml index cf8b2748f6..4bde9774e3 100644 --- a/app/src/main/res/layout/dialog_junk.xml +++ b/app/src/main/res/layout/dialog_junk.xml @@ -117,6 +117,30 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvMore" /> + + + + + app:layout_constraintTop_toBottomOf="@id/tvJunkAutoHint" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 314e3e72d9..f1fe23f6c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,11 +94,13 @@ Block sender Block sender domain - Blocking a sender will create a rule to automatically move future messages to the spam folder. - Creating and using rules is a pro feature. + Blocking a sender is a free feature. + Blocking a sender domain uses filter rules, which is a pro feature. + Automatically block spam senders Use local spam filter + This will block senders of new messages in the spam folder This can increase battery usage and incorrectly mark messages as spam Use spam block lists