Merge pull request #279 from michaelschattgen/feature/multi-select

Add ability to select multiple entries
This commit is contained in:
Alexander Bakker 2020-01-05 11:42:36 +01:00 committed by GitHub
commit 97d824d779
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 23 deletions

View file

@ -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))

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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>