Skip to content

Commit 0d5ba3c

Browse files
committed
Implement archive
1 parent c8d5be6 commit 0d5ba3c

File tree

10 files changed

+164
-20
lines changed

10 files changed

+164
-20
lines changed

app/src/androidTest/java/com/beemdevelopment/aegis/OverallTest.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,20 @@ public void testOverall() {
142142
onView(withId(R.id.action_share_qr)).perform(click());
143143
onView(withId(R.id.btnNext)).perform(click()).perform(click()).perform(click());
144144

145-
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 0, longClick()));
145+
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset, longClick()));
146+
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset + 1, click()));
146147
onView(allOf(isDescendantOfA(withClassName(containsString("ActionBarContextView"))), withClassName(containsString("OverflowMenuButton")))).perform(click());
147148
onView(withText(R.string.action_delete)).perform(click());
148149
onView(withId(android.R.id.button1)).perform(click());
150+
onView(withText(R.string.archive)).perform(click());
151+
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset, longClick()));
152+
onView(allOf(isDescendantOfA(withClassName(containsString("ActionBarContextView"))), withClassName(containsString("OverflowMenuButton")))).perform(click());
153+
onView(withText(R.string.action_delete)).perform(click());
154+
onView(withId(android.R.id.button1)).perform(click());
155+
onView(withId(R.id.rvKeyProfiles)).perform(RecyclerViewActions.actionOnItemAtPosition(entryPosOffset, longClick()));
156+
onView(allOf(isDescendantOfA(withClassName(containsString("ActionBarContextView"))), withClassName(containsString("OverflowMenuButton")))).perform(click());
157+
onView(withText(R.string.action_restore)).perform(click());
158+
onView(withText(R.string.archive)).perform(click());
149159

150160
openContextualActionModeOverflowMenu();
151161
onView(withText(R.string.lock)).perform(click());

app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java

+68-13
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
9696
private boolean _isDPadPressed;
9797
private boolean _isDoingIntro;
9898
private boolean _isAuthenticating;
99+
private boolean _isArchiveEnabled;
99100

