mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-20 22:09:12 +00:00
Add ability to sort based on last used timestamp
This commit is contained in:
parent
dfd720b406
commit
9bae4d6bbc
11 changed files with 130 additions and 3 deletions
|
@ -235,10 +235,51 @@ public class Preferences {
|
||||||
setUsageCount(usageCounts);
|
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() {
|
public void clearUsageCount() {
|
||||||
_prefs.edit().remove("pref_usage_count").apply();
|
_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() {
|
public Map<UUID, Integer> getUsageCounts() {
|
||||||
Map<UUID, Integer> usageCounts = new HashMap<>();
|
Map<UUID, Integer> usageCounts = new HashMap<>();
|
||||||
String usageCount = _prefs.getString("pref_usage_count", "");
|
String usageCount = _prefs.getString("pref_usage_count", "");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.beemdevelopment.aegis;
|
package com.beemdevelopment.aegis;
|
||||||
|
|
||||||
|
import com.beemdevelopment.aegis.helpers.comparators.LastUsedComparator;
|
||||||
import com.beemdevelopment.aegis.helpers.comparators.UsageCountComparator;
|
import com.beemdevelopment.aegis.helpers.comparators.UsageCountComparator;
|
||||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||||
import com.beemdevelopment.aegis.helpers.comparators.AccountNameComparator;
|
import com.beemdevelopment.aegis.helpers.comparators.AccountNameComparator;
|
||||||
|
@ -14,7 +15,8 @@ public enum SortCategory {
|
||||||
ACCOUNT_REVERSED,
|
ACCOUNT_REVERSED,
|
||||||
ISSUER,
|
ISSUER,
|
||||||
ISSUER_REVERSED,
|
ISSUER_REVERSED,
|
||||||
USAGE_COUNT;
|
USAGE_COUNT,
|
||||||
|
LAST_USED;
|
||||||
|
|
||||||
private static SortCategory[] _values;
|
private static SortCategory[] _values;
|
||||||
|
|
||||||
|
@ -45,6 +47,8 @@ public enum SortCategory {
|
||||||
case USAGE_COUNT:
|
case USAGE_COUNT:
|
||||||
comparator = Collections.reverseOrder(new UsageCountComparator());
|
comparator = Collections.reverseOrder(new UsageCountComparator());
|
||||||
break;
|
break;
|
||||||
|
case LAST_USED:
|
||||||
|
comparator = Collections.reverseOrder(new LastUsedComparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
return comparator;
|
return comparator;
|
||||||
|
@ -64,6 +68,8 @@ public enum SortCategory {
|
||||||
return R.id.menu_sort_alphabetically_reverse;
|
return R.id.menu_sort_alphabetically_reverse;
|
||||||
case USAGE_COUNT:
|
case USAGE_COUNT:
|
||||||
return R.id.menu_sort_usage_count;
|
return R.id.menu_sort_usage_count;
|
||||||
|
case LAST_USED:
|
||||||
|
return R.id.menu_sort_last_used;
|
||||||
default:
|
default:
|
||||||
return R.id.menu_sort_custom;
|
return R.id.menu_sort_custom;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import android.widget.AutoCompleteTextView;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
@ -75,10 +76,12 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -113,6 +116,7 @@ public class EditEntryActivity extends AegisActivity {
|
||||||
private LinearLayout _textPinLayout;
|
private LinearLayout _textPinLayout;
|
||||||
private TextInputEditText _textUsageCount;
|
private TextInputEditText _textUsageCount;
|
||||||
private TextInputEditText _textNote;
|
private TextInputEditText _textNote;
|
||||||
|
private TextView _textLastUsed;
|
||||||
|
|
||||||
private AutoCompleteTextView _dropdownType;
|
private AutoCompleteTextView _dropdownType;
|
||||||
private AutoCompleteTextView _dropdownAlgo;
|
private AutoCompleteTextView _dropdownAlgo;
|
||||||
|
@ -200,6 +204,7 @@ public class EditEntryActivity extends AegisActivity {
|
||||||
_textPinLayout = findViewById(R.id.layout_pin);
|
_textPinLayout = findViewById(R.id.layout_pin);
|
||||||
_textUsageCount = findViewById(R.id.text_usage_count);
|
_textUsageCount = findViewById(R.id.text_usage_count);
|
||||||
_textNote = findViewById(R.id.text_note);
|
_textNote = findViewById(R.id.text_note);
|
||||||
|
_textLastUsed = findViewById(R.id.text_last_used);
|
||||||
_dropdownType = findViewById(R.id.dropdown_type);
|
_dropdownType = findViewById(R.id.dropdown_type);
|
||||||
DropdownHelper.fillDropdown(this, _dropdownType, R.array.otp_types_array);
|
DropdownHelper.fillDropdown(this, _dropdownType, R.array.otp_types_array);
|
||||||
_dropdownAlgoLayout = findViewById(R.id.dropdown_algo_layout);
|
_dropdownAlgoLayout = findViewById(R.id.dropdown_algo_layout);
|
||||||
|
@ -378,6 +383,7 @@ public class EditEntryActivity extends AegisActivity {
|
||||||
});
|
});
|
||||||
|
|
||||||
_textUsageCount.setText(_prefs.getUsageCount(entryUUID).toString());
|
_textUsageCount.setText(_prefs.getUsageCount(entryUUID).toString());
|
||||||
|
setLastUsedTimestamp(_prefs.getLastUsedTimestamp(entryUUID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAdvancedFieldStatus(String otpType) {
|
private void updateAdvancedFieldStatus(String otpType) {
|
||||||
|
@ -608,6 +614,16 @@ public class EditEntryActivity extends AegisActivity {
|
||||||
saveAndFinish(entry, false);
|
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) {
|
private void deleteAndFinish(VaultEntry entry) {
|
||||||
_vaultManager.getVault().removeEntry(entry);
|
_vaultManager.getVault().removeEntry(entry);
|
||||||
saveAndFinish(entry, true);
|
saveAndFinish(entry, true);
|
||||||
|
|
|
@ -241,6 +241,11 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
||||||
_prefs.setUsageCount(usageMap);
|
_prefs.setUsageCount(usageMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<UUID, Long> lastUsedMap = _entryListView.getLastUsedTimestamps();
|
||||||
|
if (lastUsedMap != null) {
|
||||||
|
_prefs.setLastUsedTimestamps(lastUsedMap);
|
||||||
|
}
|
||||||
|
|
||||||
super.onPause();
|
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
|
// update the usage counts in case they are edited outside of the EntryListView
|
||||||
_entryListView.setUsageCounts(_prefs.getUsageCounts());
|
_entryListView.setUsageCounts(_prefs.getUsageCounts());
|
||||||
|
|
||||||
|
_entryListView.setLastUsedTimestamps(_prefs.getLastUsedTimestamps());
|
||||||
|
|
||||||
// refresh all codes to prevent showing old ones
|
// refresh all codes to prevent showing old ones
|
||||||
_entryListView.refresh(false);
|
_entryListView.refresh(false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -825,6 +832,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
||||||
sortCategory = SortCategory.ACCOUNT_REVERSED;
|
sortCategory = SortCategory.ACCOUNT_REVERSED;
|
||||||
} else if (subItemId == R.id.menu_sort_usage_count) {
|
} else if (subItemId == R.id.menu_sort_usage_count) {
|
||||||
sortCategory = SortCategory.USAGE_COUNT;
|
sortCategory = SortCategory.USAGE_COUNT;
|
||||||
|
} else if (subItemId == R.id.menu_sort_last_used) {
|
||||||
|
sortCategory = SortCategory.LAST_USED;
|
||||||
} else {
|
} else {
|
||||||
sortCategory = SortCategory.CUSTOM;
|
sortCategory = SortCategory.CUSTOM;
|
||||||
}
|
}
|
||||||
|
@ -847,6 +856,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
||||||
private void loadEntries() {
|
private void loadEntries() {
|
||||||
if (!_loaded) {
|
if (!_loaded) {
|
||||||
_entryListView.setUsageCounts(_prefs.getUsageCounts());
|
_entryListView.setUsageCounts(_prefs.getUsageCounts());
|
||||||
|
_entryListView.setLastUsedTimestamps(_prefs.getLastUsedTimestamps());
|
||||||
_entryListView.addEntries(_vaultManager.getVault().getEntries());
|
_entryListView.addEntries(_vaultManager.getVault().getEntries());
|
||||||
_entryListView.runEntriesAnimation();
|
_entryListView.runEntriesAnimation();
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
|
|
|
@ -37,6 +37,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -51,6 +52,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
private List<VaultEntry> _shownEntries;
|
private List<VaultEntry> _shownEntries;
|
||||||
private List<VaultEntry> _selectedEntries;
|
private List<VaultEntry> _selectedEntries;
|
||||||
private Map<UUID, Integer> _usageCounts;
|
private Map<UUID, Integer> _usageCounts;
|
||||||
|
private Map<UUID, Long> _lastUsedTimestamps;
|
||||||
private VaultEntry _focusedEntry;
|
private VaultEntry _focusedEntry;
|
||||||
private VaultEntry _clickedEntry;
|
private VaultEntry _clickedEntry;
|
||||||
private Preferences.CodeGrouping _codeGroupSize;
|
private Preferences.CodeGrouping _codeGroupSize;
|
||||||
|
@ -190,6 +192,7 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
public void addEntries(Collection<VaultEntry> entries) {
|
public void addEntries(Collection<VaultEntry> entries) {
|
||||||
for (VaultEntry entry: entries) {
|
for (VaultEntry entry: entries) {
|
||||||
entry.setUsageCount(_usageCounts.containsKey(entry.getUUID()) ? _usageCounts.get(entry.getUUID()) : 0);
|
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);
|
_entries.addAll(entries);
|
||||||
|
@ -407,6 +410,10 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
|
|
||||||
public Map<UUID, Integer> getUsageCounts() { return _usageCounts; }
|
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() {
|
public int getShownFavoritesCount() {
|
||||||
return (int) _shownEntries.stream().filter(VaultEntry::isFavorite).count();
|
return (int) _shownEntries.stream().filter(VaultEntry::isFavorite).count();
|
||||||
}
|
}
|
||||||
|
@ -805,6 +812,8 @@ public class EntryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
int usageCount = _usageCounts.get(entry.getUUID());
|
int usageCount = _usageCounts.get(entry.getUUID());
|
||||||
_usageCounts.put(entry.getUUID(), ++usageCount);
|
_usageCounts.put(entry.getUUID(), ++usageCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_lastUsedTimestamps.put(entry.getUUID(), new Date().getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDragAndDropAllowed() {
|
public boolean isDragAndDropAllowed() {
|
||||||
|
|
|
@ -223,6 +223,14 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
|
||||||
return _adapter.getUsageCounts();
|
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) {
|
public void setSearchFilter(String search) {
|
||||||
_adapter.setSearchFilter(search);
|
_adapter.setSearchFilter(search);
|
||||||
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());
|
_touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed());
|
||||||
|
|
|
@ -23,6 +23,7 @@ public class VaultEntry extends UUIDMap.Value {
|
||||||
private VaultEntryIcon _icon;
|
private VaultEntryIcon _icon;
|
||||||
private boolean _isFavorite;
|
private boolean _isFavorite;
|
||||||
private int _usageCount;
|
private int _usageCount;
|
||||||
|
private long _lastUsedTimestamp;
|
||||||
private String _note = "";
|
private String _note = "";
|
||||||
private String _oldGroup;
|
private String _oldGroup;
|
||||||
private Set<UUID> _groups = new TreeSet<>();
|
private Set<UUID> _groups = new TreeSet<>();
|
||||||
|
@ -135,6 +136,10 @@ public class VaultEntry extends UUIDMap.Value {
|
||||||
return _usageCount;
|
return _usageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLastUsedTimestamp() {
|
||||||
|
return _lastUsedTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public String getNote() {
|
public String getNote() {
|
||||||
return _note;
|
return _note;
|
||||||
}
|
}
|
||||||
|
@ -143,8 +148,6 @@ public class VaultEntry extends UUIDMap.Value {
|
||||||
return _isFavorite;
|
return _isFavorite;
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
_name = name;
|
_name = name;
|
||||||
}
|
}
|
||||||
|
@ -187,6 +190,8 @@ public class VaultEntry extends UUIDMap.Value {
|
||||||
_usageCount = usageCount;
|
_usageCount = usageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLastUsedTimestamp(long lastUsedTimestamp) { _lastUsedTimestamp = lastUsedTimestamp; }
|
||||||
|
|
||||||
public void setNote(String note) {
|
public void setNote(String note) {
|
||||||
_note = note;
|
_note = note;
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,8 +376,22 @@
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</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>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_sort_usage_count"
|
android:id="@+id/menu_sort_usage_count"
|
||||||
android:title="@string/sort_usage_count"/>
|
android:title="@string/sort_usage_count"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_sort_last_used"
|
||||||
|
android:title="@string/sort_last_used"/>
|
||||||
</group>
|
</group>
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -303,7 +303,10 @@
|
||||||
<string name="sort_alphabetically_name">Account (A to Z)</string>
|
<string name="sort_alphabetically_name">Account (A to Z)</string>
|
||||||
<string name="sort_alphabetically_name_reverse">Account (Z to A)</string>
|
<string name="sort_alphabetically_name_reverse">Account (Z to A)</string>
|
||||||
<string name="sort_usage_count">Usage count</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="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="new_group">New group…</string>
|
||||||
<string name="group">Group</string>
|
<string name="group">Group</string>
|
||||||
<string name="group_name_hint">Group name</string>
|
<string name="group_name_hint">Group name</string>
|
||||||
|
|
Loading…
Add table
Reference in a new issue