From a6da17417eed1412dfbd9c06d03d85f100d35608 Mon Sep 17 00:00:00 2001 From: Daniƫl van den Berg Date: Fri, 9 Sep 2016 11:47:49 +0200 Subject: Added the ability to move passwords around. (#210) * Added the ability to move passwords around. * Generified the PasswordRecyclerAdapter and the FolderRecyclerAdapter into EntryRecyclerAdapter --- .../zeapo/pwdstore/utils/EntryRecyclerAdapter.java | 162 ++++++++++++++++++ .../pwdstore/utils/FolderRecyclerAdapter.java | 31 ++++ .../pwdstore/utils/PasswordRecyclerAdapter.java | 184 ++++----------------- 3 files changed, 228 insertions(+), 149 deletions(-) create mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java create mode 100644 app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java (limited to 'app/src/main/java/com/zeapo/pwdstore/utils') diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java new file mode 100644 index 00000000..78b81928 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/utils/EntryRecyclerAdapter.java @@ -0,0 +1,162 @@ +package com.zeapo.pwdstore.utils; + +import android.app.Activity; +import android.graphics.Color; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.zeapo.pwdstore.R; + +import java.util.ArrayList; +import java.util.Set; +import java.util.TreeSet; + +public abstract class EntryRecyclerAdapter extends RecyclerView.Adapter { + private final Activity activity; + protected final ArrayList values; + protected final Set selectedItems = new TreeSet<>(); + + public EntryRecyclerAdapter(Activity activity, ArrayList values) { + this.activity = activity; + this.values = values; + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return values.size(); + } + + public ArrayList getValues() { + return this.values; + } + + public void clear() { + this.values.clear(); + this.notifyDataSetChanged(); + } + + public void addAll(ArrayList list) { + this.values.addAll(list); + this.notifyDataSetChanged(); + } + + public void add(PasswordItem item) { + this.values.add(item); + this.notifyItemInserted(getItemCount()); + } + + public void toggleSelection(int position) { + if (!selectedItems.remove(position)) { + selectedItems.add(position); + } + } + + // use this after an item is removed to update the positions of items in set + // that followed the removed position + public void updateSelectedItems(int position, Set selectedItems) { + Set temp = new TreeSet<>(); + for (int selected : selectedItems) { + if (selected > position) { + temp.add(selected - 1); + } else { + temp.add(selected); + } + } + selectedItems.clear(); + selectedItems.addAll(temp); + } + + public void remove(int position) { + this.values.remove(position); + this.notifyItemRemoved(position); + + // keep selectedItems updated so we know what to notifyItemChanged + // (instead of just using notifyDataSetChanged) + updateSelectedItems(position, selectedItems); + } + + @NonNull + protected View.OnLongClickListener getOnLongClickListener(ViewHolder holder, PasswordItem pass) { + return new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + return false; + } + }; + } + + // Replace the contents of a view (invoked by the layout manager) + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + final PasswordItem pass = getValues().get(position); + holder.name.setText(pass.toString()); + if (pass.getType() == PasswordItem.TYPE_CATEGORY) { + holder.typeImage.setImageResource(R.drawable.ic_folder_grey600_24dp); + holder.name.setText(pass.toString() + "/"); + } else { + holder.typeImage.setImageResource(R.drawable.ic_action_secure); + holder.name.setText(pass.toString()); + } + + holder.type.setText(pass.getFullPathName()); + if (pass.getType() == PasswordItem.TYPE_CATEGORY) { +// holder.card.setCardBackgroundColor(activity.getResources().getColor(R.color.blue_grey_200)); + } else { +// holder.card.setCardBackgroundColor(activity.getResources().getColor(R.color.blue_grey_50)); + } + + holder.view.setOnClickListener(getOnClickListener(holder, pass)); + + holder.view.setOnLongClickListener(getOnLongClickListener(holder, pass)); + + // after removal, everything is rebound for some reason; views are shuffled? + boolean selected = selectedItems.contains(position); + holder.view.setSelected(selected); + if (selected) { + holder.itemView.setBackgroundResource(R.color.deep_orange_200); + holder.type.setTextColor(Color.BLACK); + } else { + holder.itemView.setBackgroundResource(Color.alpha(1)); + holder.type.setTextColor(ContextCompat.getColor(activity, R.color.grey_500)); + } + } + + @NonNull + protected abstract View.OnClickListener getOnClickListener(ViewHolder holder, PasswordItem pass); + + // Provide a reference to the views for each data item + // Complex data items may need more than one view per item, and + // you provide access to all the views for a data item in a view holder + public static class ViewHolder extends RecyclerView.ViewHolder { + // each data item is just a string in this case + public View view; + public TextView name; + public TextView type; + public ImageView typeImage; + + public ViewHolder(View v) { + super(v); + view = v; + name = (TextView) view.findViewById(R.id.label); + type = (TextView) view.findViewById(R.id.type); + typeImage = (ImageView) view.findViewById(R.id.type_image); + } + } + + // Create new views (invoked by the layout manager) + @Override + public PasswordRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, + int viewType) { + // create a new view + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.password_row_layout, parent, false); + return new ViewHolder(v); + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java new file mode 100644 index 00000000..04b243e1 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/utils/FolderRecyclerAdapter.java @@ -0,0 +1,31 @@ +package com.zeapo.pwdstore.utils; + +import android.support.annotation.NonNull; +import android.view.View; + +import com.zeapo.pwdstore.SelectFolderFragment; +import com.zeapo.pwdstore.crypto.PgpHandler; + +import java.util.ArrayList; + +public class FolderRecyclerAdapter extends EntryRecyclerAdapter { + private final SelectFolderFragment.OnFragmentInteractionListener listener; + + // Provide a suitable constructor (depends on the kind of dataset) + public FolderRecyclerAdapter(PgpHandler activity, SelectFolderFragment.OnFragmentInteractionListener listener, ArrayList values) { + super(activity, values); + this.listener = listener; + } + + @NonNull + protected View.OnClickListener getOnClickListener(final ViewHolder holder, final PasswordItem pass) { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + listener.onFragmentInteraction(pass); + notifyItemChanged(holder.getAdapterPosition()); + } + }; + } + +} diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java index 0835425a..a4446198 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRecyclerAdapter.java @@ -1,17 +1,10 @@ package com.zeapo.pwdstore.utils; -import android.graphics.Color; -import android.os.Build; -import android.support.v4.content.ContextCompat; +import android.support.annotation.NonNull; import android.support.v7.view.ActionMode; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; import com.zeapo.pwdstore.PasswordFragment; import com.zeapo.pwdstore.PasswordStore; @@ -19,76 +12,46 @@ import com.zeapo.pwdstore.R; import java.util.ArrayList; import java.util.Iterator; -import java.util.Set; import java.util.TreeSet; -public class PasswordRecyclerAdapter extends RecyclerView.Adapter { +public class PasswordRecyclerAdapter extends EntryRecyclerAdapter { private final PasswordStore activity; - private final ArrayList values; private final PasswordFragment.OnFragmentInteractionListener listener; - private final Set selectedItems; private ActionMode mActionMode; private Boolean canEdit; - // Provide a reference to the views for each data item - // Complex data items may need more than one view per item, and - // you provide access to all the views for a data item in a view holder - public static class ViewHolder extends RecyclerView.ViewHolder { - // each data item is just a string in this case - public View view; - public TextView name; - public TextView type; - public ImageView typeImage; - - public ViewHolder(View v) { - super(v); - view = v; - name = (TextView) view.findViewById(R.id.label); - type = (TextView) view.findViewById(R.id.type); - typeImage = (ImageView) view.findViewById(R.id.type_image); - } - } - // Provide a suitable constructor (depends on the kind of dataset) public PasswordRecyclerAdapter(PasswordStore activity, PasswordFragment.OnFragmentInteractionListener listener, ArrayList values) { - this.values = values; + super(activity, values); this.activity = activity; this.listener = listener; - selectedItems = new TreeSet<>(); } - // Create new views (invoked by the layout manager) @Override - public PasswordRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, - int viewType) { - // create a new view - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.password_row_layout, parent, false); - return new ViewHolder(v); + @NonNull + protected View.OnLongClickListener getOnLongClickListener(final ViewHolder holder, final PasswordItem pass) { + return new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mActionMode != null) { + return false; + } + toggleSelection(holder.getAdapterPosition()); + canEdit = pass.getType() == PasswordItem.TYPE_PASSWORD; + // Start the CAB using the ActionMode.Callback + mActionMode = activity.startSupportActionMode(mActionModeCallback); + mActionMode.setTitle("" + selectedItems.size()); + mActionMode.invalidate(); + notifyItemChanged(holder.getAdapterPosition()); + return true; + } + }; } - // Replace the contents of a view (invoked by the layout manager) @Override - public void onBindViewHolder(final ViewHolder holder, int position) { - final PasswordItem pass = values.get(position); - holder.name.setText(pass.toString()); - if (pass.getType() == PasswordItem.TYPE_CATEGORY) { - holder.typeImage.setImageResource(R.drawable.ic_folder_grey600_24dp); - holder.name.setText(pass.toString() + "/"); - } else { - holder.typeImage.setImageResource(R.drawable.ic_action_secure); - holder.name.setText(pass.toString()); - } - int sdk = Build.VERSION.SDK_INT; - - holder.type.setText(pass.getFullPathName()); - if (pass.getType() == PasswordItem.TYPE_CATEGORY) { -// holder.card.setCardBackgroundColor(activity.getResources().getColor(R.color.blue_grey_200)); - } else { -// holder.card.setCardBackgroundColor(activity.getResources().getColor(R.color.blue_grey_50)); - } - - holder.view.setOnClickListener(new View.OnClickListener() { + @NonNull + protected View.OnClickListener getOnClickListener(final ViewHolder holder, final PasswordItem pass) { + return new View.OnClickListener() { @Override public void onClick(View v) { if (mActionMode != null) { @@ -97,7 +60,7 @@ public class PasswordRecyclerAdapter extends RecyclerView.Adapter selectedPasswords = new ArrayList<>(); + for (Integer id : selectedItems) { + selectedPasswords.add(getValues().get(id)); + } + activity.movePasswords(selectedPasswords); default: return false; } @@ -183,67 +124,12 @@ public class PasswordRecyclerAdapter extends RecyclerView.Adapter it = selectedItems.iterator(); it.hasNext(); ) { // need the setSelected line in onBind - notifyItemChanged((Integer) it.next()); + notifyItemChanged(it.next()); it.remove(); } mActionMode = null; } }; - - // Return the size of your dataset (invoked by the layout manager) - @Override - public int getItemCount() { - return values.size(); - } - - public ArrayList getValues() { - return this.values; - } - - public void clear() { - this.values.clear(); - this.notifyDataSetChanged(); - } - - public void addAll(ArrayList list) { - this.values.addAll(list); - this.notifyDataSetChanged(); - } - - public void add(PasswordItem item) { - this.values.add(item); - this.notifyItemInserted(values.size()); - } - - public void remove(int position) { - this.values.remove(position); - this.notifyItemRemoved(position); - - // keep selectedItems updated so we know what to notifyItemChanged - // (instead of just using notifyDataSetChanged) - updateSelectedItems(position, selectedItems); - } - - public void toggleSelection(int position) { - if (!selectedItems.remove(position)) { - selectedItems.add(position); - } - } - - // use this after an item is removed to update the positions of items in set - // that followed the removed position - public void updateSelectedItems(int position, Set selectedItems) { - Set temp = new TreeSet<>(); - for (int selected : selectedItems) { - if (selected > position) { - temp.add(selected - 1); - } else { - temp.add(selected); - } - } - selectedItems.clear(); - selectedItems.addAll(temp); - } } -- cgit v1.2.3