From 283fb5630de72de3e95af8d2118978dffc540e19 Mon Sep 17 00:00:00 2001 From: Jakob Nixdorf Date: Sun, 7 Mar 2021 20:15:31 +0100 Subject: [PATCH] Use stable IDs for the adapter Fixes #346 --- .../flocke/andotp/Database/Entry.java | 36 +- .../flocke/andotp/Database/EntryList.java | 205 +++++++++++ .../andotp/View/EntriesCardAdapter.java | 317 ++++++------------ 3 files changed, 319 insertions(+), 239 deletions(-) create mode 100644 app/src/main/java/org/shadowice/flocke/andotp/Database/EntryList.java diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java b/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java index 04e4310c..ee7e6dee 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java @@ -79,6 +79,7 @@ public class Entry { public static final int COLOR_RED = 1; private static final int EXPIRY_TIME = 8; private int color = COLOR_DEFAULT; + private long listId = 0; public Entry(){} @@ -368,10 +369,6 @@ public class Entry { return period; } - public void setPeriod(int period) { - this.period = period; - } - public long getCounter() { return counter; } @@ -384,10 +381,6 @@ public class Entry { return digits; } - public void setDigits(int digits) { - this.digits = digits; - } - public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } @@ -400,10 +393,6 @@ public class Entry { return this.algorithm; } - public void setAlgorithm(TokenCalculator.HashAlgorithm algorithm) { - this.algorithm = algorithm; - } - public boolean hasNonDefaultPeriod() { return this.period != TokenCalculator.TOTP_DEFAULT_PERIOD; } @@ -444,6 +433,14 @@ public class Entry { return currentOTP; } + public long getListId() { + return listId; + } + + public void setListId(long newId) { + listId = newId; + } + public boolean updateOTP() { if (type == OTPType.TOTP || type == OTPType.STEAM) { long time = System.currentTimeMillis() / 1000; @@ -503,9 +500,14 @@ public class Entry { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + Entry entry = (Entry) o; + return type == entry.type && period == entry.period && counter == entry.counter && @@ -541,9 +543,9 @@ public class Entry { /** * Returns the label with issuer prefix removed (if present) - * @param issuer - * @param label - * @return + * @param issuer - Name of the issuer to remove from the label + * @param label - Full label from which the issuer should be removed + * @return - label with the issuer removed */ private String getStrippedLabel(String issuer, String label) { if (issuer == null || issuer.isEmpty() || !label.startsWith(issuer + ":")) { diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Database/EntryList.java b/app/src/main/java/org/shadowice/flocke/andotp/Database/EntryList.java new file mode 100644 index 00000000..4133b8f0 --- /dev/null +++ b/app/src/main/java/org/shadowice/flocke/andotp/Database/EntryList.java @@ -0,0 +1,205 @@ +package org.shadowice.flocke.andotp.Database; + +import org.shadowice.flocke.andotp.Utilities.Constants; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +public class EntryList { + private final ArrayList entries; + private final AtomicLong currentId = new AtomicLong(); + + public EntryList() { + entries = new ArrayList<>(); + } + + public boolean addEntry(Entry newEntry) { + return addEntry(newEntry, false); + } + + public boolean addEntry(Entry newEntry, boolean update) { + if (! entries.contains(newEntry)) { + long newId = currentId.incrementAndGet(); + newEntry.setListId(newId); + + entries.add(newEntry); + + return true; + } else { + if (update) { + int oldIdx = entries.indexOf(newEntry); + Entry oldEntry = entries.get(oldIdx); + + newEntry.setListId(oldEntry.getListId()); + entries.set(oldIdx, newEntry); + } + } + + return false; + } + + public void updateEntries(ArrayList newEntries, boolean update) { + // Remove all items not in the new list + entries.retainAll(newEntries); + + // Add new and update existing entries + for (Entry e : newEntries) { + addEntry(e, update); + } + } + + public Entry getEntry(int pos) { + return entries.get(pos); + } + + public void swapEntries(int fromPosition, int toPosition) { + Collections.swap(entries, fromPosition, toPosition); + } + + public void removeEntry(int pos) { + entries.remove(pos); + } + + public int indexOf(Entry e) { + return entries.indexOf(e); + } + + public boolean isEqual(ArrayList otherEntries) { + return entries.equals(otherEntries); + } + + public ArrayList getEntries() { + return new ArrayList<>(entries); + } + + public ArrayList getEntriesSorted(Constants.SortMode sortMode) { + return sortEntries(entries, sortMode); + } + + public static ArrayList sortEntries(ArrayList unsortedEntries, Constants.SortMode sortMode) { + ArrayList sorted = new ArrayList<>(unsortedEntries); + + if (sortMode == Constants.SortMode.ISSUER) { + Collections.sort(sorted, new IssuerComparator()); + } else if (sortMode == Constants.SortMode.LABEL) { + Collections.sort(sorted, new LabelComparator()); + } else if (sortMode == Constants.SortMode.LAST_USED) { + Collections.sort(sorted, new LastUsedComparator()); + } else if (sortMode == Constants.SortMode.MOST_USED) { + Collections.sort(sorted, new MostUsedComparator()); + } + + return sorted; + } + + public ArrayList getAllTags() { + HashSet tags = new HashSet<>(); + + for(Entry entry : entries) { + tags.addAll(entry.getTags()); + } + + return new ArrayList<>(tags); + } + + public ArrayList getFilteredEntries(CharSequence constraint, List filterValues, Constants.SortMode sortMode) { + ArrayList filtered = new ArrayList<>(); + + if (constraint != null && constraint.length() != 0){ + for (int i = 0; i < entries.size(); i++) { + if (filterValues.contains(Constants.SearchIncludes.LABEL) && entries.get(i).getLabel().toLowerCase().contains(constraint.toString().toLowerCase())) { + filtered.add(entries.get(i)); + } else if (filterValues.contains(Constants.SearchIncludes.ISSUER) && entries.get(i).getIssuer().toLowerCase().contains(constraint.toString().toLowerCase())) { + filtered.add(entries.get(i)); + } else if (filterValues.contains(Constants.SearchIncludes.TAGS)) { + List tags = entries.get(i).getTags(); + for (int j = 0; j < tags.size(); j++) { + if (tags.get(j).toLowerCase().contains(constraint.toString().toLowerCase())) { + filtered.add(entries.get(i)); + break; + } + } + } + } + } else { + filtered = entries; + } + + return sortEntries(filtered, sortMode); + } + + public ArrayList getEntriesFilteredByTags(List tags, boolean noTags, Constants.TagFunctionality tagFunctionality, Constants.SortMode sortMode) { + ArrayList matchingEntries = new ArrayList<>(); + + for(Entry e : entries) { + // Entries with no tags will always be shown + boolean foundMatchingTag = e.getTags().isEmpty() && noTags; + + if(tagFunctionality == Constants.TagFunctionality.AND) { + if(e.getTags().containsAll(tags)) { + foundMatchingTag = true; + } + } else { + for (String tag : tags) { + if (e.getTags().contains(tag)) { + foundMatchingTag = true; + break; + } + } + } + + if(foundMatchingTag) { + matchingEntries.add(e); + } + } + + return sortEntries(matchingEntries, sortMode); + } + + public static class IssuerComparator implements Comparator { + Collator collator; + + IssuerComparator(){ + collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + } + + @Override + public int compare(Entry o1, Entry o2) { + return collator.compare(o1.getIssuer(), o2.getIssuer()); + } + } + + public static class LabelComparator implements Comparator { + Collator collator; + + LabelComparator(){ + collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + } + + @Override + public int compare(Entry o1, Entry o2) { + return collator.compare(o1.getLabel(), o2.getLabel()); + } + } + + public static class LastUsedComparator implements Comparator { + @Override + public int compare(Entry o1, Entry o2) { + return Long.compare(o2.getLastUsed(), o1.getLastUsed()); + } + } + + public static class MostUsedComparator implements Comparator { + @Override + public int compare(Entry o1, Entry o2) { + return Long.compare(o2.getUsedFrequency(), o1.getUsedFrequency()); + } + } +} diff --git a/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java b/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java index 83906356..f477aa4b 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java @@ -22,6 +22,7 @@ package org.shadowice.flocke.andotp.View; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -38,10 +39,8 @@ import android.text.TextUtils; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.Filter; @@ -57,6 +56,7 @@ import com.journeyapps.barcodescanner.BarcodeEncoder; import org.shadowice.flocke.andotp.Activities.MainActivity; import org.shadowice.flocke.andotp.Database.Entry; +import org.shadowice.flocke.andotp.Database.EntryList; import org.shadowice.flocke.andotp.Dialogs.ManualEntryDialog; import org.shadowice.flocke.andotp.R; import org.shadowice.flocke.andotp.Utilities.BackupHelper; @@ -68,12 +68,8 @@ import org.shadowice.flocke.andotp.Utilities.Settings; import org.shadowice.flocke.andotp.Utilities.Tools; import org.shadowice.flocke.andotp.View.ItemTouchHelper.ItemTouchHelperAdapter; -import java.text.Collator; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -83,10 +79,10 @@ import static org.shadowice.flocke.andotp.Utilities.Constants.SortMode; public class EntriesCardAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter, Filterable { - private Context context; - private Handler taskHandler; + private final Context context; + private final Handler taskHandler; private EntryFilter filter; - private ArrayList entries; + private final EntryList entries; private ArrayList displayedEntries; private Callback callback; private List tagsFilter = new ArrayList<>(); @@ -94,15 +90,17 @@ public class EntriesCardAdapter extends RecyclerView.Adapter private SecretKey encryptionKey = null; private SortMode sortMode = SortMode.UNSORTED; - private TagsAdapter tagsFilterAdapter; - private Settings settings; + private final TagsAdapter tagsFilterAdapter; + private final Settings settings; public EntriesCardAdapter(Context context, TagsAdapter tagsFilterAdapter) { this.context = context; this.tagsFilterAdapter = tagsFilterAdapter; this.settings = new Settings(context); this.taskHandler = new Handler(); - this.entries = new ArrayList<>(); + this.entries = new EntryList(); + + setHasStableIds(true); } public void setEncryptionKey(SecretKey key) { @@ -118,8 +116,13 @@ public class EntriesCardAdapter extends RecyclerView.Adapter return displayedEntries.size(); } + @Override + public long getItemId(int position) { + return displayedEntries.get(position).getListId(); + } + public ArrayList getEntries() { - return entries; + return entries.getEntries(); } public void saveAndRefresh(boolean auto_backup) { @@ -129,8 +132,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter } public void addEntry(Entry e) { - if (! entries.contains(e)) { - entries.add(e); + if (entries.addEntry(e)) { saveAndRefresh(settings.getAutoBackupEncryptedPasswordsEnabled()); } else { Toast.makeText(context, R.string.toast_entry_exists, Toast.LENGTH_LONG).show(); @@ -142,7 +144,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter } private void entriesChanged() { - displayedEntries = sortEntries(entries); + displayedEntries = entries.getEntriesSorted(sortMode); filterByTags(tagsFilter); notifyDataSetChanged(); } @@ -170,7 +172,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter } public void saveEntries(boolean auto_backup) { - DatabaseHelper.saveDatabase(context, entries, encryptionKey); + DatabaseHelper.saveDatabase(context, entries.getEntries(), encryptionKey); if(auto_backup) { Constants.BackupType backupType = BackupHelper.autoBackupType(context); @@ -196,44 +198,24 @@ public class EntriesCardAdapter extends RecyclerView.Adapter public void loadEntries() { if (encryptionKey != null) { - entries = DatabaseHelper.loadDatabase(context, encryptionKey); + ArrayList newEntries = DatabaseHelper.loadDatabase(context, encryptionKey); + + entries.updateEntries(newEntries, true); entriesChanged(); } } public void filterByTags(List tags) { + displayedEntries = entries.getEntriesFilteredByTags(tags, settings.getNoTagsToggle(), settings.getTagFunctionality(), sortMode); tagsFilter = tags; - List matchingEntries = new ArrayList<>(); - for(Entry e : entries) { - //Entries with no tags will always be shown - Boolean foundMatchingTag = e.getTags().isEmpty() && settings.getNoTagsToggle(); - - if(settings.getTagFunctionality() == Constants.TagFunctionality.AND) { - if(e.getTags().containsAll(tags)) { - foundMatchingTag = true; - } - } else { - for (String tag : tags) { - if (e.getTags().contains(tag)) { - foundMatchingTag = true; - } - } - } - - if(foundMatchingTag) { - matchingEntries.add(e); - } - } - - displayedEntries = sortEntries(matchingEntries); notifyDataSetChanged(); } public void updateTimeBasedTokens() { boolean change = false; - for (Entry e : entries) { + for (Entry e : entries.getEntries()) { if (e.isTimeBased()) { boolean cardVisible = !settings.getTapToReveal() || e.isVisible(); @@ -348,7 +330,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter @Override public void onCounterClicked(int position) { - updateEntry(displayedEntries.get(position), entries.get(getRealIndex(position)), position); + updateEntry(displayedEntries.get(position), entries.getEntry(getRealIndex(position)), position); } @Override @@ -375,16 +357,11 @@ public class EntriesCardAdapter extends RecyclerView.Adapter if (entry.isVisible()) { hideEntry(entry); } else { - entries.get(realIndex).setHideTask(new Runnable() { - @Override - public void run() { - hideEntry(entry); - } - }); - taskHandler.postDelayed(entries.get(realIndex).getHideTask(), settings.getTapToRevealTimeout() * 1000); + entries.getEntry(realIndex).setHideTask(() -> hideEntry(entry)); + taskHandler.postDelayed(entries.getEntry(realIndex).getHideTask(), settings.getTapToRevealTimeout() * 1000); if (entry.isCounterBased()) { - updateEntry(entry, entries.get(realIndex), position); + updateEntry(entry, entries.getEntry(realIndex), position); } entry.setVisible(true); notifyItemChanged(position); @@ -409,9 +386,9 @@ public class EntriesCardAdapter extends RecyclerView.Adapter int realIndex = entries.indexOf(entry); if (realIndex >= 0) { - entries.get(realIndex).setVisible(false); - taskHandler.removeCallbacks(entries.get(realIndex).getHideTask()); - entries.get(realIndex).setHideTask(null); + entries.getEntry(realIndex).setVisible(false); + taskHandler.removeCallbacks(entries.getEntry(realIndex).getHideTask()); + entries.getEntry(realIndex).setHideTask(null); } boolean updateNeeded = updateLastUsedAndFrequency(pos, realIndex); @@ -442,25 +419,19 @@ public class EntriesCardAdapter extends RecyclerView.Adapter AlertDialog dialog = builder.setTitle(R.string.dialog_title_counter) .setView(container) - .setPositiveButton(R.string.button_save, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - int realIndex = getRealIndex(pos); - long newCounter = Long.parseLong(input.getEditableText().toString()); + .setPositiveButton(R.string.button_save, (dialogInterface, i) -> { + int realIndex = getRealIndex(pos); + long newCounter = Long.parseLong(input.getEditableText().toString()); - displayedEntries.get(pos).setCounter(newCounter); - notifyItemChanged(pos); + displayedEntries.get(pos).setCounter(newCounter); + notifyItemChanged(pos); - Entry e = entries.get(realIndex); - e.setCounter(newCounter); + Entry e = entries.getEntry(realIndex); + e.setCounter(newCounter); - saveEntries(settings.getAutoBackupEncryptedFullEnabled()); - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) {} + saveEntries(settings.getAutoBackupEncryptedFullEnabled()); }) + .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {}) .create(); addCounterValidationWatcher(input, dialog); dialog.show(); @@ -497,7 +468,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter private boolean updateLastUsedAndFrequency(int position, int realIndex) { long timeStamp = System.currentTimeMillis(); - long entryUsedFrequency = entries.get(realIndex).getUsedFrequency(); + long entryUsedFrequency = entries.getEntry(realIndex).getUsedFrequency(); if (position >= 0) { long displayEntryUsedFrequency = displayedEntries.get(position).getUsedFrequency(); @@ -505,16 +476,16 @@ public class EntriesCardAdapter extends RecyclerView.Adapter displayedEntries.get(position).setUsedFrequency(displayEntryUsedFrequency + 1); } - entries.get(realIndex).setLastUsed(timeStamp); - entries.get(realIndex).setUsedFrequency(entryUsedFrequency + 1); + entries.getEntry(realIndex).setLastUsed(timeStamp); + entries.getEntry(realIndex).setUsedFrequency(entryUsedFrequency + 1); saveEntries(false); if (sortMode == SortMode.LAST_USED) { - displayedEntries = sortEntries(displayedEntries); + displayedEntries = EntryList.sortEntries(displayedEntries, sortMode); notifyDataSetChanged(); return false; } else if (sortMode == SortMode.MOST_USED) { - displayedEntries = sortEntries(displayedEntries); + displayedEntries = EntryList.sortEntries(displayedEntries, sortMode); notifyDataSetChanged(); return false; } @@ -524,10 +495,10 @@ public class EntriesCardAdapter extends RecyclerView.Adapter @Override public boolean onItemMove(int fromPosition, int toPosition) { - if (sortMode == SortMode.UNSORTED && displayedEntries.equals(entries)) { - Collections.swap(entries, fromPosition, toPosition); + if (sortMode == SortMode.UNSORTED && entries.isEqual(displayedEntries)) { + entries.swapEntries(fromPosition, toPosition); - displayedEntries = new ArrayList<>(entries); + displayedEntries = entries.getEntries(); notifyItemMoved(fromPosition, toPosition); saveEntries(false); @@ -543,7 +514,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter int marginMedium = context.getResources().getDimensionPixelSize(R.dimen.activity_margin_medium); int realIndex = getRealIndex(pos); - final ThumbnailSelectionAdapter thumbnailAdapter = new ThumbnailSelectionAdapter(context, entries.get(realIndex).getIssuer(), entries.get(realIndex).getLabel()); + final ThumbnailSelectionAdapter thumbnailAdapter = new ThumbnailSelectionAdapter(context, entries.getEntry(realIndex).getIssuer(), entries.getEntry(realIndex).getLabel()); final EditText input = new EditText(context); input.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); @@ -590,36 +561,31 @@ public class EntriesCardAdapter extends RecyclerView.Adapter final AlertDialog alert = builder.setTitle(R.string.menu_popup_change_image) .setView(container) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) {} - }) + .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {}) .create(); - grid.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - int realIndex = getRealIndex(pos); - EntryThumbnail.EntryThumbnails thumbnail = EntryThumbnail.EntryThumbnails.Default; - try { - int realPos = thumbnailAdapter.getRealIndex(position); - thumbnail = EntryThumbnail.EntryThumbnails.values()[realPos]; - } catch (Exception e) { - e.printStackTrace(); - } - - Entry e = entries.get(realIndex); - e.setThumbnail(thumbnail); - - saveEntries(settings.getAutoBackupEncryptedFullEnabled()); - notifyItemChanged(pos); - alert.cancel(); + grid.setOnItemClickListener((parent, view, position, id) -> { + int realIndex1 = getRealIndex(pos); + EntryThumbnail.EntryThumbnails thumbnail = EntryThumbnail.EntryThumbnails.Default; + try { + int realPos = thumbnailAdapter.getRealIndex(position); + thumbnail = EntryThumbnail.EntryThumbnails.values()[realPos]; + } catch (Exception e) { + e.printStackTrace(); } + + Entry e = entries.getEntry(realIndex1); + e.setThumbnail(thumbnail); + + saveEntries(settings.getAutoBackupEncryptedFullEnabled()); + notifyItemChanged(pos); + alert.cancel(); }); alert.show(); } + @SuppressLint("StringFormatInvalid") public void removeItem(final int pos) { AlertDialog.Builder builder = new AlertDialog.Builder(context); @@ -628,22 +594,16 @@ public class EntriesCardAdapter extends RecyclerView.Adapter builder.setTitle(R.string.dialog_title_remove) .setMessage(message) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - int realIndex = getRealIndex(pos); + .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> { + int realIndex = getRealIndex(pos); - displayedEntries.remove(pos); - notifyItemRemoved(pos); + displayedEntries.remove(pos); + notifyItemRemoved(pos); - entries.remove(realIndex); - saveEntries(settings.getAutoBackupEncryptedFullEnabled()); - } - }) - .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) {} + entries.removeEntry(realIndex); + saveEntries(settings.getAutoBackupEncryptedFullEnabled()); }) + .setNegativeButton(android.R.string.no, (dialogInterface, i) -> {}) .show(); } @@ -682,26 +642,23 @@ public class EntriesCardAdapter extends RecyclerView.Adapter MenuInflater inflate = popup.getMenuInflater(); inflate.inflate(R.menu.menu_popup, popup.getMenu()); - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - int id = item.getItemId(); + popup.setOnMenuItemClickListener(item -> { + int id = item.getItemId(); - if (id == R.id.menu_popup_edit) { - ManualEntryDialog.show((MainActivity) context, settings, EntriesCardAdapter.this, entries.get(getRealIndex(pos))); - return true; - } else if(id == R.id.menu_popup_changeImage) { - changeThumbnail(pos); - return true; - } else if (id == R.id.menu_popup_remove) { - removeItem(pos); - return true; - } else if (id == R.id.menu_popup_show_qr_code) { - showQRCode(pos); - return true; - } else { - return false; - } + if (id == R.id.menu_popup_edit) { + ManualEntryDialog.show((MainActivity) context, settings, EntriesCardAdapter.this, entries.getEntry(getRealIndex(pos))); + return true; + } else if(id == R.id.menu_popup_changeImage) { + changeThumbnail(pos); + return true; + } else if (id == R.id.menu_popup_remove) { + removeItem(pos); + return true; + } else if (id == R.id.menu_popup_show_qr_code) { + showQRCode(pos); + return true; + } else { + return false; } }); popup.show(); @@ -716,22 +673,6 @@ public class EntriesCardAdapter extends RecyclerView.Adapter return this.sortMode; } - private ArrayList sortEntries(List unsorted) { - ArrayList sorted = new ArrayList<>(unsorted); - - if (sortMode == SortMode.ISSUER) { - Collections.sort(sorted, new IssuerComparator()); - } else if (sortMode == SortMode.LABEL) { - Collections.sort(sorted, new LabelComparator()); - } else if (sortMode == SortMode.LAST_USED) { - Collections.sort(sorted, new LastUsedComparator()); - } else if (sortMode == SortMode.MOST_USED) { - Collections.sort(sorted, new MostUsedComparator()); - } - - return sorted; - } - public void setCallback(Callback cb) { this.callback = cb; } @@ -749,44 +690,17 @@ public class EntriesCardAdapter extends RecyclerView.Adapter } public List getTags() { - HashSet tags = new HashSet(); - - for(Entry entry : entries) { - tags.addAll(entry.getTags()); - } - - return new ArrayList(tags); + return entries.getAllTags(); } public class EntryFilter extends Filter { - private List filterValues = settings.getSearchValues(); + private final List filterValues = settings.getSearchValues(); @Override protected FilterResults performFiltering(CharSequence constraint) { + ArrayList filtered = entries.getFilteredEntries(constraint, filterValues, sortMode); final FilterResults filterResults = new FilterResults(); - - ArrayList filtered = new ArrayList<>(); - if (constraint != null && constraint.length() != 0){ - for (int i = 0; i < entries.size(); i++) { - if (filterValues.contains(Constants.SearchIncludes.LABEL) && entries.get(i).getLabel().toLowerCase().contains(constraint.toString().toLowerCase())) { - filtered.add(entries.get(i)); - } else if (filterValues.contains(Constants.SearchIncludes.ISSUER) && entries.get(i).getIssuer().toLowerCase().contains(constraint.toString().toLowerCase())) { - filtered.add(entries.get(i)); - } else if (filterValues.contains(Constants.SearchIncludes.TAGS)) { - List tags = entries.get(i).getTags(); - for (int j = 0; j < tags.size(); j++) { - if (tags.get(j).toLowerCase().contains(constraint.toString().toLowerCase())) { - filtered.add(entries.get(i)); - break; - } - } - } - } - } else { - filtered = entries; - } - filterResults.count = filtered.size(); filterResults.values = filtered; @@ -794,54 +708,13 @@ public class EntriesCardAdapter extends RecyclerView.Adapter } @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - displayedEntries = sortEntries((ArrayList) results.values); + @SuppressWarnings("unchecked") + protected void publishResults(CharSequence constraint, @NonNull FilterResults results) { + displayedEntries = (ArrayList) results.values; notifyDataSetChanged(); } } - public class IssuerComparator implements Comparator { - Collator collator; - - IssuerComparator(){ - collator = Collator.getInstance(); - collator.setStrength(Collator.PRIMARY); - } - - @Override - public int compare(Entry o1, Entry o2) { - return collator.compare(o1.getIssuer(), o2.getIssuer()); - } - } - - public class LabelComparator implements Comparator { - Collator collator; - - LabelComparator(){ - collator = Collator.getInstance(); - collator.setStrength(Collator.PRIMARY); - } - - @Override - public int compare(Entry o1, Entry o2) { - return collator.compare(o1.getLabel(), o2.getLabel()); - } - } - - public class LastUsedComparator implements Comparator { - @Override - public int compare(Entry o1, Entry o2) { - return Long.compare(o2.getLastUsed(), o1.getLastUsed()); - } - } - - public class MostUsedComparator implements Comparator { - @Override - public int compare(Entry o1, Entry o2) { - return Long.compare(o2.getUsedFrequency(), o1.getUsedFrequency()); - } - } - public interface Callback { void onMoveEventStart(); void onMoveEventStop();