100101
private String _submittedSearchQuery;
101102
private String _pendingSearchQuery;
@@ -187,6 +188,7 @@ protected void onCreate(Bundle savedInstanceState) {
187188
_isDPadPressed = false;
188189
_isDoingIntro = false;
189190
_isAuthenticating = false;
191+
_isArchiveEnabled = false;
190192
if (savedInstanceState != null) {
191193
_isRecreated = true;
192194
_pendingSearchQuery = savedInstanceState.getString("pendingSearchQuery");
@@ -249,7 +251,6 @@ protected void onCreate(Bundle savedInstanceState) {
249251

250252
public void setGroups(Collection<VaultGroup> groups) {
251253
_groups = groups;
252-
_groupChip.setVisibility(_groups.isEmpty() ? View.GONE : View.VISIBLE);
253254

254255
if (_prefGroupFilter != null) {
255256
Set<UUID> groupFilter = cleanGroupFilter(_prefGroupFilter);
@@ -273,13 +274,17 @@ public void setGroups(Collection<VaultGroup> groups) {
273274
private void initializeGroups() {
274275
_groupChip.removeAllViews();
275276

277+
addArchiveChip(_groupChip);
278+
276279
for (VaultGroup group : _groups) {
277280
addChipTo(_groupChip, new VaultGroupModel(group));
278281
}
279282

280-
GroupPlaceholderType placeholderType = GroupPlaceholderType.NO_GROUP;
281-
addChipTo(_groupChip, new VaultGroupModel(this, placeholderType));
282-
addSaveChip(_groupChip);
283+
if (!_groups.isEmpty()) {
284+
GroupPlaceholderType placeholderType = GroupPlaceholderType.NO_GROUP;
285+
addChipTo(_groupChip, new VaultGroupModel(this, placeholderType));
286+
addSaveChip(_groupChip);
287+
}
283288
}
284289

285290
private Set<UUID> cleanGroupFilter(Set<UUID> groupFilter) {
@@ -290,6 +295,21 @@ private Set<UUID> cleanGroupFilter(Set<UUID> groupFilter) {
290295
.collect(Collectors.toSet());
291296
}
292297

298+
private void addArchiveChip(ChipGroup chipGroup) {
299+
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_group_filter, null, false);
300+
chip.setText(getString(R.string.archive));
301+
chip.setCheckedIconVisible(false);
302+
chip.setOnCheckedChangeListener((button, isChecked) -> {
303+
_isArchiveEnabled = isChecked;
304+
if (_actionMode != null) {
305+
_actionMode.finish();
306+
}
307+
chip.setChecked(isChecked);
308+
_entryListView.enableArchive(isChecked);
309+
});
310+
chipGroup.addView(chip);
311+
}
312+
293313
private void addChipTo(ChipGroup chipGroup, VaultGroupModel group) {
294314
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.chip_group_filter, null, false);
295315
chip.setText(group.getName());
@@ -1153,6 +1173,7 @@ public void onEntryClick(VaultEntry entry) {
11531173
} else {
11541174
setFavoriteMenuItemVisiblity();
11551175
setIsMultipleSelected(_selectedEntries.size() > 1);
1176+
setRestoreMenuItemVisibility();
11561177
}
11571178
}
11581179
}
@@ -1195,6 +1216,11 @@ private void setFavoriteMenuItemVisiblity() {
11951216
}
11961217
}
11971218

1219+
private void setRestoreMenuItemVisibility() {
1220+
MenuItem restoreMenuItem = _actionMode.getMenu().findItem(R.id.action_restore);
1221+
restoreMenuItem.setVisible(_isArchiveEnabled);
1222+
}
1223+
11981224
@Override
11991225
public void onLongEntryClick(VaultEntry entry) {
12001226
if (!_selectedEntries.isEmpty()) {
@@ -1211,6 +1237,7 @@ private void startActionMode() {
12111237
_actionModeBackPressHandler.setEnabled(true);
12121238
setFavoriteMenuItemVisiblity();
12131239
setAssignIconsMenuItemVisibility();
1240+
setRestoreMenuItemVisibility();
12141241
}
12151242

12161243
@Override
@@ -1298,6 +1325,40 @@ private void copyEntryCode(VaultEntry entry) {
12981325
}
12991326
}
13001327

1328+
private void onActionRestore(ActionMode mode) {
1329+
for (VaultEntry entry : _selectedEntries) {
1330+
entry.setIsArchived(false);
1331+
}
1332+
saveAndBackupVault();
1333+
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
1334+
_entryListView.setEntries(_vaultManager.getVault().getEntries());
1335+
mode.finish();
1336+
}
1337+
1338+
private void onActionDelete(ActionMode mode) {
1339+
if (_isArchiveEnabled) {
1340+
Dialogs.showDeleteEntriesDialog(MainActivity.this, _selectedEntries, (d, which) -> {
1341+
for (VaultEntry entry : _selectedEntries) {
1342+
_vaultManager.getVault().removeEntry(entry);
1343+
}
1344+
saveAndBackupVault();
1345+
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
1346+
_entryListView.setEntries(_vaultManager.getVault().getEntries());
1347+
mode.finish();
1348+
});
1349+
} else {
1350+
Dialogs.showArchiveEntriesDialog(MainActivity.this, _selectedEntries.size(), (dialog, which) -> {
1351+
for (VaultEntry entry : _selectedEntries) {
1352+
entry.setIsArchived(true);
1353+
}
1354+
saveAndBackupVault();
1355+
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
1356+
_entryListView.setEntries(_vaultManager.getVault().getEntries());
1357+
mode.finish();
1358+
});
1359+
}
1360+
}
1361+
13011362
private class SearchViewBackPressHandler extends OnBackPressedCallback {
13021363
public SearchViewBackPressHandler() {
13031364
super(false);
@@ -1394,16 +1455,10 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
13941455
startActivity(intent);
13951456

13961457
mode.finish();
1458+
} else if (itemId == R.id.action_restore) {
1459+
onActionRestore(mode);
13971460
} else if (itemId == R.id.action_delete) {
1398-
Dialogs.showDeleteEntriesDialog(MainActivity.this, _selectedEntries, (d, which) -> {
1399-
for (VaultEntry entry : _selectedEntries) {
1400-
_vaultManager.getVault().removeEntry(entry);
1401-
}
1402-
saveAndBackupVault();
1403-
_entryListView.setGroups(_vaultManager.getVault().getUsedGroups());
1404-
_entryListView.setEntries(_vaultManager.getVault().getEntries());
1405-
mode.finish();
1406-
});
1461+
onActionDelete(mode);
14071462
} else if (itemId == R.id.action_select_all) {
14081463
_selectedEntries = _entryListView.selectAllEntries();
14091464
setFavoriteMenuItemVisiblity();

app/src/main/java/com/beemdevelopment/aegis/ui/dialogs/Dialogs.java

+13
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ public static void showDeleteEntriesDialog(Context context, List<VaultEntry> ser
9393
.create());
9494
}
9595

96+
public static void showArchiveEntriesDialog(Context context, int count, DialogInterface.OnClickListener onArchive) {
97+
String title = context.getResources().getQuantityString(R.plurals.archive_entry, count);
98+
String message = context.getResources().getQuantityString(R.plurals.archive_entry_description, count);
99+
Dialog dialog = new MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Aegis_AlertDialog_Warning)
100+
.setTitle(title)
101+
.setMessage(message)
102+
.setIconAttribute(android.R.attr.alertDialogIcon)
103+
.setPositiveButton(android.R.string.ok, onArchive)
104+
.setNegativeButton(android.R.string.no, null)
105+
.create();
106+
showSecureDialog(dialog);
107+
}
108+
96109
private static String getVaultEntryName(Context context, VaultEntry entry) {
97110
if (!entry.getIssuer().isEmpty() && !entry.getName().isEmpty()) {
98111
return String.format("%s (%s)", entry.getIssuer(), entry.getName());

app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
7979
private Handler _dimHandler;
8080
private Handler _doubleTapHandler;
8181
private boolean _pauseFocused;
82+
private boolean _isArchiveEnabled;
8283

8384
// keeps track of the EntryHolders that are currently bound
8485
private List<EntryHolder> _holders;
@@ -196,7 +197,13 @@ private boolean isEntryFiltered(VaultEntry entry) {
196197
String name = entry.getName().toLowerCase();
197198
String note = entry.getNote().toLowerCase();
198199

199-
if (!_groupFilter.isEmpty()) {
200+
if (!_isArchiveEnabled && entry.isArchived()) {
201+
return true;
202+
}
203+
204+
if (_isArchiveEnabled && !entry.isArchived()) {
205+
return true;
206+
} else if (!_groupFilter.isEmpty()) {
200207
if (groups.isEmpty() && !_groupFilter.contains(null)) {
201208
return true;
202209
}
@@ -774,6 +781,11 @@ public boolean isErrorCardShown() {
774781
return _entryList.isErrorCardShown();
775782
}
776783

784+
public void enableArchive(boolean enable) {
785+
_isArchiveEnabled = enable;
786+
refreshEntryList();
787+
}
788+
777789
private class FooterView extends RecyclerView.ViewHolder {
778790
View _footerView;
779791

app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import android.view.ViewGroup;
1515
import android.view.animation.LayoutAnimationController;
1616
import android.widget.LinearLayout;
17+
import android.widget.TextView;
1718

1819
import androidx.annotation.AttrRes;
1920
import androidx.annotation.NonNull;
@@ -72,6 +73,8 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
7273
private boolean _showExpirationState;
7374
private ViewMode _viewMode;
7475
private LinearLayout _emptyStateView;
76+
private boolean _isArchiveEnabled;
77+
private TextView _archiveEmptyText;
7578

7679
private UiRefresher _refresher;
7780

@@ -150,6 +153,7 @@ public long getMillisTillNextRefresh() {
150153
});
151154

152155
_emptyStateView = view.findViewById(R.id.vEmptyList);
156+
_archiveEmptyText = view.findViewById(R.id.archive_empty_text);
153157
return view;
154158
}
155159

@@ -471,6 +475,12 @@ public void runEntriesAnimation() {
471475
_recyclerView.scheduleLayoutAnimation();
472476
}
473477

478+
public void enableArchive(boolean enable) {
479+
_isArchiveEnabled = enable;
480+
_adapter.enableArchive(enable);
481+
updateEmptyState();
482+
}
483+
474484
private void setShowProgress(boolean showProgress) {
475485
_showProgress = showProgress;
476486
updateDividerDecoration();
@@ -495,9 +505,12 @@ private void updateEmptyState() {
495505
if (_adapter.getShownEntriesCount() > 0) {
496506
_recyclerView.setVisibility(View.VISIBLE);
497507
_emptyStateView.setVisibility(View.GONE);
498-
} else {
499-
if (Strings.isNullOrEmpty(_adapter.getSearchFilter())) {
500-
_recyclerView.setVisibility(View.GONE);
508+
_archiveEmptyText.setVisibility(View.GONE);
509+
} else if (Strings.isNullOrEmpty(_adapter.getSearchFilter())) {
510+
_recyclerView.setVisibility(View.GONE);
511+
if (_isArchiveEnabled) {
512+
_archiveEmptyText.setVisibility(View.VISIBLE);
513+
} else {
501514
_emptyStateView.setVisibility(View.VISIBLE);
502515
}
503516
}

app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class VaultEntry extends UUIDMap.Value {
2727
private String _note = "";
2828
private String _oldGroup;
2929
private Set<UUID> _groups = new TreeSet<>();
30+
private boolean _isArchived;
3031

3132
private VaultEntry(UUID uuid, OtpInfo info) {
3233
super(uuid);
@@ -66,6 +67,7 @@ public JSONObject toJson() {
6667
groupUuids.put(uuid.toString());
6768
}
6869
obj.put("groups", groupUuids);
70+
obj.put("archived", _isArchived);
6971

7072
} catch (JSONException e) {
7173
throw new RuntimeException(e);
@@ -90,6 +92,7 @@ public static VaultEntry fromJson(JSONObject obj) throws VaultEntryException {
9092
entry.setIssuer(obj.getString("issuer"));
9193
entry.setNote(obj.optString("note", ""));
9294
entry.setIsFavorite(obj.optBoolean("favorite", false));
95+
entry.setIsArchived(obj.optBoolean("archived", false));
9396

9497
// If the entry contains a list of group UUID's, assume conversion from the
9598
// old group system has already taken place and ignore the old group field.
@@ -148,6 +151,10 @@ public boolean isFavorite() {
148151
return _isFavorite;
149152
}
150153

154+
public boolean isArchived() {
155+
return _isArchived;
156+
}
157+
151158
public void setName(String name) {
152159
_name = name;
153160
}
@@ -200,6 +207,10 @@ public void setIsFavorite(boolean isFavorite) {
200207
_isFavorite = isFavorite;
201208
}
202209

210+
public void setIsArchived(boolean isArchived) {
211+
_isArchived = isArchived;
212+
}
213+
203214
void setOldGroup(String oldGroup) {
204215
_oldGroup = oldGroup;
205216
}
@@ -230,7 +241,8 @@ && getInfo().equals(entry.getInfo())
230241
&& Objects.equals(getIcon(), entry.getIcon())
231242
&& getNote().equals(entry.getNote())
232243
&& isFavorite() == entry.isFavorite()
233-
&& getGroups().equals(entry.getGroups());
244+
&& getGroups().equals(entry.getGroups())
245+
&& isArchived() == entry.isArchived();
234246
}
235247

236248
/**

app/src/main/java/com/beemdevelopment/aegis/vault/VaultRepository.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,9 @@ public Collection<VaultGroup> getGroups() {
321321
public Collection<VaultGroup> getUsedGroups() {
322322
Set<UUID> usedGroups = new HashSet<>();
323323
for (VaultEntry entry : getEntries()) {
324-
usedGroups.addAll(entry.getGroups());
324+
if (!entry.isArchived()) {
325+
usedGroups.addAll(entry.getGroups());
326+
}
325327
}
326328

327329
return getGroups().stream()

app/src/main/res/layout/fragment_entry_list_view.xml

+9
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,13 @@
6363
</LinearLayout>
6464

6565
</LinearLayout>
66+
67+
<TextView
68+
android:id="@+id/archive_empty_text"
69+
android:layout_width="match_parent"
70+
android:layout_height="match_parent"
71+
android:text="@string/archive_empty"
72+
android:gravity="center"
73+
android:visibility="gone" />
74+
6675
</LinearLayout>

app/src/main/res/menu/menu_action_mode.xml

+7
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646
android:icon="@drawable/ic_outline_qr_code_2_24"
4747
app:showAsAction="always"/>
4848

49+
<item
50+
android:id="@+id/action_restore"
51+
android:title="@string/action_restore"
52+
android:orderInCategory="120"
53+
android:icon="@drawable/ic_outline_refresh_24"
54+
app:showAsAction="ifRoom"/>
55+
4956
<item
5057
android:id="@+id/action_delete"
5158
android:title="@string/action_delete"

0 commit comments

Comments
 (0)