mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-23 23:39:14 +00:00
Merge pull request #279 from michaelschattgen/feature/multi-select
Add ability to select multiple entries
This commit is contained in:
commit
97d824d779
5 changed files with 107 additions and 23 deletions
|
@ -57,6 +57,24 @@ public class Dialogs {
|
|||
.create());
|
||||
}
|
||||
|
||||
public static void showDeleteEntriesDialog(Activity activity, DialogInterface.OnClickListener onDelete, int totalEntries) {
|
||||
String title, message;
|
||||
if (totalEntries > 1) {
|
||||
title = activity.getString(R.string.delete_entries);
|
||||
message = String.format(activity.getString(R.string.delete_entries_description), totalEntries);
|
||||
} else {
|
||||
title = activity.getString(R.string.delete_entry);
|
||||
message = activity.getString(R.string.delete_entry_description);
|
||||
}
|
||||
|
||||
showSecureDialog(new AlertDialog.Builder(activity)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.yes, onDelete)
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.create());
|
||||
}
|
||||
|
||||
public static void showDiscardDialog(Activity activity, DialogInterface.OnClickListener onSave, DialogInterface.OnClickListener onDiscard) {
|
||||
showSecureDialog(new AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.discard_changes))
|
||||
|
|
|
@ -73,7 +73,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
private String _selectedGroup;
|
||||
private boolean _searchSubmitted;
|
||||
|
||||
private VaultEntry _selectedEntry;
|
||||
private List<VaultEntry> _selectedEntries;
|
||||
private ActionMode _actionMode;
|
||||
|
||||
private Menu _menu;
|
||||
|
@ -123,6 +123,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
});
|
||||
|
||||
_fabScrollHelper = new FabScrollHelper(_fabMenu);
|
||||
_selectedEntries = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -502,6 +503,15 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void deleteEntries(List<VaultEntry> entries) {
|
||||
for (VaultEntry entry: entries) {
|
||||
VaultEntry oldEntry = _vault.removeEntry(entry);
|
||||
_entryListView.removeEntry(oldEntry);
|
||||
}
|
||||
|
||||
saveVault();
|
||||
}
|
||||
|
||||
private void deleteEntry(VaultEntry entry) {
|
||||
VaultEntry oldEntry = _vault.removeEntry(entry);
|
||||
saveVault();
|
||||
|
@ -671,9 +681,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
|
||||
@Override
|
||||
public void onEntryClick(VaultEntry entry) {
|
||||
if (_selectedEntry != null) {
|
||||
if (_selectedEntry == entry) {
|
||||
if (_actionMode != null) {
|
||||
if (_selectedEntries.isEmpty()) {
|
||||
_actionMode.finish();
|
||||
} else {
|
||||
setIsMultipleSelected(_selectedEntries.size() > 1);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -682,13 +694,29 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
copyEntryCode(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelect(VaultEntry entry) {
|
||||
_selectedEntries.add(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeselect(VaultEntry entry) {
|
||||
_selectedEntries.remove(entry);
|
||||
}
|
||||
|
||||
private void setIsMultipleSelected(boolean multipleSelected) {
|
||||
_entryListView.setIsLongPressDragEnabled(!multipleSelected);
|
||||
_actionMode.getMenu().findItem(R.id.action_edit).setVisible(!multipleSelected);
|
||||
_actionMode.getMenu().findItem(R.id.action_copy).setVisible(!multipleSelected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongEntryClick(VaultEntry entry) {
|
||||
if (_selectedEntry != null) {
|
||||
if (!_selectedEntries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedEntry = entry;
|
||||
_selectedEntries.add(entry);
|
||||
_entryListView.setActionModeState(true, entry);
|
||||
_actionMode = this.startSupportActionMode(_actionModeCallbacks);
|
||||
}
|
||||
|
@ -753,26 +781,29 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_copy:
|
||||
copyEntryCode(_selectedEntry);
|
||||
copyEntryCode(_selectedEntries.get(0));
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_edit:
|
||||
startEditProfileActivity(CODE_EDIT_ENTRY, _selectedEntry, false);
|
||||
startEditProfileActivity(CODE_EDIT_ENTRY, _selectedEntries.get(0), false);
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_delete:
|
||||
Dialogs.showDeleteEntryDialog(MainActivity.this, (d, which) -> {
|
||||
deleteEntry(_selectedEntry);
|
||||
Dialogs.showDeleteEntriesDialog(MainActivity.this, (d, which) -> {
|
||||
deleteEntries(_selectedEntries);
|
||||
|
||||
if (_selectedEntry.getGroup() != null) {
|
||||
if (!_vault.getGroups().contains(_selectedEntry.getGroup())) {
|
||||
updateGroupFilterMenu();
|
||||
for (VaultEntry entry : _selectedEntries) {
|
||||
if (entry.getGroup() != null) {
|
||||
if (!_vault.getGroups().contains(entry.getGroup())) {
|
||||
updateGroupFilterMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mode.finish();
|
||||
});
|
||||
}, _selectedEntries.size());
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -782,7 +813,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
_entryListView.setActionModeState(false, null);
|
||||
_selectedEntry = null;
|
||||
_selectedEntries.clear();
|
||||
_actionMode = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
private EntryListView _view;
|
||||
private List<VaultEntry> _entries;
|
||||
private List<VaultEntry> _shownEntries;
|
||||
private VaultEntry _selectedEntry;
|
||||
private List<VaultEntry> _selectedEntries;
|
||||
private VaultEntry _focusedEntry;
|
||||
private boolean _showAccountName;
|
||||
private boolean _searchAccountName;
|
||||
|
@ -46,6 +46,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
public EntryAdapter(EntryListView view) {
|
||||
_entries = new ArrayList<>();
|
||||
_shownEntries = new ArrayList<>();
|
||||
_selectedEntries = new ArrayList<>();
|
||||
_holders = new ArrayList<>();
|
||||
_dimHandler = new Handler();
|
||||
_view = view;
|
||||
|
@ -302,7 +303,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
@Override
|
||||
public void onBindViewHolder(final EntryHolder holder, int position) {
|
||||
VaultEntry entry = _shownEntries.get(position);
|
||||
holder.setFocused(entry == _selectedEntry);
|
||||
holder.setFocused(_selectedEntries.contains(entry));
|
||||
|
||||
boolean hidden = _tapToReveal && entry != _focusedEntry;
|
||||
boolean dimmed = _highlightEntry && _focusedEntry != null && _focusedEntry != entry;
|
||||
|
@ -315,7 +316,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
public void onClick(View v) {
|
||||
boolean handled = false;
|
||||
|
||||
if (_selectedEntry == null) {
|
||||
if (_selectedEntries.isEmpty()) {
|
||||
if (_highlightEntry || _tapToReveal) {
|
||||
if (_focusedEntry == entry) {
|
||||
resetFocus();
|
||||
|
@ -324,6 +325,16 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
focusEntry(entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_selectedEntries.contains(entry)) {
|
||||
_view.onDeselect(entry);
|
||||
removeSelectedEntry(entry);
|
||||
holder.setFocused(false);
|
||||
} else {
|
||||
holder.setFocused(true);
|
||||
addSelectedEntry(entry);
|
||||
_view.onSelect(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
|
@ -335,8 +346,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
int position = holder.getAdapterPosition();
|
||||
if (_selectedEntry == null) {
|
||||
setSelectedEntry(_shownEntries.get(position));
|
||||
if (_selectedEntries.isEmpty()) {
|
||||
holder.setFocused(true);
|
||||
}
|
||||
|
||||
|
@ -444,14 +454,23 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
_focusedEntry = null;
|
||||
}
|
||||
|
||||
public void setSelectedEntry(VaultEntry entry) {
|
||||
public void removeSelectedEntry(VaultEntry entry) {
|
||||
_selectedEntries.remove(entry);
|
||||
}
|
||||
|
||||
public void addSelectedEntry(VaultEntry entry) {
|
||||
if (entry == null) {
|
||||
notifyItemChanged(_shownEntries.indexOf(_selectedEntry));
|
||||
for (VaultEntry vaultEntry: _selectedEntries) {
|
||||
notifyItemChanged(_shownEntries.indexOf(vaultEntry));
|
||||
}
|
||||
|
||||
_selectedEntries.clear();
|
||||
return;
|
||||
} else if (_highlightEntry) {
|
||||
resetFocus();
|
||||
}
|
||||
|
||||
_selectedEntry = entry;
|
||||
_selectedEntries.add(entry);
|
||||
}
|
||||
|
||||
public boolean isDragAndDropAllowed() {
|
||||
|
@ -474,5 +493,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
|
|||
void onEntryDrop(VaultEntry entry);
|
||||
void onEntryChange(VaultEntry entry);
|
||||
void onPeriodUniformityChanged(boolean uniform);
|
||||
void onSelect(VaultEntry entry);
|
||||
void onDeselect(VaultEntry entry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,10 +132,14 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
}
|
||||
}
|
||||
|
||||
public void setIsLongPressDragEnabled(boolean enabled) {
|
||||
_touchCallback.setIsLongPressDragEnabled(enabled);
|
||||
}
|
||||
|
||||
public void setActionModeState(boolean enabled, VaultEntry entry) {
|
||||
_touchCallback.setSelectedEntry(entry);
|
||||
_touchCallback.setIsLongPressDragEnabled(enabled && _adapter.isDragAndDropAllowed());
|
||||
_adapter.setSelectedEntry(entry);
|
||||
_adapter.addSelectedEntry(entry);
|
||||
}
|
||||
|
||||
public void setSortCategory(SortCategory sortCategory, boolean apply) {
|
||||
|
@ -194,6 +198,12 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
_listener.onEntryChange(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelect(VaultEntry entry) { _listener.onSelect(entry); }
|
||||
|
||||
@Override
|
||||
public void onDeselect(VaultEntry entry) { _listener.onDeselect(entry); }
|
||||
|
||||
@Override
|
||||
public void onPeriodUniformityChanged(boolean isUniform) {
|
||||
setShowProgress(isUniform);
|
||||
|
@ -298,6 +308,8 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
|||
void onEntryChange(VaultEntry entry);
|
||||
void onLongEntryClick(VaultEntry entry);
|
||||
void onScroll(int dx, int dy);
|
||||
void onSelect(VaultEntry entry);
|
||||
void onDeselect(VaultEntry entry);
|
||||
}
|
||||
|
||||
private class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
|
|
@ -92,6 +92,8 @@
|
|||
<string name="encrypting_vault">Encrypting the vault</string>
|
||||
<string name="delete_entry">Delete entry</string>
|
||||
<string name="delete_entry_description">Are you sure you want to delete this entry?</string>
|
||||
<string name="delete_entries">Delete entries</string>
|
||||
<string name="delete_entries_description">Are you sure you want to delete %d entries?</string>
|
||||
<string name="discard_changes">Discard changes?</string>
|
||||
<string name="discard_changes_description">Your changes have not been saved</string>
|
||||
<string name="folder">Folder</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue