Merge pull request #1310 from michaelschattgen/feature/last-used

Add ability to sort based on last used timestamp
This commit is contained in:
Alexander Bakker 2024-03-16 16:15:26 +01:00 committed by GitHub
commit 6fc9cd5a71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 130 additions and 3 deletions

View file

@ -235,10 +235,51 @@ public class Preferences {
setUsageCount(usageCounts);
}
public long getLastUsedTimestamp(UUID uuid) {
Map<UUID, Long> 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<UUID, Long> getLastUsedTimestamps() {
Map<UUID, Long> 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<UUID, Long> lastUsedTimestamps) {
JSONArray lastUsedTimestampJson = new JSONArray();
for (Map.Entry<UUID, Long> 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<UUID, Integer> getUsageCounts() {
Map<UUID, Integer> usageCounts = new HashMap<>();
String usageCount = _prefs.getString("pref_usage_count", "");

View file

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

View file

@ -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<VaultEntry> {
@Override
public int compare(VaultEntry a, VaultEntry b) {
return Long.compare(a.getLastUsedTimestamp(), b.getLastUsedTimestamp());
}
}

View file

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

View file

@ -241,6 +241,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
_prefs.setUsageCount(usageMap);
}
Map<UUID, Long> 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;

View file

@ -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<RecyclerView.ViewHolder>
private List<VaultEntry> _shownEntries;
private List<VaultEntry> _selectedEntries;
private Map<UUID, Integer> _usageCounts;
private Map<UUID, Long> _lastUsedTimestamps;
private VaultEntry _focusedEntry;
private VaultEntry _clickedEntry;
private Preferences.CodeGrouping _codeGroupSize;
@ -190,6 +192,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
public void addEntries(Collection<VaultEntry> 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);
@ -408,6 +411,10 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
public Map<UUID, Integer> getUsageCounts() { return _usageCounts; }
public void setLastUsedTimestamps(Map<UUID, Long> lastUsedTimestamps) { _lastUsedTimestamps = lastUsedTimestamps; }
public Map<UUID, Long> getLastUsedTimestamps() { return _lastUsedTimestamps; }
public int getShownFavoritesCount() {
return (int) _shownEntries.stream().filter(VaultEntry::isFavorite).count();
}
@ -810,6 +817,8 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
int usageCount = _usageCounts.get(entry.getUUID());
_usageCounts.put(entry.getUUID(), ++usageCount);
}
_lastUsedTimestamps.put(entry.getUUID(), new Date().getTime());
}
public boolean isDragAndDropAllowed() {

View file

@ -223,6 +223,14 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
return _adapter.getUsageCounts();
}
public void setLastUsedTimestamps(Map<UUID, Long> lastUsedTimestamps) {
_adapter.setLastUsedTimestamps(lastUsedTimestamps);
}
public Map<UUID, Long> getLastUsedTimestamps() {
return _adapter.getLastUsedTimestamps();
}
public void setSearchFilter(String search) {
_adapter.setSearchFilter(search);
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());

View file

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

View file

@ -377,8 +377,22 @@
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/text_last_used"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:layout_centerInParent="true"
android:layout_gravity="bottom|center"
android:textSize="14sp" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -40,6 +40,9 @@
<item
android:id="@+id/menu_sort_usage_count"
android:title="@string/sort_usage_count"/>
<item
android:id="@+id/menu_sort_last_used"
android:title="@string/sort_last_used"/>
</group>
</menu>
</item>

View file

@ -303,7 +303,10 @@
<string name="sort_alphabetically_name">Account (A to Z)</string>
<string name="sort_alphabetically_name_reverse">Account (Z to A)</string>
<string name="sort_usage_count">Usage count</string>
<string name="sort_last_used">Last used</string>
<string name="sort_custom">Custom</string>
<string name="last_used">Last used</string>
<string name="last_used_never">never</string>
<string name="new_group">New group…</string>
<string name="group">Group</string>
<string name="group_name_hint">Group name</string>