diff --git a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java index 9d7effbb..6d41e410 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java +++ b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java @@ -235,27 +235,6 @@ public class Preferences { _prefs.edit().putString("pref_usage_count", usageCountJson.toString()).apply(); } - public List getFavorites() { - List favorites = new ArrayList<>(); - Set favoritesStringSet = _prefs.getStringSet("pref_favorites", null); - if(favoritesStringSet != null) { - for (String favorite : favoritesStringSet) { - favorites.add(UUID.fromString(favorite)); - } - } - - return favorites; - } - - public void setFavorites(List favorites) { - Set favoritesHashSet = new HashSet(); - for (UUID favorite : favorites) { - favoritesHashSet.add(favorite.toString()); - } - - _prefs.edit().putStringSet("pref_favorites", favoritesHashSet).apply(); - } - public int getTimeout() { return _prefs.getInt("pref_timeout", -1); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/SimpleItemTouchHelperCallback.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/SimpleItemTouchHelperCallback.java index 4ac8c79e..8aac5597 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/SimpleItemTouchHelperCallback.java +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/SimpleItemTouchHelperCallback.java @@ -33,7 +33,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { return; } - if (!entry.getIsFavorited()) { + if (!entry.isFavorite()) { _selectedEntry = entry; } } @@ -65,7 +65,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { - if(target.getAdapterPosition() < _adapter.getFavorites().size()){ + if (target.getAdapterPosition() < _adapter.getShownFavoritesCount()){ return false; } _adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); @@ -87,6 +87,4 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { _positionChanged = false; } } - - } diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/FavoriteComparator.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/FavoriteComparator.java index 095c4008..fc16f009 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/FavoriteComparator.java +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/FavoriteComparator.java @@ -7,6 +7,6 @@ import java.util.Comparator; public class FavoriteComparator implements Comparator { @Override public int compare(VaultEntry a, VaultEntry b) { - return -1 * Boolean.compare(a.getIsFavorited(), b.getIsFavorited()); + return -1 * Boolean.compare(a.isFavorite(), b.isFavorite()); } } \ No newline at end of file diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index 95a4419e..3930c20c 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -176,15 +176,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene @Override protected void onPause() { Map usageMap = _entryListView.getUsageCounts(); - List favoritesList = _entryListView.getFavorites(); if (usageMap != null) { _prefs.setUsageCount(usageMap); } - if (favoritesList != null) { - _prefs.setFavorites(favoritesList); - } - super.onPause(); } @@ -779,7 +774,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene private void loadEntries() { if (!_loaded) { _entryListView.setUsageCounts(_prefs.getUsageCounts()); - _entryListView.setFavorites(_prefs.getFavorites()); _entryListView.addEntries(_vaultManager.getVault().getEntries()); _entryListView.runEntriesAnimation(); _loaded = true; @@ -893,7 +887,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene MenuItem toggleFavoriteMenuItem = _actionMode.getMenu().findItem(R.id.action_toggle_favorite); if (_selectedEntries.size() == 1){ - if (_selectedEntries.get(0).getIsFavorited()) { + if (_selectedEntries.get(0).isFavorite()) { toggleFavoriteMenuItem.setIcon(R.drawable.ic_set_favorite); toggleFavoriteMenuItem.setTitle(R.string.unfavorite); } else { @@ -906,10 +900,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene } } - private void toggleFavorite(VaultEntry entry) { - _entryListView.toggleFavoriteState(entry); - } - @Override public void onLongEntryClick(VaultEntry entry) { if (!_selectedEntries.isEmpty()) { @@ -1062,7 +1052,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - if(_selectedEntries.size() == 0) { + if (_selectedEntries.size() == 0) { mode.finish(); return true; } @@ -1078,10 +1068,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene return true; case R.id.action_toggle_favorite: - for (VaultEntry entry : _selectedEntries) { - toggleFavorite(entry); - } + for (VaultEntry entry : _selectedEntries) { + entry.setIsFavorite(!entry.isFavorite()); + _entryListView.replaceEntry(entry.getUUID(), entry); + } + _entryListView.refresh(true); + saveAndBackupVault(); mode.finish(); return true; diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java index 26e4f520..6ddb1eb1 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java @@ -42,7 +42,6 @@ public class EntryAdapter extends RecyclerView.Adapter private List _shownEntries; private List _selectedEntries; private Map _usageCounts; - private List _favorites; private VaultEntry _focusedEntry; private Preferences.CodeGrouping _codeGroupSize; private boolean _showAccountName; @@ -113,17 +112,6 @@ public class EntryAdapter extends RecyclerView.Adapter _pauseFocused = pauseFocused; } - public void toggleFavoriteState(VaultEntry entry) { - if (_favorites.contains(entry.getUUID())) { - _favorites.remove(entry.getUUID()); - } else { - _favorites.add(entry.getUUID()); - } - - entry.setIsFavorited(_favorites.contains(entry.getUUID())); - updateShownEntries(); - } - public VaultEntry getEntryAt(int position) { return _shownEntries.get(position); } @@ -139,7 +127,7 @@ public class EntryAdapter extends RecyclerView.Adapter if (comparator != null) { // insert the entry in the correct order // note: this assumes that _shownEntries has already been sorted - for (int i = _favorites.size(); i < _shownEntries.size(); i++) { + for (int i = getShownFavoritesCount(); i < _shownEntries.size(); i++) { if (comparator.compare(_shownEntries.get(i), entry) > 0) { _shownEntries.add(i, entry); notifyItemInserted(i); @@ -169,7 +157,6 @@ public class EntryAdapter extends RecyclerView.Adapter public void addEntries(Collection entries) { for (VaultEntry entry: entries) { entry.setUsageCount(_usageCounts.containsKey(entry.getUUID()) ? _usageCounts.get(entry.getUUID()) : 0); - entry.setIsFavorited(_favorites.contains(entry.getUUID())); } _entries.addAll(entries); @@ -180,10 +167,6 @@ public class EntryAdapter extends RecyclerView.Adapter public void removeEntry(VaultEntry entry) { _entries.remove(entry); - if (_favorites.contains(entry.getUUID())) { - removeFavorite(entry); - } - if (_shownEntries.contains(entry)) { int position = _shownEntries.indexOf(entry); _shownEntries.remove(position); @@ -270,7 +253,7 @@ public class EntryAdapter extends RecyclerView.Adapter public void refresh(boolean hard) { if (hard) { - notifyDataSetChanged(); + updateShownEntries(); } else { for (EntryHolder holder : _holders) { holder.refresh(); @@ -336,19 +319,8 @@ public class EntryAdapter extends RecyclerView.Adapter public Map getUsageCounts() { return _usageCounts; } - public void setFavorites(List favorites) { _favorites = favorites; } - - public List getFavorites() { return _favorites; } - - public void removeFavorite(VaultEntry entry) { - int position = -1; - for (int i = 0; i < _favorites.size(); i++) { - if (_favorites.get(i).equals(entry.getUUID())) { - position = i; - } - } - - _favorites.remove(position); + public int getShownFavoritesCount() { + return (int) _shownEntries.stream().filter(VaultEntry::isFavorite).count(); } public void setGroups(TreeSet groups) { @@ -485,7 +457,7 @@ public class EntryAdapter extends RecyclerView.Adapter boolean dragEnabled = _selectedEntries.size() == 0 || _selectedEntries.size() == 1 && _selectedEntries.get(0) == entryHolder.getEntry(); - if (dragEnabled && isDragAndDropAllowed() && !entryHolder.getEntry().getIsFavorited()) { + if (dragEnabled && isDragAndDropAllowed() && !entryHolder.getEntry().isFavorite()) { _view.startDrag(_dragHandleHolder); } @@ -500,7 +472,7 @@ public class EntryAdapter extends RecyclerView.Adapter && _selectedEntries.size() == 1 && _selectedEntries.get(0) == entryHolder.getEntry() && isDragAndDropAllowed() - && !entryHolder.getEntry().getIsFavorited()) { + && !entryHolder.getEntry().isFavorite()) { _view.startDrag(_dragHandleHolder); return true; } @@ -644,7 +616,7 @@ public class EntryAdapter extends RecyclerView.Adapter return; } - if (_selectedEntries.size() == 1 && _dragHandleHolder == null && !_selectedEntries.get(0).getIsFavorited()) { + if (_selectedEntries.size() == 1 && _dragHandleHolder == null && !_selectedEntries.get(0).isFavorite()) { // Find and enable dragging for the single selected EntryHolder // Not nice but this is the best method I could find for (int i = 0; i < _holders.size(); i++) { diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java index f31f5e0e..50300aea 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java @@ -116,7 +116,7 @@ public class EntryHolder extends RecyclerView.ViewHolder { _selectedHandler.removeCallbacksAndMessages(null); _animationHandler.removeCallbacksAndMessages(null); - _favoriteIndicator.setVisibility(_entry.getIsFavorited() ? View.VISIBLE : View.INVISIBLE); + _favoriteIndicator.setVisibility(_entry.isFavorite() ? View.VISIBLE : View.INVISIBLE); // only show the progress bar if there is no uniform period and the entry type is TotpInfo setShowProgress(showProgress); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java index 93ef8096..c982daaa 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java @@ -1,5 +1,7 @@ package com.beemdevelopment.aegis.ui.views; +import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; + import android.annotation.SuppressLint; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -197,12 +199,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { return _adapter.getUsageCounts(); } - public void setFavorites(List favorites) { - _adapter.setFavorites(favorites); - } - - public List getFavorites() { return _adapter.getFavorites(); } - public void setSearchFilter(String search) { _adapter.setSearchFilter(search); _touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed()); @@ -356,10 +352,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { } } - public void toggleFavoriteState(VaultEntry entry) { - _adapter.toggleFavoriteState(entry); - } - public void tempHighlightEntry(VaultEntry entry) { _adapter.setTempHighlightEntry(true); @@ -540,7 +532,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { } private class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration { - private int _height; + private final int _height; private VerticalSpaceItemDecoration(float dp) { // convert dp to pixels @@ -549,27 +541,49 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { - if (parent.getChildAdapterPosition(view) == 0 && (_groups == null || _groups.isEmpty())) { - // the first item should also have a top margin + int adapterPosition = parent.getChildAdapterPosition(view); + if (adapterPosition == NO_POSITION) { + return; + } + + // The footer always has a top and bottom margin + final int defaultMargin = MetricsHelper.convertDpToPixels(requireContext(), 20); + if (_adapter.isPositionFooter(adapterPosition)) { + outRect.top = defaultMargin; + outRect.bottom = defaultMargin; + return; + } + + // The first entry should have a top margin, but only if the group chip is not shown + if (adapterPosition == 0 && (_groups == null || _groups.isEmpty())) { outRect.top = _height; } - int adapterPosition = parent.getChildAdapterPosition(view); - if (adapterPosition == -1) { - return; + // Only non-favorite entries have a bottom margin, except for the final favorite and non-favorite entry + int totalFavorites = _adapter.getShownFavoritesCount(); + if (totalFavorites == 0 + || (adapterPosition < _adapter.getEntriesCount() && !_adapter.getEntryAt(adapterPosition).isFavorite()) + || totalFavorites == adapterPosition + 1) { + outRect.bottom = _height; } - if (adapterPosition < _adapter.getEntriesCount() && _adapter.getEntryAt(adapterPosition).getIsFavorited()) { - if (_adapter.getFavorites().size() == parent.getChildAdapterPosition(view) + 1) { - outRect.bottom = MetricsHelper.convertDpToPixels(requireContext(), 20); - return; + if (totalFavorites > 0) { + // If this entry is the last favorite entry in the list, it should always have + // a bottom margin, regardless of the view mode + if (adapterPosition == totalFavorites - 1) { + outRect.bottom = defaultMargin; } - outRect.bottom = 0; - return; + // If this is the first non-favorite entry, it should have a top margin + if (adapterPosition == totalFavorites) { + outRect.top = _height; + } } - outRect.top = _height; + // The last entry should never have a bottom margin + if (_adapter.getEntriesCount() == adapterPosition + 1) { + outRect.bottom = 0; + } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java b/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java index 4aca13a3..9ad2d70b 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java +++ b/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java @@ -17,8 +17,6 @@ import java.util.Arrays; import java.util.Objects; import java.util.UUID; -import javax.annotation.Nonnull; - public class VaultEntry extends UUIDMap.Value { private String _name = ""; private String _issuer = ""; @@ -26,7 +24,7 @@ public class VaultEntry extends UUIDMap.Value { private OtpInfo _info; private byte[] _icon; private IconType _iconType = IconType.INVALID; - private boolean _isFavorited; + private boolean _isFavorite; private int _usageCount; private String _note = ""; @@ -67,6 +65,7 @@ public class VaultEntry extends UUIDMap.Value { obj.put("issuer", _issuer); obj.put("group", _group); obj.put("note", _note); + obj.put("favorite", _isFavorite); obj.put("icon", _icon == null ? JSONObject.NULL : Base64.encode(_icon)); obj.put("icon_mime", _icon == null ? null : _iconType.toMimeType()); obj.put("info", _info.toJson()); @@ -93,6 +92,7 @@ public class VaultEntry extends UUIDMap.Value { entry.setIssuer(obj.getString("issuer")); entry.setGroup(obj.optString("group", null)); entry.setNote(obj.optString("note", "")); + entry.setIsFavorite(obj.optBoolean("favorite", false)); Object icon = obj.get("icon"); if (icon != JSONObject.NULL) { @@ -143,7 +143,7 @@ public class VaultEntry extends UUIDMap.Value { public String getNote() { return _note; } - public boolean getIsFavorited() { return _isFavorited; }; + public boolean isFavorite() { return _isFavorite; }; public void setName(String name) { _name = name; @@ -174,7 +174,7 @@ public class VaultEntry extends UUIDMap.Value { public void setNote(String note) { _note = note; } - public void setIsFavorited(boolean isFavorited) { _isFavorited = isFavorited; } + public void setIsFavorite(boolean isFavorite) { _isFavorite = isFavorite; } @Override public boolean equals(Object o) { @@ -198,7 +198,8 @@ public class VaultEntry extends UUIDMap.Value { && getInfo().equals(entry.getInfo()) && Arrays.equals(getIcon(), entry.getIcon()) && getIconType().equals(entry.getIconType()) - && getNote().equals(entry.getNote()); + && getNote().equals(entry.getNote()) + && isFavorite() == entry.isFavorite(); } /**