diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index 9deecff23a..69d923317b 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -388,7 +388,7 @@ class Core { break; case EntityOperation.MOVE: - onMove(context, jargs, account, folder, message); + onMove(context, jargs, account, folder, message, (POP3Folder) ifolder, (POP3Store) istore, state); break; case EntityOperation.DELETE: @@ -1607,7 +1607,7 @@ class Core { } } - private static void onMove(Context context, JSONArray jargs, EntityAccount account, EntityFolder folder, EntityMessage message) throws JSONException, FolderNotFoundException { + private static void onMove(Context context, JSONArray jargs, EntityAccount account, EntityFolder folder, EntityMessage message, POP3Folder ifolder, POP3Store istore, State state) throws JSONException, FolderNotFoundException, IOException, MessagingException { // Move message DB db = DB.getInstance(context); @@ -1623,6 +1623,13 @@ class Core { if (folder.id.equals(target.id)) throw new IllegalArgumentException("self"); + if (Boolean.TRUE.equals(account.leave_deleted) && + EntityFolder.INBOX.equals(folder.type) && + EntityFolder.TRASH.equals(target.type)) { + onDelete(context, jargs, account, folder, message, ifolder, istore, state); + return; + } + // Move from trash/drafts only if (!EntityFolder.DRAFTS.equals(folder.type) && !(EntityFolder.TRASH.equals(folder.type) && account.leave_deleted)) @@ -2844,10 +2851,10 @@ class Core { if (sync) { // Index IDs - Map uidlMsgId = new HashMap<>(); + Map uidlTuple = new HashMap<>(); for (TupleUidl id : ids) { if (id.uidl != null && id.msgid != null) - uidlMsgId.put(id.uidl, id.msgid); + uidlTuple.put(id.uidl, id); } Map msgIdTuple = new HashMap<>(); @@ -2922,7 +2929,8 @@ class Core { continue; } - msgid = uidlMsgId.get(uidl); + TupleUidl tuple = uidlTuple.get(uidl); + msgid = (tuple == null ? null : tuple.msgid); if (msgid == null) { msgid = helper.getMessageID(); if (TextUtils.isEmpty(msgid)) @@ -2946,18 +2954,27 @@ class Core { continue; } - if (hasUidl ? uidlMsgId.containsKey(uidl) : msgIdTuple.containsKey(msgid)) { + TupleUidl tuple = (hasUidl ? uidlTuple.get(uidl) : msgIdTuple.get(msgid)); + if (tuple != null) { _new = false; Log.i(account.name + " POP having " + msgid + "=" + msgIdTuple.containsKey(msgid) + "/" + - uidl + "=" + uidlMsgId.containsKey(uidl)); + uidl + "=" + uidlTuple.containsKey(uidl)); + + if (tuple.ui_hide) { + boolean found = false; + List threaded = db.message().getMessagesByThread(account.id, tuple.thread, null, null); + for (EntityMessage m : threaded) + if (!m.folder.equals(folder.id) && m.received > 1654034400 /* 2022-06-01 */) { + found = true; + break; + } + if (!found) + db.message().setMessageUiHide(tuple.id, false); + } if (download_eml) try { - TupleUidl tuple = msgIdTuple.get(msgid); - if (tuple == null) - continue; - File raw = EntityMessage.getRawFile(context, tuple.id); if (raw.exists()) continue; diff --git a/app/src/main/java/eu/faircode/email/DaoMessage.java b/app/src/main/java/eu/faircode/email/DaoMessage.java index 278bbd1b8b..a5d3f140f9 100644 --- a/app/src/main/java/eu/faircode/email/DaoMessage.java +++ b/app/src/main/java/eu/faircode/email/DaoMessage.java @@ -638,7 +638,7 @@ public interface DaoMessage { " AND NOT uid IS NULL") List getBusyUids(long folder, long time); - @Query("SELECT id, uidl, msgid FROM message" + + @Query("SELECT id, uidl, msgid, thread, ui_hide FROM message" + " WHERE folder = :folder") List getUidls(long folder); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 9fb62a91eb..d953a55400 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -303,6 +303,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. private ImageButton ibArchive; private ImageButton ibJunk; private ImageButton ibTrash; + private ImageButton ibDelete; private ImageButton ibMove; private ImageButton ibMoreSettings; private FloatingActionButton fabSearch; @@ -582,6 +583,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. ibArchive = view.findViewById(R.id.ibArchive); ibJunk = view.findViewById(R.id.ibJunk); ibTrash = view.findViewById(R.id.ibTrash); + ibDelete = view.findViewById(R.id.ibDelete); ibMove = view.findViewById(R.id.ibMove); ibMoreSettings = view.findViewById(R.id.ibMoreSettings); fabSearch = view.findViewById(R.id.fabSearch); @@ -1387,6 +1389,13 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. } }); + ibDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onActionDeleteSelection(); + } + }); + ibMove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -3435,12 +3444,11 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. popupMenu.getMenu().add(Menu.FIRST, R.string.title_trash, order++, R.string.title_trash) .setIcon(R.drawable.twotone_delete_24); - if (!result.read_only) { - boolean leave = (Boolean.TRUE.equals(result.leave_deleted) && result.isInbox); - popupMenu.getMenu().add(Menu.FIRST, R.string.title_delete_permanently, order++, - leave ? R.string.title_trash : R.string.title_delete_permanently) - .setIcon(leave ? R.drawable.twotone_delete_24 : R.drawable.twotone_delete_forever_24); + if (result.canDelete()) + popupMenu.getMenu().add(Menu.FIRST, R.string.title_delete_permanently, order++, R.string.title_delete_permanently) + .setIcon(R.drawable.twotone_delete_forever_24); + if (!result.read_only) { for (EntityAccount account : result.accounts.keySet()) { String title = getString(R.string.title_move_to_account, account.name); SpannableString ssb = new SpannableString(title); @@ -3523,9 +3531,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. onActionMoveSelection(EntityFolder.TRASH, false); return true; } else if (itemId == R.string.title_delete_permanently) { - onActionDeleteSelection( - result.hasPop && !result.hasImap, - result.leave_deleted != null && result.leave_deleted); + onActionDeleteSelection(); return true; } else if (itemId == R.string.title_move_to_account) { long account = target.getIntent().getLongExtra("account", -1); @@ -3845,7 +3851,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. ask.show(getParentFragmentManager(), "messages:raw"); } - private void onActionDeleteSelection(boolean popOnly, Boolean leave_delete) { + private void onActionDeleteSelection() { Bundle args = new Bundle(); args.putLongArray("selected", getSelection()); @@ -3861,7 +3867,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. for (long id : selected) { EntityMessage message = db.message().getMessage(id); - if (message == null) + if (message == null || message.ui_hide) continue; EntityAccount account = db.account().getAccount(message.account); @@ -3871,7 +3877,8 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. List messages = db.message().getMessagesByThread( message.account, message.thread, threading ? null : id, message.folder); for (EntityMessage threaded : messages) - if (message.uid != null || account.protocol != EntityAccount.TYPE_IMAP) + if (!threaded.ui_hide && + (threaded.uid != null || account.protocol != EntityAccount.TYPE_IMAP)) ids.add(threaded.id); } @@ -3886,28 +3893,13 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. @Override protected void onExecuted(Bundle args, final List ids) { Bundle aargs = new Bundle(); - if (popOnly && leave_delete) { - aargs.putString("question", getResources() - .getQuantityString(R.plurals.title_moving_messages, ids.size(), ids.size())); - aargs.putString("notagain", "delete_asked"); - } else { - aargs.putString("question", getResources() - .getQuantityString(R.plurals.title_deleting_messages, ids.size(), ids.size())); - aargs.putString("accept", getString(R.string.title_ask_delete_accept)); - } + aargs.putString("question", getResources() + .getQuantityString(R.plurals.title_deleting_messages, ids.size(), ids.size())); + aargs.putString("accept", getString(R.string.title_ask_delete_accept)); aargs.putInt("faq", 160); aargs.putLongArray("ids", Helper.toLongArray(ids)); aargs.putBoolean("warning", true); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean delete_asked = prefs.getBoolean("delete_asked", false); - if (popOnly && leave_delete && delete_asked) { - Intent data = new Intent(); - data.putExtra("args", aargs); - onActivityResult(REQUEST_MESSAGES_DELETE, RESULT_OK, data); - return; - } - FragmentDialogAsk ask = new FragmentDialogAsk(); ask.setArguments(aargs); ask.setTargetFragment(FragmentMessages.this, REQUEST_MESSAGES_DELETE); @@ -5869,6 +5861,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. boolean more_archive = prefs.getBoolean("more_archive", true); boolean more_junk = prefs.getBoolean("more_junk", true); boolean more_trash = prefs.getBoolean("more_trash", true); + boolean more_delete = prefs.getBoolean("more_delete", false); boolean more_move = prefs.getBoolean("more_move", true); int count = 0; @@ -5877,6 +5870,10 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. if (move) count++; + boolean delete = (more_delete && count < MAX_QUICK_ACTIONS && result.canDelete()); + if (delete) + count++; + boolean trash = (more_trash && count < MAX_QUICK_ACTIONS && result.canTrash()); if (trash) count++; @@ -5934,6 +5931,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. ibArchive.setVisibility(archive ? View.VISIBLE : View.GONE); ibJunk.setVisibility(junk ? View.VISIBLE : View.GONE); ibTrash.setVisibility(trash ? View.VISIBLE : View.GONE); + ibDelete.setVisibility(delete ? View.VISIBLE : View.GONE); ibMove.setVisibility(move ? View.VISIBLE : View.GONE); cardMore.setTag(fabMore.isOrWillBeShown() ? result : null); cardMore.setVisibility(fabMore.isOrWillBeShown() ? View.VISIBLE : View.GONE); @@ -9670,7 +9668,14 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. boolean canTrash() { if (read_only) return false; - return (!isTrash && hasTrash && !isJunk); + return (!isTrash && hasTrash && !isJunk) || + (hasPop && Boolean.TRUE.equals(leave_deleted) && isInbox); + } + + boolean canDelete() { + if (read_only) + return false; + return (!hasPop || !Boolean.TRUE.equals(leave_deleted) || !isInbox); } boolean canMove() { @@ -10294,6 +10299,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. final CheckBox cbArchive = dview.findViewById(R.id.cbArchive); final CheckBox cbJunk = dview.findViewById(R.id.cbJunk); final CheckBox cbTrash = dview.findViewById(R.id.cbTrash); + final CheckBox cbDelete = dview.findViewById(R.id.cbDelete); final CheckBox cbMove = dview.findViewById(R.id.cbMove); tvHint.setText(getString(R.string.title_quick_actions_hint, MAX_QUICK_ACTIONS)); @@ -10308,6 +10314,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. cbArchive.setChecked(prefs.getBoolean("more_archive", true)); cbJunk.setChecked(prefs.getBoolean("more_junk", true)); cbTrash.setChecked(prefs.getBoolean("more_trash", true)); + cbDelete.setChecked(prefs.getBoolean("more_delete", false)); cbMove.setChecked(prefs.getBoolean("more_move", true)); return new AlertDialog.Builder(getContext()) @@ -10327,6 +10334,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. editor.putBoolean("more_archive", cbArchive.isChecked()); editor.putBoolean("more_junk", cbJunk.isChecked()); editor.putBoolean("more_trash", cbTrash.isChecked()); + editor.putBoolean("more_delete", cbDelete.isChecked()); editor.putBoolean("more_move", cbMove.isChecked()); editor.apply(); sendResult(Activity.RESULT_OK); diff --git a/app/src/main/java/eu/faircode/email/TupleUidl.java b/app/src/main/java/eu/faircode/email/TupleUidl.java index 52ea555e65..81497f6dea 100644 --- a/app/src/main/java/eu/faircode/email/TupleUidl.java +++ b/app/src/main/java/eu/faircode/email/TupleUidl.java @@ -23,4 +23,6 @@ public class TupleUidl { long id; String uidl; String msgid; + String thread; + boolean ui_hide; } diff --git a/app/src/main/res/layout/dialog_quick_actions.xml b/app/src/main/res/layout/dialog_quick_actions.xml index 536e658385..c76df68c41 100644 --- a/app/src/main/res/layout/dialog_quick_actions.xml +++ b/app/src/main/res/layout/dialog_quick_actions.xml @@ -174,6 +174,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbJunk" /> + + + app:layout_constraintTop_toBottomOf="@id/cbDelete" /> diff --git a/app/src/main/res/layout/fragment_messages.xml b/app/src/main/res/layout/fragment_messages.xml index 9d868a7c86..b0685e1439 100644 --- a/app/src/main/res/layout/fragment_messages.xml +++ b/app/src/main/res/layout/fragment_messages.xml @@ -667,15 +667,29 @@ android:layout_width="48dp" android:layout_height="48dp" android:background="?android:attr/selectableItemBackgroundBorderless" - android:contentDescription="@string/title_delete" + android:contentDescription="@string/title_trash" android:padding="6dp" android:scaleType="fitCenter" android:tint="@color/action_foreground" - android:tooltipText="@string/title_delete" + android:tooltipText="@string/title_trash" app:layout_constraintEnd_toStartOf="@id/ibJunk" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/twotone_delete_24" /> + +