parent
ba5bb7705b
commit
283fb5630d
3 changed files with 319 additions and 239 deletions
|
@ -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<String> getTags() { return tags; }
|
||||
|
||||
public void setTags(List<String> 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 + ":")) {
|
||||
|
|
|
@ -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<Entry> 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<Entry> 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<Entry> otherEntries) {
|
||||
return entries.equals(otherEntries);
|
||||
}
|
||||
|
||||
public ArrayList<Entry> getEntries() {
|
||||
return new ArrayList<>(entries);
|
||||
}
|
||||
|
||||
public ArrayList<Entry> getEntriesSorted(Constants.SortMode sortMode) {
|
||||
return sortEntries(entries, sortMode);
|
||||
}
|
||||
|
||||
public static ArrayList<Entry> sortEntries(ArrayList<Entry> unsortedEntries, Constants.SortMode sortMode) {
|
||||
ArrayList<Entry> 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<String> getAllTags() {
|
||||
HashSet<String> tags = new HashSet<>();
|
||||
|
||||
for(Entry entry : entries) {
|
||||
tags.addAll(entry.getTags());
|
||||
}
|
||||
|
||||
return new ArrayList<>(tags);
|
||||
}
|
||||
|
||||
public ArrayList<Entry> getFilteredEntries(CharSequence constraint, List<Constants.SearchIncludes> filterValues, Constants.SortMode sortMode) {
|
||||
ArrayList<Entry> 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<String> 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<Entry> getEntriesFilteredByTags(List<String> tags, boolean noTags, Constants.TagFunctionality tagFunctionality, Constants.SortMode sortMode) {
|
||||
ArrayList<Entry> 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<Entry> {
|
||||
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<Entry> {
|
||||
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<Entry> {
|
||||
@Override
|
||||
public int compare(Entry o1, Entry o2) {
|
||||
return Long.compare(o2.getLastUsed(), o1.getLastUsed());
|
||||
}
|
||||
}
|
||||
|
||||
public static class MostUsedComparator implements Comparator<Entry> {
|
||||
@Override
|
||||
public int compare(Entry o1, Entry o2) {
|
||||
return Long.compare(o2.getUsedFrequency(), o1.getUsedFrequency());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<EntryViewHolder>
|
||||
implements ItemTouchHelperAdapter, Filterable {
|
||||
private Context context;
|
||||
private Handler taskHandler;
|
||||
private final Context context;
|
||||
private final Handler taskHandler;
|
||||
private EntryFilter filter;
|
||||
private ArrayList<Entry> entries;
|
||||
private final EntryList entries;
|
||||
private ArrayList<Entry> displayedEntries;
|
||||
private Callback callback;
|
||||
private List<String> tagsFilter = new ArrayList<>();
|
||||
|
@ -94,15 +90,17 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
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<EntryViewHolder>
|
|||
return displayedEntries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return displayedEntries.get(position).getListId();
|
||||
}
|
||||
|
||||
public ArrayList<Entry> getEntries() {
|
||||
return entries;
|
||||
return entries.getEntries();
|
||||
}
|
||||
|
||||
public void saveAndRefresh(boolean auto_backup) {
|
||||
|
@ -129,8 +132,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
}
|
||||
|
||||
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<EntryViewHolder>
|
|||
}
|
||||
|
||||
private void entriesChanged() {
|
||||
displayedEntries = sortEntries(entries);
|
||||
displayedEntries = entries.getEntriesSorted(sortMode);
|
||||
filterByTags(tagsFilter);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
@ -170,7 +172,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
}
|
||||
|
||||
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<EntryViewHolder>
|
|||
|
||||
public void loadEntries() {
|
||||
if (encryptionKey != null) {
|
||||
entries = DatabaseHelper.loadDatabase(context, encryptionKey);
|
||||
ArrayList<Entry> newEntries = DatabaseHelper.loadDatabase(context, encryptionKey);
|
||||
|
||||
entries.updateEntries(newEntries, true);
|
||||
entriesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void filterByTags(List<String> tags) {
|
||||
displayedEntries = entries.getEntriesFilteredByTags(tags, settings.getNoTagsToggle(), settings.getTagFunctionality(), sortMode);
|
||||
tagsFilter = tags;
|
||||
List<Entry> 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<EntryViewHolder>
|
|||
|
||||
@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<EntryViewHolder>
|
|||
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<EntryViewHolder>
|
|||
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<EntryViewHolder>
|
|||
|
||||
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) {
|
||||
.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);
|
||||
|
||||
Entry e = entries.get(realIndex);
|
||||
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) {}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {})
|
||||
.create();
|
||||
addCounterValidationWatcher(input, dialog);
|
||||
dialog.show();
|
||||
|
@ -497,7 +468,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
|
||||
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<EntryViewHolder>
|
|||
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<EntryViewHolder>
|
|||
|
||||
@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<EntryViewHolder>
|
|||
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,16 +561,11 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
|
||||
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);
|
||||
grid.setOnItemClickListener((parent, view, position, id) -> {
|
||||
int realIndex1 = getRealIndex(pos);
|
||||
EntryThumbnail.EntryThumbnails thumbnail = EntryThumbnail.EntryThumbnails.Default;
|
||||
try {
|
||||
int realPos = thumbnailAdapter.getRealIndex(position);
|
||||
|
@ -608,18 +574,18 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Entry e = entries.get(realIndex);
|
||||
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<EntryViewHolder>
|
|||
|
||||
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) {
|
||||
.setPositiveButton(android.R.string.yes, (dialogInterface, i) -> {
|
||||
int realIndex = getRealIndex(pos);
|
||||
|
||||
displayedEntries.remove(pos);
|
||||
notifyItemRemoved(pos);
|
||||
|
||||
entries.remove(realIndex);
|
||||
entries.removeEntry(realIndex);
|
||||
saveEntries(settings.getAutoBackupEncryptedFullEnabled());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, (dialogInterface, i) -> {})
|
||||
.show();
|
||||
}
|
||||
|
||||
|
@ -682,13 +642,11 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
MenuInflater inflate = popup.getMenuInflater();
|
||||
inflate.inflate(R.menu.menu_popup, popup.getMenu());
|
||||
|
||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
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)));
|
||||
ManualEntryDialog.show((MainActivity) context, settings, EntriesCardAdapter.this, entries.getEntry(getRealIndex(pos)));
|
||||
return true;
|
||||
} else if(id == R.id.menu_popup_changeImage) {
|
||||
changeThumbnail(pos);
|
||||
|
@ -702,7 +660,6 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
popup.show();
|
||||
}
|
||||
|
@ -716,22 +673,6 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
|
|||
return this.sortMode;
|
||||
}
|
||||
|
||||
private ArrayList<Entry> sortEntries(List<Entry> unsorted) {
|
||||
ArrayList<Entry> 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<EntryViewHolder>
|
|||
}
|
||||
|
||||
public List<String> getTags() {
|
||||
HashSet<String> tags = new HashSet<String>();
|
||||
|
||||
for(Entry entry : entries) {
|
||||
tags.addAll(entry.getTags());
|
||||
}
|
||||
|
||||
return new ArrayList<String>(tags);
|
||||
return entries.getAllTags();
|
||||
}
|
||||
|
||||
public class EntryFilter extends Filter {
|
||||
private List<Constants.SearchIncludes> filterValues = settings.getSearchValues();
|
||||
private final List<Constants.SearchIncludes> filterValues = settings.getSearchValues();
|
||||
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
ArrayList<Entry> filtered = entries.getFilteredEntries(constraint, filterValues, sortMode);
|
||||
|
||||
final FilterResults filterResults = new FilterResults();
|
||||
|
||||
ArrayList<Entry> 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<String> 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<EntryViewHolder>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
displayedEntries = sortEntries((ArrayList<Entry>) results.values);
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void publishResults(CharSequence constraint, @NonNull FilterResults results) {
|
||||
displayedEntries = (ArrayList<Entry>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public class IssuerComparator implements Comparator<Entry> {
|
||||
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<Entry> {
|
||||
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<Entry> {
|
||||
@Override
|
||||
public int compare(Entry o1, Entry o2) {
|
||||
return Long.compare(o2.getLastUsed(), o1.getLastUsed());
|
||||
}
|
||||
}
|
||||
|
||||
public class MostUsedComparator implements Comparator<Entry> {
|
||||
@Override
|
||||
public int compare(Entry o1, Entry o2) {
|
||||
return Long.compare(o2.getUsedFrequency(), o1.getUsedFrequency());
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onMoveEventStart();
|
||||
void onMoveEventStop();
|
||||
|
|
Loading…
Reference in a new issue