Sweeper
Sweeper

Reputation: 271420

How to store key value pairs in SharedPreferences?

I am developing an app that generates passwords randomly. I am adding a "save" feature so that the user can save their passwords in SharedPreferences. (which is not what you are supposed to do with your passwords)

I have an activity called PasswordActivity and in that activity I display the saved passwords.

This is how I save a password: I prompt the user to enter a name/key thingy for the password so that he/she can identify it later. And then I save the key and the password in the SharedPreferences using the following method in a utility class:

public static int savePassword (Context c, String passwordKey, String passwordValue) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences (c);
    Set<String> keys = prefs.getStringSet (PASSWORD_KEYS, new HashSet<String> ());
    Set<String> values = prefs.getStringSet (PASSWORD_VALUES, new HashSet<String> ());
    SharedPreferences.Editor editor = prefs.edit ();

    boolean duplicateKey = !keys.add (passwordKey);
    boolean duplicateValue = !values.add (passwordValue);

    if (duplicateKey)
        return KEY_DUPLICATE;
    if (duplicateValue)
        return VALUE_DUPLICATE;

    editor.putStringSet (PASSWORD_KEYS, keys).
            putStringSet (PASSWORD_VALUES, values).apply ();

    return SUCCESS;
}

And I wrote another method to get the passwords and their names. The method returns a HashMap<String, String> as you might have guessed.

public static HashMap<String, String> getPasswords (Context c) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences (c);
    Set<String> keys = prefs.getStringSet (PASSWORD_KEYS, new HashSet<String> ());
    Set<String> values = prefs.getStringSet (PASSWORD_VALUES, new HashSet<String> ());

    ArrayList<String> keysList = new ArrayList<> ();
    ArrayList<String> valuesList = new ArrayList<> ();

    for (String key : keys) {
        keysList.add (key);
    }

    for (String value : values) {
        valuesList.add (value);
    }

    HashMap<String, String> map = new HashMap<> ();

    for (int i = 0 ; i < keys.size () ; i++) {
        map.put (keysList.get (i), valuesList.get (i));
    }

    return map;
}

I admit that the code is pretty messy. I first get the things in the two sets and turn them into ArrayLists and add the stuff in the array list to the map and return.

Phew! That was a lot of work!

Now when the user generates a password and saves it, the two sets contain the right things: (These are fake data)

keys:
key1

values:
value1

That's all good. But when I generate another password and save it, the two sets become messy:

keys:
key1
key2

values:
value2
value1

Now when I call the getPasswords method, it will return

{key1, value2}, {key2, value1}

which is incorrect. After looking at the docs, I found out that:

It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.

which I guess is the reason that makes the incorrect results.

So I was wondering is there an alternative way to store these passwords and their names?

Upvotes: 3

Views: 3544

Answers (2)

Moonbloom
Moonbloom

Reputation: 7918

As you're storing multiple names & passwords, i would suggest a local SQLite database. SharedPreferences are meant for single values, not sets of values.

If you can't be bothered setting up the entire SQLiteOpenHelper and all that raw SQL code, you could take a look at some ORM helpers such as SugarOrm (i currently use this, its EXTREMELY easy to set up and use)

Upvotes: 1

CommonsWare
CommonsWare

Reputation: 1006724

I would recommend a database, such as SQLCipher for Android, so the user can provide a master passphrase for the app, and all the data is stored encrypted on the device.

But, let's pretend that, in order to successfully repel an alien invasion and prevent the wholesale slaughter of countless humans, you have to implement this using SharedPreferences. After that, you will need to fly with a wise-cracking companion in a commandeered alien spacecraft and successfully upload your app into the computer systems of one of the alien mother ships (which, fortunately, happen to be running Android, as Android has remarkable market share across the galaxy).

Either:

  1. Have each key and value be separate entries in the SharedPreferences. After all, SharedPreferences is a key-value store. Use a dedicated preference file for your keys and values (so as not to collide with anything else you might want to store), then you know that all the keys are ones from the user.

  2. Marshal the HashMap into JSON or XML or something and save that as a string, unmarshalling it as needed.

Upvotes: 0

Related Questions