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?
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)
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:
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.
Marshal the HashMap into JSON or XML or something and save that as a string, unmarshalling it as needed.
Related
I need an advise on the best approach to complete this task in my android app:
I am working on an android app and got stuck on how to calculate the diagnosis of the patient.
The user (doctor) will perform physical tests on the patient
The user records the value of each physical test <= this data is stored in a shared pref object
i.e- when the user performs an empty can test (physical test), the user will record if the test is positive (1) or negative (0) <= Boolean value (for simplicity, I am using only 4 tests)
The application is supposed to compare the patient findings to the conditions that I want to just hard code in my app (for simplicity, I am using only 8 conditions to compare) then display the condition that the patient would most likely have
--> I am storing the physical test, performed on the patient, results in a shared preference object but do not know how to go from there to come up with the diagnosis (I am looking for a simple approach to make this work)
*** I saved the values from my shared preference object into one dimensional array as below:
// User SharedPreferences to save the value per special id
SharedPreferences sharedPref = getSharedPreferences("STData", Context.MODE_PRIVATE);
Map<String, String> map = (Map<String, String>) sharedPref.getAll();
int[] PTFindings = new int[map.size()];
int index = 0;
if(!map.isEmpty()){
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry)iterator.next();
PTFindings[index] = (int) entry.getValue();
}
}
**** How should I hard code few conditions to proceed? -> assuming the results of the physical tests of those conditions (positive (1) or negative (0)) do not change
*** I need to compare their values to the one I saved from shared preference then display the most likely diagnosis
Any suggestion would be greatly appreciated
As far as I could understando from your question, you just need a cascading condition.
if(boolean1){
if(boolean2){
//something boolean2 related
} else {
if(boolean3){
etc...
}
}
} else {
//something boolean1 related
}
I need to save two depending Strings (action and parameter) into a file or a hashtable/map or an Array, depending what is the best solution for speed and memory.
My Application iterates through a large amount of forms on a website and i want to skip if the combination (String action,String parameter) already was tested and therefore saved. I thing an Array would be too slow if I have more then thousands of different action and parameter tupels. I´m not experienced enough to chose the right method for this. I tried a hashtable but it does not work:
Hashtable<String, String> ht = new Hashtable<String, String>();
if (ht.containsKey(action) && ht.get(action).contains(parameter)) {
System.out.println("Tupel already exists");
continue;
}
else
ht.put(action, parameter);
If a action and parameter will always be a 1-to-1 mapping (an action will only ever have one parameter), then your basic premise should be fine (though I'd recommend HashMap over Hashtable as it's faster and supports null keys)
If you will have many parameters for a given action, then you want Map<String, Set<String>> - where action is the key and each action is then associated with a set of parameters.
Declare it like this:
Map<String, Set<String>> map = new HashMap<>();
Use it like this:
Set<String> parameterSet = map.get(action); // lookup the parameterSet
if ((parameterSet != null) && (parameterSet.contains(parameter)) { // if it exists and contains the key
System.out.println("Tupel already exists");
} else { // pair doesn't exist
if (parameterSet == null) { // create parameterSet if needed
parameterSet = new HashSet<String>();
map.put(action, parameterSet);
}
parameterSet.add(parameter); // and add your parameter
}
As for the rest of your code and other things that may not be working:
I'm not sure what your use of continue is for in your original code; it's hard to tell without the rest of the method.
I'm assuming the creation of your hashtable is separated from the usage - if you're recreating it each time, then you'll definitely have problems.
My code here will be simplified for readability.
Methods for getting the Set and adding a password to it:
public void addPass(String pass)
{
// Get the current list.
SharedPreferences sp = getSharedPreferences("passes", 0);
SharedPreferences.Editor editor = getSharedPreferences("passes", 0).edit();
Set<String> passes = sp.getStringSet("myStrings", new HashSet<String>());
// Add the new value.
passes.add(pass);
// Save the list.
editor.putStringSet("myStrings", passes);
editor.commit();
}
public Set<String> getPasses()
{
SharedPreferences sp = getSharedPreferences("passes", 0);
return sp.getStringSet("myStrings", new HashSet<String>());
}
Reading from the Set
Set<String> x = getPasses();
String[] passes = x.toArray(new String[0]); // convert the set to array
toast(Arrays.toString(passes)); // test what the set looks like
I converted the set to an array because it was easier to test conditions with an array for me.
Adding to the Set
EditText password1 = new EditText(this);
String p1 = password1.getText().toString();
addPass(p1.trim()); // add it to the set
toast("Account successfully created.");
Problem
When I first ran this code, I added three String values: "a", "b", "c" (in that order).
All the value were added to the Set correctly and it was confirmed with this line of code from above:
toast(Arrays.toString(passes)); // test what the set looks like
It outputted [b, c, a] after the "c" was added to the set.
The problem is that when I closed the app and reopened it, the Set only contained "a".
I could add the "b" and "c" values again, but the cycle would just continue where the Set only kept the first value added to it after the activity was recreated.
I've troubleshooted for a while and cannot fix it. I can provide more detail on how and when I'm using this code if necessary. I'm hoping I don't need to and someone can point out a problem in the code as shown.
Was searching for a solution for the same issue, a solution to this would be
// Get the current list.
SharedPreferences sp = getSharedPreferences("passes", 0);
SharedPreferences.Editor editor = getSharedPreferences("passes", 0).edit();
Set<String> passes = sp.getStringSet("myStrings", new HashSet<String>());
//Make a copy, update it and save it
Set<String> newPasses = new HashSet<String>();
newPasses.add(pass);
newPasses.addAll(passes);
editor.putStringSet("myStrings", newPasses); editor.commit();
It might be that your issue stems from using getStringSet(), have a look at the docs:
public abstract Set<String> getStringSet (String key, Set<String> defValues)
Retrieve a set of String values from the preferences.
Note that you must not modify the set instance returned by this call. The consistency of the stored data is not guaranteed if you do, nor is your ability to modify the instance at all.
Based on this doc , and this SO Post about the exact same problem as yours, it seems you should create a new copy of the returned instance of the HashSet, and you seem to have done that, at least from what code you've pasted here. Maybe there's something else that's being missed.
However, check out the answer on this post . What the guy did was:
Add the first value to SharedPreferences as a String Set.
To add subsequent values, first read the existing pref into a new String Set.
Remove the old String Set key.
Modify the new string set to add the new values you want to store.
Add the new String Set to preferences.
While that is a long and silly way to do things, it seems like the last option. So basically, store all the values you want to store, in one go (not in instalments). If you want to add new values, remove the old key, create a new key with the old + new values, then store the new key.
Hope that helps.
i am fetching a data from sqlite in android which is as follows
URL PHONE
---------------------------------
/test/img1.png 98989898
/test/img1.png 61216121
/test/img2.png 75757575
/test/img2.png 40404040
/test/img3.png 36363636
now i want to create such a map which stores the data as follows
/test/img1.png [98989898 , 61216121 ]
/test/img2.png [75757575 , 40404040 ]
/test/img3.png [36363636]
so that i can pass the whole map to the function which function eventually in background pick up the image url and send the data to the arrays listed to the phone number. so how can i transform the data that i have fetched into the key to string array style ?
I'd create a Map<String, List<String>> (aka "multi-map"). You don't have to know how many phone numbers for a given URL before you start if you use List<String>. That's not so if you choose the array route.
Map<String, List<String>> results = new HashMap<String, List<String>>();
while (rs.next()) {
String url = rs.getString(1);
String phone = rs.getString(2);
List<String> phones = (results.contains(url) ? results.get(url) : new ArrayList<String>());
phones.add(phone);
results.put(url, phones);
}
Google Collections has a multi-map that you can use out of the box, but I think you'll agree that this is sufficient.
If you want to store more items (e.g. name) you should start thinking about an object that encapsulates all of them together into one coherent thing. Java's an object-oriented language. You sound like you're guilty of thinking at too low a level. Strings, primitives, and data structures are building blocks for objects. Perhaps you need a Person here:
package model;
public class Person {
private String name;
private Map<String, List<String>> contacts;
// need constructors and other methods. This one is key
public void addPhone(String url, String phone) {
List<String> phones = (this.contacts.contains(url) ? this.contacts.get(url) : new ArrayList<String>());
phones.add(phone);
this.contacts.put(url, phones);
}
}
I'll leave the rest for you.
If you go this way, you'll need to map a result set into a Person. But you should see the idea from the code I've posted.
Here's what I'm trying to do:
- Retrieve a Map of key-value pairs from a SharedPreferences object (User#, Name)
- write those key-value pairs into an ArrayList such that I can
- use them to populate a ListView with each row containing BOTH the key and the value like so:
User 1 - Joe R.
User 2 - Frank B.
etc
UPDATE:
so after taking a good long look at the SimpleAdapter class, and talking with some wiser more knowledgable folks - I'm a lot closer than I was... but still not all the way there.
here's what I have now:
public class LogHistory extends ListActivity {
static final ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
private static final String KEY = null;
private static final String VALUE = null;
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.log_history);
SharedPreferences logPrefs = getSharedPreferences(LoginField.PREFSNAME, 0);
Map<String, ?> logMap = logPrefs.getAll();
for (Map.Entry<String, ?> e : logMap.entrySet()) {
HashMap<String, String> row = new HashMap<String, String>();
String mKey = e.getKey();
String mValue = (String) e.getValue();
row.put(KEY, mKey);
row.put(VALUE, mValue);
list.add(row);
// FOR DEBUGGING PURPOSES
makeToast(mKey);
makeToast(mValue);
}
SimpleAdapter adapter = new SimpleAdapter(
this,
list,
R.layout.list_item,
new String[] { KEY, VALUE },
new int[] {R.id.item1, R.id.item2}
);
setListAdapter(adapter);
This sort of works - but only half way... what I get as a result is a list of the VALUES in both columns...
HOWEVER the makeToast function returns the proper values for both the KEY and the VALUE - so the problem must be in the SimpleAdapter method yes??
assistance would be great - homework is due tonight! :-0
You need to search for "custom listview", "listview custom adapter" and those things. "two line listview item layout"...
See this example. There are others on Google.
Basically, you can create a ArrayList<Hashmap<String,String>>, which is your data container. You add values to that creating as many HashMap<String, String> objects as you need and using list.add(yourHashMap), where list is the ArrayList.
At the end you feed that to a SimpleAdapter (there are other methods, but this works without much trouble).
Check the docs to see how each thing works exactly.
You are nulling your index keys. Put a name into those final Strings.
This sort of works - but only half way... what I get as a result is a list of the VALUES in both columns...
HOWEVER the makeToast function returns the proper values for both the KEY and the VALUE - so the problem must be in the SimpleAdapter method yes??
As I said, no. When you do this:
row.put(KEY, mKey);
row.put(VALUE, mValue);
You are not providing a meaninful difference between KEY and VALUE, because both are null. It's something like putting all things into the same column.
Your mistake into reasoning that is because the Toast test you created yourself test only the correctness of the values, not the columns:
makeToast(mKey);
makeToast(mValue);
In that you test only the values. ;) You assume that the columns are right, and that the mistake could only be in the values, which is exactly the opposite.
First rule of dealing with computers: computers never assume LOL. ;-)