What to do if the TYPE of preference changed in Android Preferences? For instance if Boolean changed into ListPreference?
Really noone at Google thought about Preference Migrations?
The only sensible way for now seems to version preferences and mark for removal preferences that changed with a given version..?
Try to read key with new data type, in case of ClassCastException exception delete "old" key, and create new key with same name but new type. Something like this:
SharedPreferences prefs;
String key = "key";
prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.contains(key)) {
// key exists, so tetermine it's type
try {
prefs.edit().get<old_type_name>(key, <default_old_type_value>);
} catch (Exceprtion e) {
if (e instanceOf ClassCastException) {
prefs.edit().remove(key).apply();
}
}
}
// we are here if no key exists or key removed
prefs.edit().put<new_type_name>(key, <new_type_value>).apply();
and if needed do check if (prefs.contains(key)) ... only once on first app start.
I did something that works on raw SharedPreferences and does not need a PreferenceFragment:
introduced settings version.
preferences are stored in xml resources both with dedicated xml strings.
split code to load, migrate, set default settings on application start.
created two string arrays versions and keys - that keep track of preference changes - keys is a comma separated string - with the same index this pair keeps information on given version migration.
check the current version from stored settings and verify against versions stored in string arrays, if its current version is older (lower number) then delete keys provided in a keys string array with the same index (needs string split) and re-create them with default values.
This gives me a nice way of settings migration, based purely on strings xml resources and no code changes, also it should migrate step by step all following versions if user did not update the application frequently :-)
It is also good to mark recent migration for a user review of recent changes..
Related
Note this question is NOT about obtaining the contents of a SharedPreferences object if the keys are not known, it is about if its possible to obtain the SharedPreferences themselves if its name is not known.
Suppose there are SharedPrefreferences collections with 'file names' "something-a", "something-b", "something-c". Then is it possible to determine that there are 3 sets of SharedPreferences object available and to be able to get them when only the "something-" part of their name is known?
I guess not but hopefully there is.
I'm not going into the reasons behind the requirement except to say there is a requirement to update an app to Marshmallows permissions model and it needs to be capable of reading preferences files written by older versions of the app where the preferences name is "something-imsi". If the user has not granted the relevant permission on Marshmallow then the app does not know the imsi and thus I am trying to find out if its possible for the app to read the shared preferences when it doesn't know the full name.
You can retrieve all preferences with SharedPreferences.getAll() and iterate over this collection for string keys that match your pattern. For example:
SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, 0);
Map<String, ?> pairs = prefs.getAll();
for (Map.Entry<String, ?> entry : pairs.entrySet()) {
// Check for your string pattern here...
}
This answer assumes you have one shared preferences file with keys matching the pattern described in the question. If that's not the case, and you have multiple dynamically named shared preferences files matching the pattern, it's a harder problem. You might make some progress by iterating over the files in Context.getFilesDir()
I don't think it is possible to get the list of file names of the preferences available. Though if you know the file name and then you can get an instance of the SharedPreferences and use getAll method to get all the keys present in that set of preferences.
Here is the documentation:
https://developer.android.com/reference/android/content/SharedPreferences.html#getAll()
Example:
SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_FILE_NAME, 0);
Map<String, ?> map = sharedPreferences.getAll();
for (Map.Entry<String, ?> e : map.entrySet()) {
String key = e.getKey();
// this is the key that is present
}
What I thought was going to be the easiest solution, is proving to be the most confusing.
I am creating an app that needs to reference information from multiple "accounts"... such as name, email address, acct number, password and a few other fields... approximately 15 items. There can be as many as 15-20 of these accounts in this app depending on the user. Each one will be manually added and the information filled in and stored.
I decided to use a Preference Fragment to make entering this information simpler because everyone is familiar with how Android Preference screens work and the information being entered is pretty straightforward. Why create my own input screens when the built-in looks like it will work.
I asked on here opinions if I should store the data in the preference or an SQL database. Basic consensus, depends on if I need to search or sort, might be easier in an SQL database.... otherwise, internally in preferences.
I created the code for the SQL database and then had some confusion on how to get the data in and out of the preference code. I asked on here for some help in getting this to work. Unfortunately, that sort of got lost and was again recommended that I store in pref instead.
So, I started looking into keeping it in pref files, again.
Because there are approximately 15 fields per account, and the possibility of 15 or more accounts, having a single preference XML file with 225+ entries (name1, name2, name3... email1, email2, email3... etc) seems the wrong way to do that.
If I don't do it that way, I would assume I need to create a separate XML file for each "account" ... all with identical fields, just named differently such as R.xml.acct1, R.xml.acct2, etc... Then calling the appropriate R.xml file during the addPreferencesFromResources as well as the saving calls.
The question is, is that the correct or best way to do this? Is there something else I am missing? Or is using an SQL database the better way to handle this? Maybe I should just give up on preferences and use SQL and create my own input/edit screens?
Peter,
You can use the db if you are familiar with sqlite and all the code needed to handle it. That is not a bad option; however, it might be overkill for what you are doing.
If you want to use SharedPreferences, I would recommend creating xml files for each account and storing the data in an xml file. This will require you to create a view to capture the data and not use a preference fragment.
Here is some code that could help you get started for saving the data.
public static boolean setAccountInformation(final Context context, final String accountName, final Account account) {
SharedPreferences.Editor editor = context.getSharedPreferences(accountName, Context.MODE_PRIVATE).edit();
editor.putString("account_name", account.name);
editor.putString("account_email", account.email);
...
return editor.commit();
}
public static Account getAccountInformation(final Context context, final String accountName) {
Account account = new Account();
SharedPreferences preference = context.getSharedPreferences(accountName, Context.MODE_PRIVATE);
account.email = preference.getString("account_name", null);
account.name = preference.getString("account_email", null);
...
return account;
}
This will require you to keep track of the account (xml) file names. You could store them in a string array in another shared preference and add or remove the names when accounts are added or removed.
How do I save an integer that can be overwritten and then read it on android? I am using libgdx. I know how to do it for my desktop, but I want internal storage.
You should use libgdx Preferences.
That way it will work cross-plattform! (including Android)
Since you just want to store some Integer that should be just what you need.
See example:
//get a preferences instance
Preferences prefs = Gdx.app.getPreferences("My Preferences");
//put some Integer
prefs.putInteger("score", 99);
//persist preferences
prefs.flush();
//get Integer from preferences, 0 is the default value.
prefs.getInteger("score", 0);
There are different ways to do that:
You can write to a FileHandle. Just create a FileHandle like Gdx.files.local("myfile.txt"); and write to it using fileHandle.writeString(Integer.toString(myInt));
Note, that Internal and Classpath are read-only, so you can't write files there.
Also note, that not all types of the gdx.files. are usable for all backends. More on that here.
The second way to do that are the Preferences. The Preferences are the only way to have persistent data for HTML5 applications.
The Preferences are XML files in which you can store, read and change data.
To create Preferences for your app you just need to call:
Preferences myPref = Gdx.app.getPreferences("PreferenceName");
Note, that you should use the full name, for example com.stackoverflow.preferences, as on desktop all Preferences use the same file.
To write data you can use myPref.putInteger("name", value) for Integers, myPref.putBoolean(("name", value) for Booleans... Make sure you call myPref.flush() after all changes, to save the new data.
To get the data you can call myPref.getInteger("name, defaultValue"). The default value is returned if there is no data with the given name.
More on Preferences here.
Hope i could help.
I Am writing a code using SharedPreference to store username and password of a user but each time I entered information the older one in xml file are override by newer one what I have to to to get all my data?
SharedPreferences sp1=getSharedPreferences("myshared", 0);
sp1.edit().putString("name", name.getText().toString()).commit();
sp1.edit().putString("pass", pass.getText().toString()).commit();
sp1.edit().putString("age",age.getText().toString()).commit();
sp1.edit().putString("id",id.getText().toString()).commit();
It sounds like you're overwriting your shared preferences between activities. SharedPreferences are persistent like a file on disk, so you shouldn't ever have an issue with the values not being set, hence why you must be overwriting it.
You can get your SharedPreferences by doing
SharedPreferences sp1=getSharedPreferences("myshared", 0);
String name = sp1.getString("name", "noname");
String pass = sp1.getString("pass", "nopass");
...
You can determine if the name/pass was set by checking if they equal the default value (noname and nopass in this case, though it could easily be null).
I have released my app to the market and I'm preparing an update.
However, I wanted to refactor the key name of some shared preference but retain the current value on the user's device. Another thing is that I also want to delete the old key name (so the SharedPreferences file is not polluted with unnecessary keys.
How can I achieve this without any hassle to my users?
Create an array of all of your preference keys
Create an array of all the new preference keys
Then create and array of all of their values.
Then call SharedPreferences.clear(). This will completely remove all keys and values from the preferences.
The step through all values and place them back into the SharedPreferences by their new key.