Add usage count to entries

Update Preferences.java

Fix build error and review improvements
This commit is contained in:
Michael Schättgen 2021-06-02 18:02:38 +02:00
parent 7506c70236
commit 34cd16f240
16 changed files with 235 additions and 1 deletions

View file

@ -12,9 +12,15 @@ import org.json.JSONException;
import java.util.ArrayList;
import java.util.Collections;
import org.json.JSONObject;
import java.util.Date;
import java.util.List;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class Preferences {
@ -147,6 +153,56 @@ public class Preferences {
_prefs.edit().putInt("pref_current_view_mode", viewMode.ordinal()).apply();
}
public Integer getUsageCount(UUID uuid) {
Integer usageCount = getUsageCounts().get(uuid);
return usageCount != null ? usageCount : 0;
}
public void resetUsageCount(UUID uuid) {
Map<UUID, Integer> usageCounts = getUsageCounts();
usageCounts.put(uuid, 0);
setUsageCount(usageCounts);
}
public void clearUsageCount() {
_prefs.edit().remove("pref_usage_count").apply();
}
public Map<UUID, Integer> getUsageCounts() {
Map<UUID, Integer> usageCounts = new HashMap<>();
String usageCount = _prefs.getString("pref_usage_count", "");
try {
JSONArray arr = new JSONArray(usageCount);
for(int i = 0; i < arr.length(); i++) {
JSONObject json = arr.getJSONObject(i);
usageCounts.put(UUID.fromString(json.getString("uuid")), json.getInt("count"));
}
} catch (JSONException e) {
e.printStackTrace();
}
return usageCounts;
}
public void setUsageCount(Map<UUID, Integer> usageCounts) {
JSONArray usageCountJson = new JSONArray();
for (Map.Entry<UUID, Integer> entry : usageCounts.entrySet()) {
JSONObject entryJson = new JSONObject();
try {
entryJson.put("uuid", entry.getKey());
entryJson.put("count", entry.getValue());
usageCountJson.put(entryJson);
} catch (JSONException e) {
e.printStackTrace();
}
}
_prefs.edit().putString("pref_usage_count", usageCountJson.toString()).apply();
}
public int getTimeout() {
return _prefs.getInt("pref_timeout", -1);
}

View file

@ -1,5 +1,6 @@
package com.beemdevelopment.aegis;
import com.beemdevelopment.aegis.helpers.comparators.UsageCountComparator;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.helpers.comparators.AccountNameComparator;
import com.beemdevelopment.aegis.helpers.comparators.IssuerNameComparator;
@ -12,7 +13,8 @@ public enum SortCategory {
ACCOUNT,
ACCOUNT_REVERSED,
ISSUER,
ISSUER_REVERSED;
ISSUER_REVERSED,
USAGE_COUNT;
private static SortCategory[] _values;
@ -40,6 +42,9 @@ public enum SortCategory {
case ISSUER_REVERSED:
comparator = Collections.reverseOrder(new IssuerNameComparator());
break;
case USAGE_COUNT:
comparator = Collections.reverseOrder(new UsageCountComparator());
break;
}
return comparator;
@ -57,6 +62,8 @@ public enum SortCategory {
return R.id.menu_sort_alphabetically;
case ISSUER_REVERSED:
return R.id.menu_sort_alphabetically_reverse;
case USAGE_COUNT:
return R.id.menu_sort_usage_count;
default:
return R.id.menu_sort_custom;
}

View file

@ -0,0 +1,12 @@
package com.beemdevelopment.aegis.helpers.comparators;
import com.beemdevelopment.aegis.vault.VaultEntry;
import java.util.Comparator;
public class UsageCountComparator implements Comparator<VaultEntry> {
@Override
public int compare(VaultEntry a, VaultEntry b) {
return Integer.compare(a.getUsageCount(), b.getUsageCount());
}
}

View file

@ -22,6 +22,8 @@ import android.widget.AutoCompleteTextView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -100,6 +102,7 @@ public class EditEntryActivity extends AegisActivity {
private TextInputEditText _textDigits;
private TextInputLayout _textDigitsLayout;
private TextInputEditText _textSecret;
private TextInputEditText _textUsageCount;
private AutoCompleteTextView _dropdownType;
private AutoCompleteTextView _dropdownAlgo;
@ -150,6 +153,7 @@ public class EditEntryActivity extends AegisActivity {
_textDigits = findViewById(R.id.text_digits);
_textDigitsLayout = findViewById(R.id.text_digits_layout);
_textSecret = findViewById(R.id.text_secret);
_textUsageCount = findViewById(R.id.text_usage_count);
_dropdownType = findViewById(R.id.dropdown_type);
DropdownHelper.fillDropdown(this, _dropdownType, R.array.otp_types_array);
_dropdownAlgoLayout = findViewById(R.id.dropdown_algo_layout);
@ -279,6 +283,8 @@ public class EditEntryActivity extends AegisActivity {
}
}
});
_textUsageCount.setText(getPreferences().getUsageCount(entryUUID).toString());
}
private void updateAdvancedFieldStatus(String otpType) {
@ -405,6 +411,14 @@ public class EditEntryActivity extends AegisActivity {
case R.id.action_edit_icon:
startIconSelection();
break;
case R.id.action_reset_usage_count:
Dialogs.showSecureDialog(new AlertDialog.Builder(this)
.setTitle(R.string.action_reset_usage_count)
.setMessage(R.string.action_reset_usage_count_dialog)
.setPositiveButton(android.R.string.yes, (dialog, which) -> resetUsageCount())
.setNegativeButton(android.R.string.no, null)
.create());
break;
case R.id.action_default_icon:
TextDrawable drawable = TextDrawableHelper.generate(_origEntry.getIssuer(), _origEntry.getName(), _iconView);
_iconView.setImageDrawable(drawable);
@ -431,6 +445,11 @@ public class EditEntryActivity extends AegisActivity {
AegisActivity.Helper.startExtActivityForResult(this, chooserIntent, PICK_IMAGE_REQUEST);
}
private void resetUsageCount() {
getPreferences().resetUsageCount(_origEntry.getUUID());
_textUsageCount.setText("0");
}
private void startIconSelection() {
List<IconPack> iconPacks = getApp().getIconPackManager().getIconPacks().stream()
.sorted(Comparator.comparing(IconPack::getName))

View file

@ -57,6 +57,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@ -165,6 +166,17 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
super.onDestroy();
}
@Override
protected void onPause() {
Map<UUID, Integer> usageMap = _entryListView.getUsageCounts();
if (usageMap != null) {
getPreferences().setUsageCount(usageMap);
}
super.onPause();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
_isAuthenticating = false;
@ -487,6 +499,9 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
// update the list of groups in the entry list view so that the chip gets updated
_entryListView.setGroups(_vault.getGroups());
// update the usage counts in case they are edited outside of the entrylistview
_entryListView.setUsageCounts(getPreferences().getUsageCounts());
// refresh all codes to prevent showing old ones
_entryListView.refresh(false);
} else {
@ -605,6 +620,9 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
case R.id.menu_sort_alphabetically_name_reverse:
sortCategory = SortCategory.ACCOUNT_REVERSED;
break;
case R.id.menu_sort_usage_count:
sortCategory = SortCategory.USAGE_COUNT;
break;
case R.id.menu_sort_custom:
default:
sortCategory = SortCategory.CUSTOM;
@ -625,6 +643,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
private void loadEntries() {
if (!_loaded) {
_entryListView.setUsageCounts(getPreferences().getUsageCounts());
_entryListView.addEntries(_vault.getEntries());
_entryListView.runEntriesAnimation();
_loaded = true;
@ -738,6 +757,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
_entryListView.clearEntries();
_loaded = false;
if (userInitiated) {
startAuthActivity(true);
} else {

View file

@ -21,6 +21,7 @@ import java.util.HashSet;
public class AppearancePreferencesFragment extends PreferencesFragment {
private Preference _groupsPreference;
private Preference _resetUsageCountPreference;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@ -36,6 +37,17 @@ public class AppearancePreferencesFragment extends PreferencesFragment {
return true;
});
_resetUsageCountPreference = findPreference("pref_reset_usage_count");
_resetUsageCountPreference.setOnPreferenceClickListener(preference -> {
Dialogs.showSecureDialog(new AlertDialog.Builder(getActivity())
.setTitle(R.string.preference_reset_usage_count)
.setMessage(R.string.preference_reset_usage_count_dialog)
.setPositiveButton(android.R.string.yes, (dialog, which) -> getPreferences().clearUsageCount())
.setNegativeButton(android.R.string.no, null)
.create());
return true;
});
int currentTheme = prefs.getCurrentTheme().ordinal();
Preference darkModePreference = findPreference("pref_dark_mode");
darkModePreference.setSummary(String.format("%s: %s", getString(R.string.selected), getResources().getStringArray(R.array.theme_titles)[currentTheme]));

View file

@ -25,6 +25,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements ItemTouchHelperAdapter {
@ -32,6 +33,7 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
private List<VaultEntry> _entries;
private List<VaultEntry> _shownEntries;
private List<VaultEntry> _selectedEntries;
private Map<UUID, Integer> _usageCounts;
private VaultEntry _focusedEntry;
private int _codeGroupSize;
private boolean _showAccountName;
@ -139,6 +141,10 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
}
public void addEntries(Collection<VaultEntry> entries) {
for (VaultEntry entry: entries) {
entry.setUsageCount(_usageCounts.containsKey(entry.getUUID()) ? _usageCounts.get(entry.getUUID()) : 0);
}
_entries.addAll(entries);
updateShownEntries();
checkPeriodUniformity(true);
@ -282,6 +288,14 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
_viewMode = viewMode;
}
public void setUsageCounts(Map<UUID, Integer> usageCounts) { _usageCounts = usageCounts; }
public Map<UUID, Integer> getUsageCounts() { return _usageCounts; }
public void setGroups(TreeSet<String> groups) {
_view.setGroups(groups);
}
@Override
public void onItemDismiss(int position) {
@ -363,6 +377,8 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
focusEntry(entry, _tapToRevealTime);
}
}
incrementUsageCount(entry);
} else {
if (_selectedEntries.contains(entry)) {
_view.onDeselect(entry);
@ -586,6 +602,15 @@ public class EntryAdapter extends RecyclerView.Adapter<EntryHolder> implements I
updateDraggableStatus();
}
private void incrementUsageCount(VaultEntry entry) {
if (!_usageCounts.containsKey(entry.getUUID())) {
_usageCounts.put(entry.getUUID(), 1);
} else {
int usageCount = _usageCounts.get(entry.getUUID());
_usageCounts.put(entry.getUUID(), ++usageCount);
}
}
public boolean isDragAndDropAllowed() {
return _sortCategory == SortCategory.CUSTOM && _groupFilter.isEmpty() && _searchFilter == null;
}

View file

@ -47,6 +47,7 @@ import com.google.android.material.chip.ChipGroup;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
@ -184,6 +185,14 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
}
}
public void setUsageCounts(Map<UUID, Integer> usageCounts) {
_adapter.setUsageCounts(usageCounts);
}
public Map<UUID, Integer> getUsageCounts() {
return _adapter.getUsageCounts();
}
public void setSearchFilter(String search) {
_adapter.setSearchFilter(search);
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());

View file

@ -141,6 +141,7 @@ public class UUIDMap <T extends UUIDMap.Value> implements Iterable<T>, Serializa
this(UUID.randomUUID());
}
@NonNull
public final UUID getUUID() {
return _uuid;
}

View file

@ -17,6 +17,8 @@ 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 = "";
@ -24,6 +26,7 @@ public class VaultEntry extends UUIDMap.Value {
private OtpInfo _info;
private byte[] _icon;
private IconType _iconType = IconType.INVALID;
private int _usageCount;
private VaultEntry(UUID uuid, OtpInfo info) {
super(uuid);
@ -130,6 +133,10 @@ public class VaultEntry extends UUIDMap.Value {
return _info;
}
public int getUsageCount() {
return _usageCount;
}
public void setName(String name) {
_name = name;
}
@ -155,6 +162,8 @@ public class VaultEntry extends UUIDMap.Value {
return _icon != null;
}
public void setUsageCount(int usageCount) { _usageCount = usageCount; }
@Override
public boolean equals(Object o) {
if (!(o instanceof VaultEntry)) {