From b6ee20e56f29872da5ffc1390a054c3e907393a5 Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 22 May 2019 19:37:46 +0200 Subject: [PATCH] Folder selection dialog --- .../faircode/email/AdapterFolderSelect.java | 184 +++++++++++++++++ .../eu/faircode/email/AdapterMessage.java | 188 ++++++++++-------- .../eu/faircode/email/FragmentMessages.java | 66 ++++-- .../main/res/layout/dialog_folder_select.xml | 26 +++ .../main/res/layout/item_folder_select.xml | 39 ++++ app/src/main/res/values/strings.xml | 4 +- 6 files changed, 401 insertions(+), 106 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/AdapterFolderSelect.java create mode 100644 app/src/main/res/layout/dialog_folder_select.xml create mode 100644 app/src/main/res/layout/item_folder_select.xml diff --git a/app/src/main/java/eu/faircode/email/AdapterFolderSelect.java b/app/src/main/java/eu/faircode/email/AdapterFolderSelect.java new file mode 100644 index 0000000000..7b694467d6 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/AdapterFolderSelect.java @@ -0,0 +1,184 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +public class AdapterFolderSelect extends RecyclerView.Adapter { + private Context context; + private LifecycleOwner owner; + private IFolderSelectedListener listener; + private LayoutInflater inflater; + + private List items = new ArrayList<>(); + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private View view; + private ImageView ivType; + private TextView tvName; + + ViewHolder(View itemView) { + super(itemView); + + view = itemView.findViewById(R.id.clItem); + ivType = itemView.findViewById(R.id.ivType); + tvName = itemView.findViewById(R.id.tvName); + } + + private void wire() { + view.setOnClickListener(this); + } + + private void unwire() { + view.setOnClickListener(null); + } + + private void bindTo(EntityFolder folder) { + ivType.setImageResource(EntityFolder.getIcon(folder.type)); + tvName.setText(folder.getDisplayName(context)); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + EntityFolder folder = items.get(pos); + if (folder != null) + listener.onFolderSelected(folder); + } + } + + AdapterFolderSelect(Context context, LifecycleOwner owner, IFolderSelectedListener listener) { + this.context = context; + this.owner = owner; + this.listener = listener; + this.inflater = LayoutInflater.from(context); + setHasStableIds(true); + } + + public void set(@NonNull List folders) { + Log.i("Set folders=" + folders.size()); + + DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, folders), false); + + items = folders; + + diff.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.i("Inserted @" + position + " #" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.i("Removed @" + position + " #" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.i("Moved " + fromPosition + ">" + toPosition); + } + + @Override + public void onChanged(int position, int count, Object payload) { + Log.i("Changed @" + position + " #" + count); + } + }); + diff.dispatchUpdatesTo(this); + } + + private class DiffCallback extends DiffUtil.Callback { + private List prev = new ArrayList<>(); + private List next = new ArrayList<>(); + + DiffCallback(List prev, List next) { + this.prev.addAll(prev); + this.next.addAll(next); + } + + @Override + public int getOldListSize() { + return prev.size(); + } + + @Override + public int getNewListSize() { + return next.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + EntityFolder m1 = prev.get(oldItemPosition); + EntityFolder m2 = next.get(newItemPosition); + return m1.id.equals(m2.id); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + EntityFolder m1 = prev.get(oldItemPosition); + EntityFolder m2 = next.get(newItemPosition); + return m1.id.equals(m2.id); + } + } + + @Override + public long getItemId(int position) { + return items.get(position).id; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.item_folder_select, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + EntityFolder folder = items.get(position); + holder.bindTo(folder); + holder.wire(); + } + + interface IFolderSelectedListener { + void onFolderSelected(EntityFolder folder); + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 98050da2a6..63acbbfa3a 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -2455,6 +2455,66 @@ public class AdapterMessage extends RecyclerView.Adapter() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + long target = args.getLong("target"); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + EntityMessage message = db.message().getMessage(id); + if (message == null) + return null; + + EntityOperation.queue(context, message, EntityOperation.COPY, target); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(context, owner, ex); + } + }.execute(context, owner, args, "message:copy"); + } + }); + + rvFolder.setAdapter(adapter); + + rvFolder.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); + dialog.show(); + Bundle args = new Bundle(); args.putLong("id", data.message.id); @@ -2471,10 +2531,15 @@ public class AdapterMessage extends RecyclerView.Adapter 0) - Collections.sort(folders, folders.get(0).getComparator(context)); + List targets = new ArrayList<>(); + for (EntityFolder folder : folders) + if (!folder.isHidden(context) && !folder.id.equals(message.folder)) + targets.add(folder); - return folders; + if (targets.size() > 0) + Collections.sort(targets, targets.get(0).getComparator(context)); + + return targets; } @Override @@ -2482,53 +2547,9 @@ public class AdapterMessage extends RecyclerView.Adapter() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - long target = args.getLong("target"); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - EntityMessage message = db.message().getMessage(id); - if (message == null) - return null; - - EntityOperation.queue(context, message, EntityOperation.COPY, target); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(context, owner, ex); - } - }.execute(context, owner, args, "message:copy"); - - return true; - } - }); - - popupMenu.show(); + adapter.set(folders); + pbWait.setVisibility(View.GONE); + rvFolder.setVisibility(View.VISIBLE); } @Override @@ -3115,7 +3136,33 @@ public class AdapterMessage extends RecyclerView.Adapter() { - @Override - protected String onExecute(Context context, Bundle args) { - long target = args.getLong("target"); - return DB.getInstance(context).folder().getFolder(target).name; - } - - @Override - protected void onExecuted(Bundle args, String folderName) { - long id = args.getLong("id"); - properties.move(id, folderName, false); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(context, owner, ex); - } - }.execute(context, owner, args, "message:move"); - - return true; - } - }); - - popupMenu.show(); + adapter.set(folders); + pbWait.setVisibility(View.GONE); + rvFolder.setVisibility(View.VISIBLE); } @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index a091a5547b..7373488062 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -20,6 +20,7 @@ package eu.faircode.email; */ import android.app.Activity; +import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -1396,7 +1397,7 @@ public class FragmentMessages extends FragmentBase { if (targets.size() > 0) Collections.sort(targets, targets.get(0).getComparator(context)); - result.targets.put(account, targets); + result.targets.put(account.id, targets); } return result; @@ -1423,25 +1424,22 @@ public class FragmentMessages extends FragmentBase { if (result.hasArchive && !result.isArchive) // has archive and not is archive/drafts popupMenu.getMenu().add(Menu.NONE, R.string.title_archive, 7, R.string.title_archive); + int order = 8; + for (EntityAccount account : result.accounts) { + MenuItem item = popupMenu.getMenu() + .add(Menu.NONE, R.string.title_move_to_account, order++, + getString(R.string.title_move_to_account, account.name)); + item.setIntent(new Intent().putExtra("account", account.id)); + } + if (result.isTrash) // is trash - popupMenu.getMenu().add(Menu.NONE, R.string.title_delete, 8, R.string.title_delete); + popupMenu.getMenu().add(Menu.NONE, R.string.title_delete, order++, R.string.title_delete); if (!result.isTrash && result.hasTrash) // not trash and has trash - popupMenu.getMenu().add(Menu.NONE, R.string.title_trash, 9, R.string.title_trash); + popupMenu.getMenu().add(Menu.NONE, R.string.title_trash, order++, R.string.title_trash); if (result.hasJunk && !result.isJunk && !result.isDrafts) // has junk and not junk/drafts - popupMenu.getMenu().add(Menu.NONE, R.string.title_spam, 10, R.string.title_spam); - - int order = 11; - for (EntityAccount account : result.accounts) { - SubMenu smenu = popupMenu.getMenu() - .addSubMenu(Menu.NONE, 0, order++, getString(R.string.title_move_to, account.name)); - int sorder = 1; - for (EntityFolder target : result.targets.get(account)) { - MenuItem item = smenu.add(Menu.NONE, R.string.title_move_to, sorder++, target.getDisplayName(getContext())); - item.setIntent(new Intent().putExtra("target", target.id)); - } - } + popupMenu.getMenu().add(Menu.NONE, R.string.title_spam, order++, R.string.title_spam); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override @@ -1477,8 +1475,9 @@ public class FragmentMessages extends FragmentBase { case R.string.title_spam: onActionJunkSelection(); return true; - case R.string.title_move_to: - onActionMoveSelection(target.getIntent().getLongExtra("target", -1)); + case R.string.title_move_to_account: + long account = target.getIntent().getLongExtra("account", -1); + onActionMoveSelectionAccount(result.targets.get(account)); return true; default: return false; @@ -1829,6 +1828,37 @@ public class FragmentMessages extends FragmentBase { }.execute(FragmentMessages.this, args, "messages:move"); } + private void onActionMoveSelectionAccount(List folders) { + final View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_folder_select, null); + final RecyclerView rvFolder = dview.findViewById(R.id.rvFolder); + final ContentLoadingProgressBar pbWait = dview.findViewById(R.id.pbWait); + + final Dialog dialog = new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) + .setTitle(R.string.title_move_to_folder) + .setView(dview) + .create(); + + rvFolder.setHasFixedSize(false); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + rvFolder.setLayoutManager(llm); + + final AdapterFolderSelect adapter = new AdapterFolderSelect(getContext(), getViewLifecycleOwner(), + new AdapterFolderSelect.IFolderSelectedListener() { + @Override + public void onFolderSelected(EntityFolder folder) { + dialog.dismiss(); + onActionMoveSelection(folder.id); + } + }); + adapter.set(folders); + + rvFolder.setAdapter(adapter); + + rvFolder.setVisibility(View.VISIBLE); + pbWait.setVisibility(View.GONE); + dialog.show(); + } + private void onActionMoveSelection(long target) { Bundle args = new Bundle(); args.putLongArray("ids", getSelection()); @@ -3273,7 +3303,7 @@ public class FragmentMessages extends FragmentBase { Boolean isJunk; Boolean isDrafts; List accounts; - Map> targets = new HashMap<>(); + Map> targets = new HashMap<>(); } private static class MessageTarget implements Parcelable { diff --git a/app/src/main/res/layout/dialog_folder_select.xml b/app/src/main/res/layout/dialog_folder_select.xml new file mode 100644 index 0000000000..9d82415598 --- /dev/null +++ b/app/src/main/res/layout/dialog_folder_select.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_folder_select.xml b/app/src/main/res/layout/item_folder_select.xml new file mode 100644 index 0000000000..2adabe76b5 --- /dev/null +++ b/app/src/main/res/layout/item_folder_select.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ 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 a777a31419..faa1af218a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -404,12 +404,14 @@ Trash Copy … + Copy to … Subscribe Delete More Spam Move - Move to %1$s + Move to … + Move to %1$s … Snooze … Archive Reply