Add dialog for sort and precise setting

This commit is contained in:
J-Jamet
2018-02-24 18:09:17 +01:00
parent a1720c1c79
commit 0ddd08bf6d
11 changed files with 439 additions and 135 deletions

View File

@@ -46,15 +46,19 @@ import com.keepassdroid.database.PwNode;
import com.keepassdroid.database.edit.AfterAddNodeOnFinish;
import com.keepassdroid.database.edit.OnFinish;
import com.keepassdroid.fragments.AssignMasterKeyDialogFragment;
import com.keepassdroid.fragments.SortDialogFragment;
import com.keepassdroid.settings.PrefsUtil;
import com.keepassdroid.tasks.UIToastTask;
import com.keepassdroid.utils.MenuUtil;
import com.keepassdroid.database.SortNodeEnum;
import com.keepassdroid.view.AssignPasswordHelper;
import com.kunzisoft.keepass.KeePass;
import com.kunzisoft.keepass.R;
public abstract class ListNodesActivity extends LockCloseListActivity
implements AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
NodeAdapter.OnNodeClickCallback {
NodeAdapter.OnNodeClickCallback,
SortDialogFragment.SortSelectionListener {
protected RecyclerView mList;
protected NodeAdapter mAdapter;
@@ -142,39 +146,31 @@ public abstract class ListNodesActivity extends LockCloseListActivity
return true;
}
private void setSortMenuText(Menu menu) {
boolean sortByTitle = false;
@Override
public void onSortSelected(SortNodeEnum sortNodeEnum, boolean groupsBefore) {
// Toggle setting
String sortKey = getString(R.string.sort_node_key);
String groupsBeforeKey = getString(R.string.sort_group_before_key);
Editor editor = prefs.edit();
editor.putString(sortKey, sortNodeEnum.name());
editor.putBoolean(groupsBeforeKey, groupsBefore);
EditorCompat.apply(editor);
// Will be null if onPrepareOptionsMenu is called before onCreate
if (prefs != null) {
sortByTitle = prefs.getBoolean(getString(R.string.sort_key), getResources().getBoolean(R.bool.sort_default));
}
int resId;
if ( sortByTitle ) {
resId = R.string.sort_creation_time;
} else {
resId = R.string.sort_title;
}
menu.findItem(R.id.menu_sort).setTitle(resId);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if ( ! super.onPrepareOptionsMenu(menu) ) {
return false;
}
setSortMenuText(menu);
return true;
}
// Tell the adapter to refresh it's list
mAdapter.notifyChangeSort(sortNodeEnum, groupsBefore);
mAdapter.rebuildList(mCurrentGroup);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch ( item.getItemId() ) {
case R.id.menu_sort:
toggleSort();
SortDialogFragment sortDialogFragment =
SortDialogFragment.getInstance(
PrefsUtil.getListSort(this),
PrefsUtil.getGroupsBeforeSort(this));
sortDialogFragment.show(getSupportFragmentManager(), "sortDialog");
return true;
default:
@@ -182,22 +178,6 @@ public abstract class ListNodesActivity extends LockCloseListActivity
return super.onOptionsItemSelected(item);
}
}
private void toggleSort() {
// Toggle setting
String sortKey = getString(R.string.sort_key);
boolean sortByName = prefs.getBoolean(sortKey, getResources().getBoolean(R.bool.sort_default));
Editor editor = prefs.edit();
editor.putBoolean(sortKey, !sortByName);
EditorCompat.apply(editor);
// Refresh menu titles
ActivityCompat.invalidateOptionsMenu(this);
// Tell the adapter to refresh it's list
mAdapter.notifyChangeSort();
mAdapter.rebuildList(mCurrentGroup);
}
@Override
public void onAssignKeyDialogPositiveClick(

View File

@@ -36,6 +36,7 @@ import com.keepassdroid.app.App;
import com.keepassdroid.database.PwGroup;
import com.keepassdroid.database.PwNode;
import com.keepassdroid.settings.PrefsUtil;
import com.keepassdroid.database.SortNodeEnum;
import com.kunzisoft.keepass.R;
public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
@@ -45,7 +46,8 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
private Context context;
private LayoutInflater inflater;
private float textSize;
private boolean sortByName;
private SortNodeEnum listSort;
private boolean groupsBeforeSort;
private OnNodeClickCallback onNodeClickCallback;
private int nodePositionToUpdate;
@@ -71,17 +73,14 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
this.inflater = LayoutInflater.from(context);
this.context = context;
this.textSize = PrefsUtil.getListTextSize(context);
this.sortByName = PrefsUtil.isListSortByName(context);
this.listSort = PrefsUtil.getListSort(context);
this.groupsBeforeSort = PrefsUtil.getGroupsBeforeSort(context);
this.activateContextMenu = activateContextMenu;
this.nodePositionToUpdate = -1;
this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback<PwNode>(this) {
@Override public int compare(PwNode item1, PwNode item2) {
// Choose sort depend of preferences
if(sortByName)
return new PwNode.NodeNameComparator().compare(item1, item2);
else
return new PwNode.NodeCreationComparator().compare(item1, item2);
return listSort.getNodeComparator(groupsBeforeSort).compare(item1, item2);
}
@Override public boolean areContentsTheSame(PwNode oldItem, PwNode newItem) {
@@ -152,8 +151,9 @@ public class NodeAdapter extends RecyclerView.Adapter<BasicViewHolder> {
/**
* Notify a change sort of the list
*/
public void notifyChangeSort() {
this.sortByName = PrefsUtil.isListSortByName(context);
public void notifyChangeSort(SortNodeEnum sortNodeEnum, boolean groupsBefore) {
this.listSort = sortNodeEnum;
this.groupsBeforeSort = groupsBefore;
}
@Override

View File

@@ -21,7 +21,6 @@
package com.keepassdroid.database;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Date;
/**
@@ -86,78 +85,4 @@ public abstract class PwNode implements Serializable {
boolean isSameType(PwNode otherNode) {
return getType() != null ? getType().equals(otherNode.getType()) : otherNode.getType() == null;
}
/**
* Comparator of Node by Name, Groups first, Entries second
*/
public static class NodeNameComparator implements Comparator<PwNode> {
public int compare(PwNode object1, PwNode object2) {
if (object1.equals(object2))
return 0;
if (object1 instanceof PwGroup) {
if (object2 instanceof PwGroup) {
return new PwGroup.GroupNameComparator()
.compare((PwGroup) object1, (PwGroup) object2);
} else if (object2 instanceof PwEntry) {
return -1;
} else {
return -1;
}
} else if (object1 instanceof PwEntry) {
if(object2 instanceof PwEntry) {
return new PwEntry.EntryNameComparator()
.compare((PwEntry) object1, (PwEntry) object2);
} else if (object2 instanceof PwGroup) {
return 1;
} else {
return -1;
}
}
int nodeNameComp = object1.getDisplayTitle()
.compareToIgnoreCase(object2.getDisplayTitle());
// If same name, can be different
if (nodeNameComp == 0)
return object1.hashCode() - object2.hashCode();
return nodeNameComp;
}
}
/**
* Comparator of node by creation, Groups first, Entries second
*/
public static class NodeCreationComparator implements Comparator<PwNode> {
@Override
public int compare(PwNode object1, PwNode object2) {
if (object1.equals(object2))
return 0;
if (object1 instanceof PwGroup) {
if (object2 instanceof PwGroup) {
return new PwGroup.GroupCreationComparator()
.compare((PwGroup) object1, (PwGroup) object2);
} else if (object2 instanceof PwEntry) {
return -1;
} else {
return -1;
}
} else if (object1 instanceof PwEntry) {
if(object2 instanceof PwEntry) {
return new PwEntry.EntryCreationComparator()
.compare((PwEntry) object1, (PwEntry) object2);
} else if (object2 instanceof PwGroup) {
return 1;
} else {
return -1;
}
}
int nodeCreationComp = object1.getCreationTime()
.compareTo(object2.getCreationTime());
// If same creation, can be different
if (nodeCreationComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return nodeCreationComp;
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2018 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePass DX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.database;
import java.util.Comparator;
public enum SortNodeEnum {
DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME;
public Comparator<PwNode> getNodeComparator(boolean groupsBefore) {
switch (this) {
case DB:
return new NodeCreationComparator(groupsBefore); // TODO Sort
default:
case TITLE:
return new NodeTitleComparator(groupsBefore);
case USERNAME:
return new NodeCreationComparator(groupsBefore); // TODO Sort
case CREATION_TIME:
return new NodeCreationComparator(groupsBefore);
case LAST_MODIFY_TIME:
return new NodeCreationComparator(groupsBefore); // TODO Sort
case LAST_ACCESS_TIME:
return new NodeCreationComparator(groupsBefore); // TODO Sort
}
}
private static abstract class NodeComparator implements Comparator<PwNode> {
boolean groupsBefore;
NodeComparator() {
this.groupsBefore = true;
}
NodeComparator(boolean groupsBefore) {
this.groupsBefore = groupsBefore;
}
}
/**
* Comparator of Node by Title, Groups first, Entries second
*/
public static class NodeTitleComparator extends NodeComparator {
public NodeTitleComparator() {
super();
}
public NodeTitleComparator(boolean groupsBefore) {
super(groupsBefore);
}
public int compare(PwNode object1, PwNode object2) {
if (object1.equals(object2))
return 0;
if (object1 instanceof PwGroup) {
if (object2 instanceof PwGroup) {
return new PwGroup.GroupNameComparator()
.compare((PwGroup) object1, (PwGroup) object2);
} else if (object2 instanceof PwEntry) {
if(groupsBefore)
return -1;
else
return 1;
} else {
return -1;
}
} else if (object1 instanceof PwEntry) {
if(object2 instanceof PwEntry) {
return new PwEntry.EntryNameComparator()
.compare((PwEntry) object1, (PwEntry) object2);
} else if (object2 instanceof PwGroup) {
if(groupsBefore)
return 1;
else
return -1;
} else {
return -1;
}
}
int nodeNameComp = object1.getDisplayTitle()
.compareToIgnoreCase(object2.getDisplayTitle());
// If same name, can be different
if (nodeNameComp == 0)
return object1.hashCode() - object2.hashCode();
return nodeNameComp;
}
}
/**
* Comparator of node by creation, Groups first, Entries second
*/
public static class NodeCreationComparator extends NodeComparator {
public NodeCreationComparator() {
super();
}
public NodeCreationComparator(boolean groupsBefore) {
super(groupsBefore);
}
@Override
public int compare(PwNode object1, PwNode object2) {
if (object1.equals(object2))
return 0;
if (object1 instanceof PwGroup) {
if (object2 instanceof PwGroup) {
return new PwGroup.GroupCreationComparator()
.compare((PwGroup) object1, (PwGroup) object2);
} else if (object2 instanceof PwEntry) {
if(groupsBefore)
return -1;
else
return 1;
} else {
return -1;
}
} else if (object1 instanceof PwEntry) {
if(object2 instanceof PwEntry) {
return new PwEntry.EntryCreationComparator()
.compare((PwEntry) object1, (PwEntry) object2);
} else if (object2 instanceof PwGroup) {
if(groupsBefore)
return 1;
else
return -1;
} else {
return -1;
}
}
int nodeCreationComp = object1.getCreationTime()
.compareTo(object2.getCreationTime());
// If same creation, can be different
if (nodeCreationComp == 0) {
return object1.hashCode() - object2.hashCode();
}
return nodeCreationComp;
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2018 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* KeePass DX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.fragments;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.RadioGroup;
import com.keepassdroid.database.SortNodeEnum;
import com.kunzisoft.keepass.R;
public class SortDialogFragment extends DialogFragment {
private static final String SORT_NODE_ENUM_BUNDLE_KEY = "SORT_NODE_ENUM_BUNDLE_KEY";
private static final String SORT_GROUPS_BEFORE_BUNDLE_KEY = "SORT_GROUPS_BEFORE_BUNDLE_KEY";
private SortSelectionListener mListener;
private SortNodeEnum sortNodeEnum;
private @IdRes
int mCheckedId;
private boolean mGroupsBefore;
public static SortDialogFragment getInstance(SortNodeEnum sortNodeEnum, boolean groupsBefore) {
Bundle bundle = new Bundle();
bundle.putString(SORT_NODE_ENUM_BUNDLE_KEY, sortNodeEnum.name());
bundle.putBoolean(SORT_GROUPS_BEFORE_BUNDLE_KEY, groupsBefore);
SortDialogFragment fragment = new SortDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (SortSelectionListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + SortSelectionListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
sortNodeEnum = SortNodeEnum.TITLE;
mGroupsBefore = true;
if (getArguments() != null
&& getArguments().containsKey(SORT_NODE_ENUM_BUNDLE_KEY)
&& getArguments().containsKey(SORT_GROUPS_BEFORE_BUNDLE_KEY)) {
sortNodeEnum = SortNodeEnum.valueOf(getArguments().getString(SORT_NODE_ENUM_BUNDLE_KEY));
mGroupsBefore = getArguments().getBoolean(SORT_GROUPS_BEFORE_BUNDLE_KEY);
}
mCheckedId = retrieveViewFromEnum(sortNodeEnum);
View rootView = inflater.inflate(R.layout.sort_selection, null);
builder.setTitle(R.string.sort_menu);
builder.setView(rootView)
// Add action buttons
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
mListener.onSortSelected(sortNodeEnum, mGroupsBefore);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
CompoundButton groupsBeforeView = (CompoundButton) rootView.findViewById(R.id.sort_selection_groups_before);
// Check if groups before
groupsBeforeView.setChecked(mGroupsBefore);
groupsBeforeView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mGroupsBefore = isChecked;
}
});
RadioGroup sortSelectionRadioGroupView = (RadioGroup) rootView.findViewById(R.id.sort_selection_radio_group);
// Check value by default
sortSelectionRadioGroupView.check(mCheckedId);
sortSelectionRadioGroupView.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
sortNodeEnum = retrieveSortEnumFromViewId(checkedId);
}
});
return builder.create();
}
private @IdRes
int retrieveViewFromEnum(SortNodeEnum sortNodeEnum) {
switch (sortNodeEnum) {
case DB:
return R.id.sort_selection_db;
default:
case TITLE:
return R.id.sort_selection_title;
case USERNAME:
return R.id.sort_selection_username;
case CREATION_TIME:
return R.id.sort_selection_creation_time;
case LAST_MODIFY_TIME:
return R.id.sort_selection_last_modify_time;
case LAST_ACCESS_TIME:
return R.id.sort_selection_last_access_time;
}
}
private SortNodeEnum retrieveSortEnumFromViewId(@IdRes int checkedId) {
SortNodeEnum sortNodeEnum;
// Change enum
switch (checkedId) {
case R.id.sort_selection_db:
sortNodeEnum = SortNodeEnum.DB;
break;
default:
case R.id.sort_selection_title:
sortNodeEnum = SortNodeEnum.TITLE;
break;
case R.id.sort_selection_username:
sortNodeEnum = SortNodeEnum.USERNAME;
break;
case R.id.sort_selection_creation_time:
sortNodeEnum = SortNodeEnum.CREATION_TIME;
break;
case R.id.sort_selection_last_modify_time:
sortNodeEnum = SortNodeEnum.LAST_MODIFY_TIME;
break;
case R.id.sort_selection_last_access_time:
sortNodeEnum = SortNodeEnum.LAST_ACCESS_TIME;
break;
}
return sortNodeEnum;
}
public interface SortSelectionListener {
void onSortSelected(SortNodeEnum sortNodeEnum, boolean groupsBefore);
}
}

View File

@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.keepassdroid.database.SortNodeEnum;
import com.kunzisoft.keepass.R;
import java.util.Arrays;
@@ -89,10 +90,16 @@ public class PrefsUtil {
ctx.getResources().getBoolean(R.bool.full_file_path_enable_default));
}
public static boolean isListSortByName(Context ctx) {
public static SortNodeEnum getListSort(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.sort_key),
ctx.getResources().getBoolean(R.bool.sort_default));
return SortNodeEnum.valueOf(prefs.getString(ctx.getString(R.string.sort_node_key),
SortNodeEnum.TITLE.name()));
}
public static boolean getGroupsBeforeSort(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
return prefs.getBoolean(ctx.getString(R.string.sort_group_before_key),
ctx.getResources().getBoolean(R.bool.sort_group_before_default));
}
public static boolean isPasswordMask(Context ctx) {

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/default_margin">
<RadioGroup
android:id="@+id/sort_selection_radio_group"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioButton android:id="@+id/sort_selection_db"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sort_db"
android:visibility="gone"/>
<RadioButton android:id="@+id/sort_selection_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sort_title"/>
<RadioButton android:id="@+id/sort_selection_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sort_username"
android:visibility="gone"/>
<RadioButton android:id="@+id/sort_selection_creation_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sort_creation_time"/>
<RadioButton android:id="@+id/sort_selection_last_modify_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sort_last_modify_time"
android:visibility="gone"/>
<RadioButton android:id="@+id/sort_selection_last_access_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sort_last_access_time"
android:visibility="gone"/>
</RadioGroup>
<CheckBox android:id="@+id/sort_selection_groups_before"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/sort_groups_before"/>
</LinearLayout>

View File

@@ -21,6 +21,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_sort"
android:icon="@drawable/ic_sort_white_24dp"
android:title="@string/sort_name"
android:title="@string/sort_menu"
app:showAsAction="ifRoom" />
</menu>

View File

@@ -180,10 +180,15 @@
<string name="space">Espace</string>
<string name="search_label">Rechercher</string>
<string name="show_password">Afficher le mot de passe</string>
<string name="sort_name">Tri par nom</string>
<string name="sort_menu">Trier</string>
<string name="sort_groups_before">Groupes avant</string>
<string name="sort_db">Tri par défaut (base de données)</string>
<string name="sort_title">Tri par Titre</string>
<string name="sort_name">Tri par nom</string>
<string name="sort_username">Tri par Nom d\'utilisateur</string>
<string name="sort_creation_time">Tri par Date de Création</string>
<string name="sort_last_modify_time">Tri par Date de Dernière Modification</string>
<string name="sort_last_access_time">Tri par Date de Dernier Accès</string>
<string name="special">Spécial</string>
<string name="search_hint">Nom/description</string>
<string name="search_results">Résultats</string>

View File

@@ -48,7 +48,8 @@
<string name="list_size_key" translatable="false">list_size</string>
<string name="show_beta_warning" translatable="false">show_beta_warning</string>
<string name="show_read_only_warning" translatable="false">show_read_only_warning</string>
<string name="sort_key" translatable="false">sort_key</string>
<string name="sort_node_key" translatable="false">sort_node_key</string>
<string name="sort_group_before_key" translatable="false">sort_group_before_key</string>
<string name="timeout_key" translatable="false">timeout_key</string>
<string name="saf_key" translatable="false">storage_access_framework_key</string>
<integer name="roundsFix_default" translatable="false">100000</integer>
@@ -62,7 +63,6 @@
<bool name="maskpass_default" translatable="false">true</bool>
<bool name="keyfile_default" translatable="false">true</bool>
<bool name="sort_default" translatable="false">true</bool>
<bool name="omitbackup_default" translatable="false">true</bool>
<bool name="recentfile_default" translatable="false">true</bool>
<bool name="saf_default" translatable="false">false</bool>
@@ -70,6 +70,7 @@
<bool name="lock_database_screen_off_default" translatable="false">true</bool>
<bool name="fingerprint_enable_default" translatable="true">false</bool>
<bool name="full_file_path_enable_default" translatable="true">false</bool>
<bool name="sort_group_before_default" translatable="true">true</bool>
<string name="clipboard_timeout_default" translatable="false">300000</string>
<string-array name="clipboard_timeout_values">

View File

@@ -180,10 +180,15 @@
<string name="space">Space</string>
<string name="search_label">Search</string>
<string name="show_password">Show password</string>
<string name="sort_name">Sort by Name</string>
<string name="sort_menu">Sort</string>
<string name="sort_groups_before">Groups before</string>
<string name="sort_db">DB sort order</string>
<string name="sort_title">Sort by Title</string>
<string name="sort_name">Sort by Name</string>
<string name="sort_username">Sort by Username</string>
<string name="sort_creation_time">Sort by Creation Time</string>
<string name="sort_last_modify_time">Sort by Last Modify Time</string>
<string name="sort_last_access_time">Sort by Last Access Time</string>
<string name="special">Special</string>
<string name="search_hint">Search</string>
<string name="search_results">Search results</string>