mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-25 00:06:08 +00:00
Merge pull request #1006 from beemdevelopment/feature/favorites
Add ability to pin/favorite entries
This commit is contained in:
commit
031a11250a
17 changed files with 247 additions and 20 deletions
|
@ -1,6 +1,7 @@
|
|||
package com.beemdevelopment.aegis;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
|
||||
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
|
||||
import static androidx.test.espresso.action.ViewActions.clearText;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
|
@ -9,20 +10,27 @@ import static androidx.test.espresso.action.ViewActions.longClick;
|
|||
import static androidx.test.espresso.action.ViewActions.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.typeText;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions;
|
||||
import androidx.test.espresso.matcher.RootMatchers;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.beemdevelopment.aegis.encoding.Base32;
|
||||
import com.beemdevelopment.aegis.encoding.Hex;
|
||||
|
@ -127,8 +135,9 @@ public class OverallTest extends AegisTest {
|
|||
onView(withId(R.id.action_share_qr)).perform(click());
|
||||
onView(withId(R.id.btnNext)).perform(click()).perform(click()).perform(click());
|
||||
|
||||
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(2, longClick()));
|
||||
onView(withId(R.id.action_delete)).perform(click());
|
||||
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(0, longClick()));
|
||||
onView(allOf(isDescendantOfA(withClassName(containsString("ActionBarContextView"))), withClassName(containsString("OverflowMenuButton")))).perform(click());
|
||||
onView(withText(R.string.action_delete)).perform(click());
|
||||
onView(withId(android.R.id.button1)).perform(click());
|
||||
|
||||
openContextualActionModeOverflowMenu();
|
||||
|
|
|
@ -20,9 +20,11 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Preferences {
|
||||
|
@ -233,6 +235,27 @@ public class Preferences {
|
|||
_prefs.edit().putString("pref_usage_count", usageCountJson.toString()).apply();
|
||||
}
|
||||
|
||||
public List<UUID> getFavorites() {
|
||||
List<UUID> favorites = new ArrayList<>();
|
||||
Set<String> favoritesStringSet = _prefs.getStringSet("pref_favorites", null);
|
||||
if(favoritesStringSet != null) {
|
||||
for (String favorite : favoritesStringSet) {
|
||||
favorites.add(UUID.fromString(favorite));
|
||||
}
|
||||
}
|
||||
|
||||
return favorites;
|
||||
}
|
||||
|
||||
public void setFavorites(List<UUID> favorites) {
|
||||
Set<String> favoritesHashSet = new HashSet<String>();
|
||||
for (UUID favorite : favorites) {
|
||||
favoritesHashSet.add(favorite.toString());
|
||||
}
|
||||
|
||||
_prefs.edit().putStringSet("pref_favorites", favoritesHashSet).apply();
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return _prefs.getInt("pref_timeout", -1);
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
|
||||
private VaultEntry _selectedEntry;
|
||||
|
||||
private final ItemTouchHelperAdapter _adapter;
|
||||
private final EntryAdapter _adapter;
|
||||
private boolean _positionChanged = false;
|
||||
private boolean _isLongPressDragEnabled = true;
|
||||
|
||||
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||
public SimpleItemTouchHelperCallback(EntryAdapter adapter) {
|
||||
_adapter = adapter;
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,15 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
}
|
||||
|
||||
public void setSelectedEntry(VaultEntry entry) {
|
||||
if (entry == null) {
|
||||
_selectedEntry = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.getIsFavorited()) {
|
||||
_selectedEntry = entry;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
|
@ -41,6 +48,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
|
||||
int swipeFlags = 0;
|
||||
|
||||
if (viewHolder != null) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
EntryAdapter adapter = (EntryAdapter)recyclerView.getAdapter();
|
||||
if (adapter.isPositionFooter(position)
|
||||
|
@ -49,6 +57,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
{
|
||||
dragFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
}
|
||||
|
@ -56,6 +65,9 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
|||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
||||
RecyclerView.ViewHolder target) {
|
||||
if(target.getAdapterPosition() < _adapter.getFavorites().size()){
|
||||
return false;
|
||||
}
|
||||
_adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
|
||||
_positionChanged = true;
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package com.beemdevelopment.aegis.helpers.comparators;
|
||||
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class FavoriteComparator implements Comparator<VaultEntry> {
|
||||
@Override
|
||||
public int compare(VaultEntry a, VaultEntry b) {
|
||||
return -1 * Boolean.compare(a.getIsFavorited(), b.getIsFavorited());
|
||||
}
|
||||
}
|
|
@ -110,7 +110,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_isDPadPressed = false;
|
||||
_isDoingIntro = false;
|
||||
_isAuthenticating = false;
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
_isRecreated = true;
|
||||
_pendingSearchQuery = savedInstanceState.getString("pendingSearchQuery");
|
||||
|
@ -177,10 +176,15 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
@Override
|
||||
protected void onPause() {
|
||||
Map<UUID, Integer> usageMap = _entryListView.getUsageCounts();
|
||||
List<UUID> favoritesList = _entryListView.getFavorites();
|
||||
if (usageMap != null) {
|
||||
_prefs.setUsageCount(usageMap);
|
||||
}
|
||||
|
||||
if (favoritesList != null) {
|
||||
_prefs.setFavorites(favoritesList);
|
||||
}
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
|
@ -645,6 +649,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
_menu = menu;
|
||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||
|
||||
updateLockIcon();
|
||||
if (_loaded) {
|
||||
_entryListView.setGroups(_vaultManager.getVault().getGroups());
|
||||
|
@ -774,6 +779,7 @@ 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;
|
||||
|
@ -859,6 +865,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
if (_selectedEntries.isEmpty()) {
|
||||
_actionMode.finish();
|
||||
} else {
|
||||
setFavoriteMenuItemVisiblity();
|
||||
setIsMultipleSelected(_selectedEntries.size() > 1);
|
||||
}
|
||||
|
||||
|
@ -882,6 +889,27 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_actionMode.getMenu().findItem(R.id.action_copy).setVisible(!multipleSelected);
|
||||
}
|
||||
|
||||
private void setFavoriteMenuItemVisiblity() {
|
||||
MenuItem toggleFavoriteMenuItem = _actionMode.getMenu().findItem(R.id.action_toggle_favorite);
|
||||
|
||||
if (_selectedEntries.size() == 1){
|
||||
if (_selectedEntries.get(0).getIsFavorited()) {
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_set_favorite);
|
||||
toggleFavoriteMenuItem.setTitle(R.string.unfavorite);
|
||||
} else {
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_unset_favorite);
|
||||
toggleFavoriteMenuItem.setTitle(R.string.favorite);
|
||||
}
|
||||
} else {
|
||||
toggleFavoriteMenuItem.setIcon(R.drawable.ic_unset_favorite);
|
||||
toggleFavoriteMenuItem.setTitle(String.format("%s / %s", getString(R.string.favorite), getString(R.string.unfavorite)));
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleFavorite(VaultEntry entry) {
|
||||
_entryListView.toggleFavoriteState(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongEntryClick(VaultEntry entry) {
|
||||
if (!_selectedEntries.isEmpty()) {
|
||||
|
@ -896,6 +924,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
private void startActionMode() {
|
||||
_actionMode = startSupportActionMode(_actionModeCallbacks);
|
||||
_actionModeBackPressHandler.setEnabled(true);
|
||||
setFavoriteMenuItemVisiblity();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1021,7 +1050,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
private class ActionModeCallbacks implements ActionMode.Callback {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
MenuInflater inflater = mode.getMenuInflater();
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_action_mode, menu);
|
||||
return true;
|
||||
}
|
||||
|
@ -1048,6 +1077,14 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_toggle_favorite:
|
||||
for (VaultEntry entry : _selectedEntries) {
|
||||
toggleFavorite(entry);
|
||||
}
|
||||
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_share_qr:
|
||||
Intent intent = new Intent(getBaseContext(), TransferEntriesActivity.class);
|
||||
ArrayList<GoogleAuthInfo> authInfos = new ArrayList<>();
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.beemdevelopment.aegis.Preferences;
|
|||
import com.beemdevelopment.aegis.SortCategory;
|
||||
import com.beemdevelopment.aegis.ViewMode;
|
||||
import com.beemdevelopment.aegis.helpers.ItemTouchHelperAdapter;
|
||||
import com.beemdevelopment.aegis.helpers.comparators.FavoriteComparator;
|
||||
import com.beemdevelopment.aegis.otp.HotpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||
|
@ -41,6 +42,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
private List<VaultEntry> _shownEntries;
|
||||
private List<VaultEntry> _selectedEntries;
|
||||
private Map<UUID, Integer> _usageCounts;
|
||||
private List<UUID> _favorites;
|
||||
private VaultEntry _focusedEntry;
|
||||
private Preferences.CodeGrouping _codeGroupSize;
|
||||
private boolean _showAccountName;
|
||||
|
@ -111,6 +113,17 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
_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);
|
||||
}
|
||||
|
@ -126,7 +139,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
if (comparator != null) {
|
||||
// insert the entry in the correct order
|
||||
// note: this assumes that _shownEntries has already been sorted
|
||||
for (int i = 0; i < _shownEntries.size(); i++) {
|
||||
for (int i = _favorites.size(); i < _shownEntries.size(); i++) {
|
||||
if (comparator.compare(_shownEntries.get(i), entry) > 0) {
|
||||
_shownEntries.add(i, entry);
|
||||
notifyItemInserted(i);
|
||||
|
@ -156,6 +169,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
public void addEntries(Collection<VaultEntry> 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);
|
||||
|
@ -166,6 +180,10 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
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);
|
||||
|
@ -303,6 +321,9 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
Collections.sort(_shownEntries, comparator);
|
||||
}
|
||||
|
||||
Comparator<VaultEntry> favoriteComparator = new FavoriteComparator();
|
||||
Collections.sort(_shownEntries, favoriteComparator);
|
||||
|
||||
_view.onListChange();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
@ -315,6 +336,21 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
public Map<UUID, Integer> getUsageCounts() { return _usageCounts; }
|
||||
|
||||
public void setFavorites(List<UUID> favorites) { _favorites = favorites; }
|
||||
|
||||
public List<UUID> 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 void setGroups(TreeSet<String> groups) {
|
||||
_view.setGroups(groups);
|
||||
}
|
||||
|
@ -449,7 +485,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
boolean dragEnabled = _selectedEntries.size() == 0
|
||||
|| _selectedEntries.size() == 1 && _selectedEntries.get(0) == entryHolder.getEntry();
|
||||
if (dragEnabled && isDragAndDropAllowed()) {
|
||||
if (dragEnabled && isDragAndDropAllowed() && !entryHolder.getEntry().getIsFavorited()) {
|
||||
_view.startDrag(_dragHandleHolder);
|
||||
}
|
||||
|
||||
|
@ -463,7 +499,8 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
if (event.getActionMasked() == MotionEvent.ACTION_MOVE
|
||||
&& _selectedEntries.size() == 1
|
||||
&& _selectedEntries.get(0) == entryHolder.getEntry()
|
||||
&& isDragAndDropAllowed()) {
|
||||
&& isDragAndDropAllowed()
|
||||
&& !entryHolder.getEntry().getIsFavorited()) {
|
||||
_view.startDrag(_dragHandleHolder);
|
||||
return true;
|
||||
}
|
||||
|
@ -607,7 +644,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
return;
|
||||
}
|
||||
|
||||
if (_selectedEntries.size() == 1 && _dragHandleHolder == null) {
|
||||
if (_selectedEntries.size() == 1 && _dragHandleHolder == null && !_selectedEntries.get(0).getIsFavorited()) {
|
||||
// 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++) {
|
||||
|
|
|
@ -35,6 +35,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
private static final float DIMMED_ALPHA = 0.2f;
|
||||
private static final char HIDDEN_CHAR = '●';
|
||||
|
||||
private View _favoriteIndicator;
|
||||
private TextView _profileName;
|
||||
private TextView _profileCode;
|
||||
private TextView _profileIssuer;
|
||||
|
@ -76,6 +77,7 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
_buttonRefresh = view.findViewById(R.id.buttonRefresh);
|
||||
_selected = view.findViewById(R.id.ivSelected);
|
||||
_dragHandle = view.findViewById(R.id.drag_handle);
|
||||
_favoriteIndicator = view.findViewById(R.id.favorite_indicator);
|
||||
|
||||
_selectedHandler = new Handler();
|
||||
_animationHandler = new Handler();
|
||||
|
@ -114,6 +116,8 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
_selectedHandler.removeCallbacksAndMessages(null);
|
||||
_animationHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
_favoriteIndicator.setVisibility(_entry.getIsFavorited() ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
// only show the progress bar if there is no uniform period and the entry type is TotpInfo
|
||||
setShowProgress(showProgress);
|
||||
|
||||
|
|
|
@ -197,6 +197,12 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
return _adapter.getUsageCounts();
|
||||
}
|
||||
|
||||
public void setFavorites(List<UUID> favorites) {
|
||||
_adapter.setFavorites(favorites);
|
||||
}
|
||||
|
||||
public List<UUID> getFavorites() { return _adapter.getFavorites(); }
|
||||
|
||||
public void setSearchFilter(String search) {
|
||||
_adapter.setSearchFilter(search);
|
||||
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());
|
||||
|
@ -350,6 +356,10 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
}
|
||||
}
|
||||
|
||||
public void toggleFavoriteState(VaultEntry entry) {
|
||||
_adapter.toggleFavoriteState(entry);
|
||||
}
|
||||
|
||||
public void tempHighlightEntry(VaultEntry entry) {
|
||||
_adapter.setTempHighlightEntry(true);
|
||||
|
||||
|
@ -543,7 +553,23 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
// the first item should also have a top margin
|
||||
outRect.top = _height;
|
||||
}
|
||||
outRect.bottom = _height;
|
||||
|
||||
int adapterPosition = parent.getChildAdapterPosition(view);
|
||||
if (adapterPosition == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (adapterPosition < _adapter.getEntriesCount() && _adapter.getEntryAt(adapterPosition).getIsFavorited()) {
|
||||
if (_adapter.getFavorites().size() == parent.getChildAdapterPosition(view) + 1) {
|
||||
outRect.bottom = MetricsHelper.convertDpToPixels(requireContext(), 20);
|
||||
return;
|
||||
}
|
||||
|
||||
outRect.bottom = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
outRect.top = _height;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public class VaultEntry extends UUIDMap.Value {
|
|||
private OtpInfo _info;
|
||||
private byte[] _icon;
|
||||
private IconType _iconType = IconType.INVALID;
|
||||
private boolean _isFavorited;
|
||||
private int _usageCount;
|
||||
private String _note = "";
|
||||
|
||||
|
@ -142,6 +143,8 @@ public class VaultEntry extends UUIDMap.Value {
|
|||
|
||||
public String getNote() { return _note; }
|
||||
|
||||
public boolean getIsFavorited() { return _isFavorited; };
|
||||
|
||||
public void setName(String name) {
|
||||
_name = name;
|
||||
}
|
||||
|
@ -171,6 +174,8 @@ public class VaultEntry extends UUIDMap.Value {
|
|||
|
||||
public void setNote(String note) { _note = note; }
|
||||
|
||||
public void setIsFavorited(boolean isFavorited) { _isFavorited = isFavorited; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof VaultEntry)) {
|
||||
|
|
15
app/src/main/res/drawable-anydpi/ic_set_favorite.xml
Normal file
15
app/src/main/res/drawable-anydpi/ic_set_favorite.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="1.1111112"
|
||||
android:scaleY="1.1111112"
|
||||
android:translateX="-1.3333334"
|
||||
android:translateY="-1.3333334">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||
</group>
|
||||
</vector>
|
15
app/src/main/res/drawable-anydpi/ic_unset_favorite.xml
Normal file
15
app/src/main/res/drawable-anydpi/ic_unset_favorite.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="1.1111112"
|
||||
android:scaleY="1.1111112"
|
||||
android:translateX="-1.3333334"
|
||||
android:translateY="-1.3333334">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</group>
|
||||
</vector>
|
|
@ -18,6 +18,14 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/favorite_indicator"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="-11dp"
|
||||
android:backgroundTint="@color/colorFavorite"
|
||||
android:background="@drawable/button_rounded_corners" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -18,6 +18,14 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/favorite_indicator"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="-11dp"
|
||||
android:backgroundTint="@color/colorFavorite"
|
||||
android:background="@drawable/button_rounded_corners" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -27,9 +35,8 @@
|
|||
android:id="@+id/ivTextDrawable"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="45dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
/>
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/ivSelected"
|
||||
|
|
|
@ -18,6 +18,14 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/favorite_indicator"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="-11dp"
|
||||
android:backgroundTint="@color/colorFavorite"
|
||||
android:background="@drawable/button_rounded_corners" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_toggle_favorite"
|
||||
android:orderInCategory="70"
|
||||
android:icon="@drawable/ic_unset_favorite"
|
||||
android:title="@string/favorite"
|
||||
app:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/action_copy"
|
||||
android:orderInCategory="80"
|
||||
|
@ -21,7 +27,7 @@
|
|||
android:orderInCategory="100"
|
||||
android:icon="@drawable/ic_delete_white"
|
||||
android:tint="?attr/iconColorPrimary"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_share_qr"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<color name="colorPrimaryLight">#5472d3</color>
|
||||
<color name="colorAccent">#FF5252</color>
|
||||
<color name="colorAccentPressed">#FF5252</color>
|
||||
<color name="colorFavorite">#F9A825</color>
|
||||
<color name="primary_text">#212121</color>
|
||||
<color name="secondary_text">#A0A0A0</color>
|
||||
<color name="auth_text">#4c4c4c</color>
|
||||
|
|
|
@ -152,6 +152,8 @@
|
|||
<string name="set_up_biometric">Set up biometric unlock</string>
|
||||
<string name="copy">Copy</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="favorite">Favorite</string>
|
||||
<string name="unfavorite">Unfavorite</string>
|
||||
<string name="error_all_caps">ERROR</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="confirm_password">Confirm password</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue