diff --git a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java index befc3522..d94668b1 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java +++ b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java @@ -235,10 +235,51 @@ public class Preferences { setUsageCount(usageCounts); } + public long getLastUsedTimestamp(UUID uuid) { + Map timestamps = getLastUsedTimestamps(); + if (timestamps != null && timestamps.size() > 0){ + Long timestamp = timestamps.get(uuid); + return timestamp != null ? timestamp : 0; + } + + return 0; + } + public void clearUsageCount() { _prefs.edit().remove("pref_usage_count").apply(); } + public Map getLastUsedTimestamps() { + Map lastUsedTimestamps = new HashMap<>(); + String lastUsedTimestamp = _prefs.getString("pref_last_used_timestamps", ""); + try { + JSONArray arr = new JSONArray(lastUsedTimestamp); + for (int i = 0; i < arr.length(); i++) { + JSONObject json = arr.getJSONObject(i); + lastUsedTimestamps.put(UUID.fromString(json.getString("uuid")), json.getLong("timestamp")); + } + } catch (JSONException ignored) { + } + + return lastUsedTimestamps; + } + + public void setLastUsedTimestamps(Map lastUsedTimestamps) { + JSONArray lastUsedTimestampJson = new JSONArray(); + for (Map.Entry entry : lastUsedTimestamps.entrySet()) { + JSONObject entryJson = new JSONObject(); + try { + entryJson.put("uuid", entry.getKey()); + entryJson.put("timestamp", entry.getValue()); + lastUsedTimestampJson.put(entryJson); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + _prefs.edit().putString("pref_last_used_timestamps", lastUsedTimestampJson.toString()).apply(); + } + public Map getUsageCounts() { Map usageCounts = new HashMap<>(); String usageCount = _prefs.getString("pref_usage_count", ""); diff --git a/app/src/main/java/com/beemdevelopment/aegis/SortCategory.java b/app/src/main/java/com/beemdevelopment/aegis/SortCategory.java index 31dbb400..a7b3f664 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/SortCategory.java +++ b/app/src/main/java/com/beemdevelopment/aegis/SortCategory.java @@ -1,5 +1,6 @@ package com.beemdevelopment.aegis; +import com.beemdevelopment.aegis.helpers.comparators.LastUsedComparator; import com.beemdevelopment.aegis.helpers.comparators.UsageCountComparator; import com.beemdevelopment.aegis.vault.VaultEntry; import com.beemdevelopment.aegis.helpers.comparators.AccountNameComparator; @@ -14,7 +15,8 @@ public enum SortCategory { ACCOUNT_REVERSED, ISSUER, ISSUER_REVERSED, - USAGE_COUNT; + USAGE_COUNT, + LAST_USED; private static SortCategory[] _values; @@ -45,6 +47,8 @@ public enum SortCategory { case USAGE_COUNT: comparator = Collections.reverseOrder(new UsageCountComparator()); break; + case LAST_USED: + comparator = Collections.reverseOrder(new LastUsedComparator()); } return comparator; @@ -64,6 +68,8 @@ public enum SortCategory { return R.id.menu_sort_alphabetically_reverse; case USAGE_COUNT: return R.id.menu_sort_usage_count; + case LAST_USED: + return R.id.menu_sort_last_used; default: return R.id.menu_sort_custom; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/LastUsedComparator.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/LastUsedComparator.java new file mode 100644 index 00000000..5fc85edf --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/comparators/LastUsedComparator.java @@ -0,0 +1,12 @@ +package com.beemdevelopment.aegis.helpers.comparators; + +import com.beemdevelopment.aegis.vault.VaultEntry; + +import java.util.Comparator; + +public class LastUsedComparator implements Comparator { + @Override + public int compare(VaultEntry a, VaultEntry b) { + return Long.compare(a.getLastUsedTimestamp(), b.getLastUsedTimestamp()); + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java index f9472c58..c2280cc5 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java @@ -19,6 +19,7 @@ import android.widget.AutoCompleteTextView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; +import android.widget.TextView; import androidx.activity.OnBackPressedCallback; import androidx.activity.result.ActivityResultLauncher; @@ -75,10 +76,12 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -113,6 +116,7 @@ public class EditEntryActivity extends AegisActivity { private LinearLayout _textPinLayout; private TextInputEditText _textUsageCount; private TextInputEditText _textNote; + private TextView _textLastUsed; private AutoCompleteTextView _dropdownType; private AutoCompleteTextView _dropdownAlgo; @@ -200,6 +204,7 @@ public class EditEntryActivity extends AegisActivity { _textPinLayout = findViewById(R.id.layout_pin); _textUsageCount = findViewById(R.id.text_usage_count); _textNote = findViewById(R.id.text_note); + _textLastUsed = findViewById(R.id.text_last_used); _dropdownType = findViewById(R.id.dropdown_type); DropdownHelper.fillDropdown(this, _dropdownType, R.array.otp_types_array); _dropdownAlgoLayout = findViewById(R.id.dropdown_algo_layout); @@ -378,6 +383,7 @@ public class EditEntryActivity extends AegisActivity { }); _textUsageCount.setText(_prefs.getUsageCount(entryUUID).toString()); + setLastUsedTimestamp(_prefs.getLastUsedTimestamp(entryUUID)); } private void updateAdvancedFieldStatus(String otpType) { @@ -608,6 +614,16 @@ public class EditEntryActivity extends AegisActivity { saveAndFinish(entry, false); } + private void setLastUsedTimestamp(long timestamp) { + String readableDate = getString(R.string.last_used_never); + if (timestamp != 0) { + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.getDefault()); + readableDate = dateFormat.format(new Date(timestamp)); + } + + _textLastUsed.setText(String.format("%s: %s", getString(R.string.last_used), readableDate)); + } + private void deleteAndFinish(VaultEntry entry) { _vaultManager.getVault().removeEntry(entry); saveAndFinish(entry, true); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index b81c4335..1f9f0505 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -241,6 +241,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene _prefs.setUsageCount(usageMap); } + Map lastUsedMap = _entryListView.getLastUsedTimestamps(); + if (lastUsedMap != null) { + _prefs.setLastUsedTimestamps(lastUsedMap); + } + super.onPause(); } @@ -696,6 +701,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene // update the usage counts in case they are edited outside of the EntryListView _entryListView.setUsageCounts(_prefs.getUsageCounts()); + _entryListView.setLastUsedTimestamps(_prefs.getLastUsedTimestamps()); + // refresh all codes to prevent showing old ones _entryListView.refresh(false); } else { @@ -825,6 +832,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene sortCategory = SortCategory.ACCOUNT_REVERSED; } else if (subItemId == R.id.menu_sort_usage_count) { sortCategory = SortCategory.USAGE_COUNT; + } else if (subItemId == R.id.menu_sort_last_used) { + sortCategory = SortCategory.LAST_USED; } else { sortCategory = SortCategory.CUSTOM; } @@ -847,6 +856,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene private void loadEntries() { if (!_loaded) { _entryListView.setUsageCounts(_prefs.getUsageCounts()); + _entryListView.setLastUsedTimestamps(_prefs.getLastUsedTimestamps()); _entryListView.addEntries(_vaultManager.getVault().getEntries()); _entryListView.runEntriesAnimation(); _loaded = true; diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java index 29445d08..3b588a5b 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryAdapter.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,6 +52,7 @@ public class EntryAdapter extends RecyclerView.Adapter private List _shownEntries; private List _selectedEntries; private Map _usageCounts; + private Map _lastUsedTimestamps; private VaultEntry _focusedEntry; private VaultEntry _clickedEntry; private Preferences.CodeGrouping _codeGroupSize; @@ -190,6 +192,7 @@ public class EntryAdapter extends RecyclerView.Adapter public void addEntries(Collection entries) { for (VaultEntry entry: entries) { entry.setUsageCount(_usageCounts.containsKey(entry.getUUID()) ? _usageCounts.get(entry.getUUID()) : 0); + entry.setLastUsedTimestamp(_lastUsedTimestamps.containsKey(entry.getUUID()) ? _lastUsedTimestamps.get(entry.getUUID()) : 0); } _entries.addAll(entries); @@ -407,6 +410,10 @@ public class EntryAdapter extends RecyclerView.Adapter public Map getUsageCounts() { return _usageCounts; } + public void setLastUsedTimestamps(Map lastUsedTimestamps) { _lastUsedTimestamps = lastUsedTimestamps; } + + public Map getLastUsedTimestamps() { return _lastUsedTimestamps; } + public int getShownFavoritesCount() { return (int) _shownEntries.stream().filter(VaultEntry::isFavorite).count(); } @@ -805,6 +812,8 @@ public class EntryAdapter extends RecyclerView.Adapter int usageCount = _usageCounts.get(entry.getUUID()); _usageCounts.put(entry.getUUID(), ++usageCount); } + + _lastUsedTimestamps.put(entry.getUUID(), new Date().getTime()); } public boolean isDragAndDropAllowed() { diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java index 10107474..74e43188 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java @@ -223,6 +223,14 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { return _adapter.getUsageCounts(); } + public void setLastUsedTimestamps(Map lastUsedTimestamps) { + _adapter.setLastUsedTimestamps(lastUsedTimestamps); + } + + public Map getLastUsedTimestamps() { + return _adapter.getLastUsedTimestamps(); + } + public void setSearchFilter(String search) { _adapter.setSearchFilter(search); _touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed()); diff --git a/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java b/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java index 90940f2f..080580c9 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java +++ b/app/src/main/java/com/beemdevelopment/aegis/vault/VaultEntry.java @@ -23,6 +23,7 @@ public class VaultEntry extends UUIDMap.Value { private VaultEntryIcon _icon; private boolean _isFavorite; private int _usageCount; + private long _lastUsedTimestamp; private String _note = ""; private String _oldGroup; private Set _groups = new TreeSet<>(); @@ -135,6 +136,10 @@ public class VaultEntry extends UUIDMap.Value { return _usageCount; } + public long getLastUsedTimestamp() { + return _lastUsedTimestamp; + } + public String getNote() { return _note; } @@ -143,8 +148,6 @@ public class VaultEntry extends UUIDMap.Value { return _isFavorite; } - ; - public void setName(String name) { _name = name; } @@ -187,6 +190,8 @@ public class VaultEntry extends UUIDMap.Value { _usageCount = usageCount; } + public void setLastUsedTimestamp(long lastUsedTimestamp) { _lastUsedTimestamp = lastUsedTimestamp; } + public void setNote(String note) { _note = note; } diff --git a/app/src/main/res/layout/activity_edit_entry.xml b/app/src/main/res/layout/activity_edit_entry.xml index b45503ef..163b800e 100644 --- a/app/src/main/res/layout/activity_edit_entry.xml +++ b/app/src/main/res/layout/activity_edit_entry.xml @@ -376,8 +376,22 @@ + + + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index cb4f3922..4a650dd3 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -40,6 +40,9 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 826f7ab0..001a25cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -303,7 +303,10 @@ Account (A to Z) Account (Z to A) Usage count + Last used Custom + Last used + never New group… Group Group name