I'm a bit new to android dev. I have a preference screen on my app with a bunch of EditTextPreference and checkbox pref. I'm interested in doing validation on the input to the edittextpreference. It seems there is no way to do this using android, so I have developed a class that extends the edittextpreference.
For example, I have a text preference that only has a valid range of 0 to 1. The text preference will take in any numbers, but I need to validate this before setting.
I'm overriding the setText method, and trying to do validation there before actually setting the value. SetText only passes in the text string, how do I know what I am trying to validate? How can I get that information? In other words, I'd like to reuse my class to validate all of my text preferences, and I'd like something in the preference itself to tell me what type it is, or what it's range is.
Preference pref = findPreference("OutputFolder");
pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
// Do the validation here
}
return true;
}
});
Related
I have written a bare bones standard DialogPreference which is working fine, except that it is not saving the preference to default shared preferences when I expected it to.
1) open the app, and main activity shows value of foo from default shared preferences = 1
2) go to settings
3) click on foo setting which opens my DialogPreference and shows value = 1
4) enter value 3
5) close my DialogPreference using Ok button
***** default shared preferences foo should now be 3
6) click on foo setting which opens my DialogPreference and shows value = 1
***** so my DialogPreference didn't save the preference to default shared preferences?
7) cancel dialog
8) go back to main activity which shows value of foo from default shared preferences = 3
***** so my DialogPreference did save the preference to default shared preferences
9) go to settings
10) click on foo setting which opens my DialogPreference and shows value of 3
Why isn't the value of default shared preferences foo = 3 at step (6)?
It seems that the preference is only being saved to default shared preferences when the flow returns to the main activity from the settings list, which is counter intuitive to saving the preference in the onDialogClosed method of DialogPreference.
MyDialogPreference
public class MyDialogPreference extends DialogPreference
{
private static final String DEFAULT_VALUE = "0";
private String value = DEFAULT_VALUE;
private EditText editText;
public MyDialogPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
setDialogLayoutResource(R.layout.constrained_integer_preference);
}
#Override
public void onBindDialogView(View view)
{
super.onBindDialogView(view);
editText = (EditText) view.findViewById(R.id.edit);
editText.setText("" + value);
}
#Override
protected void onDialogClosed(boolean positiveResult)
{
if (positiveResult)
{
persistString(editText.getText().toString());
}
super.onDialogClosed(positiveResult);
}
#Override
protected Object onGetDefaultValue(TypedArray typedArray, int index)
{
return typedArray.getString(index);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
if (restorePersistedValue)
{
value = getPersistedString(DEFAULT_VALUE);
}
else
{
value = (String) defaultValue;
if (shouldPersist())
{
persistString(value);
}
}
}
}
EDIT: So it appears that the preference I am handling with my DialogPreference has no key, which is causing all the problems. But I have specified the key in the preferences.xml file for this DialogPreference. I have tried everything to force the key to be recognised but nothing is working.
Can anyone tell me how I get a DialogPreference to receive the android:key from the preferences.xml file to work?
preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<org.mycompany.myproject.MyDialogPreference
android:defaultValue="11"
android:dialogLayout="#layout/my_preference"
android:inputType="number"
android:key="MY_KEY"
android:selectAllOnFocus="true"
android:singleLine="true"
android:summary="summary"
android:title="My Preference" />
</PreferenceScreen>
You'd have to implement the OnPreferenceChangeListener and/or call to notifyChanged().
Unless you'd provide the code of that DialogPreference, it's difficult to reproduce the issue.
At some point I always feel like I'm hacking Android, and this is definitely a hack.
Initially I thought the problem I was fighting was that the framework ignores my android:key, because getKey() returns an empty string, but that can't be true because it gets the persistent value when it starts the PreferenceScreen, and saves my changed values to shared preferences when I close the DialogPreference.
So it seems the problem I am fighting is that the framework reads the preferences persistent values in to internal members, and then uses the internal members until the flow returns out of the preferences framework, without refreshing them after a DialogPreference has closed.
But I have finally found a way of getting the PreferenceScreen to refresh the preferences persistent values it holds in it's internal members. Although it's not really a refresh, it's a hack.
So what I do is basically throw away the PreferenceScreen and create a new one. I do this by adding the following code to my SettingsFragment.onCreate method directly before addPreferencesFromResource(R.xml.preferences).
SharedPreferences.OnSharedPreferenceChangeListener prefListener = (prefs, key) ->
{
setPreferenceScreen(null);
addPreferencesFromResource(R.xml.preferences);
};
PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()).registerOnSharedPreferenceChangeListener(prefListener);
This is probably bad. I have tested it, repeatedly, though not thouroghly, and have not witnessed any adverse effects so far.
So with this hack, I can now repeatedly open a DialogPreference from the PreferenceScreen, save a new value, then go back in to the DialogPreference with the previously updated value.
I don't believe my hack is the intended way of achieving this outcome, but after days of searching the source code and google for answers, I have not found anything else.
I am leaving this answer here for anyone else that faces the same problem and is brave enough to try my hack. I'll update the answer if (and probably when) I find any problems with the hack.
Better yet, if anyone else can provide a preferred solution, pointing out what I have done wrong that caused the problem, please do.
EDIT: After working for so long, that hack eventually broke, and consistently.
But while removing the hack, I approached the problem with a fresh mind, and using the fact that I determined the dialog is getting the preference key, I used this fix for the problem, which is working perfectly.
I added this line of code to the start of onBindDialogView
value = getSharedPreferences().getString(getKey(), "-1");
Which makes the calls to onGetDefaultValue and onSetInitialValue redundant, but they just don't work as intended, at least not for me.
EDIT:
omg, I hate this!
I did not notice that during an earlier refactor the line of code that updates the DialogPreference internal value in onDialogClosed was removed.
It's usually something simple, and with everything else I was checking, I missed that small change.
I only just noticed it during a code review on the repo, and now I feel silly. So no additional code was required in the end.
Greetings my fellow developers,
i am working on an android project in there is a need to store the user name in shared preferences. and do a bit of validation when user tries to update his/her name in the app settings, i am using EditTextPreference for this preference, and i am implementing onPreferenceChangeListner to get updated value and do some validation on the updated value
here is my listner
#Override
public boolean onPreferenceChange(Preference preference, Object object) {
String value = object.toString().trim();
preference.setSummary(value);
if (value.isEmpty()) { // this block works as expected
preference.getEditor().clear().apply();
Log.i("pref_old", preference.getExtras().getString("key", "-- old value cleared --")); // this always shows old value cleared - as expected
return false;
}
preference.getEditor().putString("key", value).apply(); // this doesn't seems to be updating the value
Log.i("pref_new", preference.getExtras().getString("key", "-- new value not created --")); // this always show "new value not created", but it should show updated value
return true;
}
in this listner what i am trying to accomplish is to trim what user has entered, after that if string becomes empty remove the preference values so and update preferences
if not then update trimmed value in the preferences,
but problem is that after user enters the values, after trimming it value is not updated in shared preference. (in second part of the function)
it would seem that we need to always return false, if we are changing preference value in onPreferencechange and update preference value manually
i still dont understand why after comiting values in preference when i try to log the new value it wont show me updated value, but in other activity where the same value is being called i can go there and confirm that either value has changed or not
but solution to my problem was always return false and update preference value manually.
When the user updates a shared preference through a subclass of PreferenceActivity I check whether or not the new value is valid at the given time. If not the value should be changed back in the onSharedPreferenceChanged method.
This works so far. I set an OnSharedPreferenceChangedListener, the method gets called. The user-set value will be overwritten and the new value will be used in the app, however when I open this specific preferences value (in this case a ListPreference) again the wrong list item will be selected (the one the user selected, not the one set in the Listener). I tried overwriting the value with both:
mPrefs.edit().putString("answers", value.toString()).commit();
mPrefs.edit().putString("answers", value.toString()).apply();
Are there additional steps I need to take to update the ListPreference? After restarting the PreferenceActivity the value will be displayed correctly.
try to use Override method SharedPreferenceChanged
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key.equals(KEY)) {
Preference ServicePref = findPreference(key);
// Set summary to be the user-description for the selected value
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
}
}
I found a way to solve this issue by setting the value manually as described here:
http://liquidlabs.ca/2011/08/25/update-preference-value-without-reloading-preferenceactivity/
I want to open some preferences (which are made by extending DialogPreference) on first app startup. Also, these preferences are used as usual preferences.
Is there a way of accomplishing this?
EDIT:
I have my custom preference, made like this:
public class CitySelectPreference extends DialogPreference {
// Some code here
}
And as the solution I want it to be shown from the code, without the need of user getting to preference screen.
Just do this :
CitySelectPreference preference = (CitySelectPreference) findPreference("city_pref_key")
//You have to set a key to yout PreferenceScreen
PreferenceScreen screen = (PreferenceScreen) findPreference("screen_pref_key");
//Retrieve the index of the preference in preferenceScreen
int index = preference.getOrder();
//Perform a click
screen.onItemClick(null, null, index, 0);
Is it possible to use a preference screen as a simple interface to read and write values to a database?
Basically, I like the way the preference screen looks and operates, but preferences aren't a suitable way to store all the data I have.
I know how to get it to display correctly, but I'm unsure on how to access the values represented on the screen, and how to keep it from writing a preference file.
Is this even a good idea?
Thanks.
Just to follow this up for anyone that is interested. I got it working by using a Preference.OnPreferenceChangeListener() to store the value as a int or string or whatever. For example:
et_model.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener(){
public boolean onPreferenceChange(Preference preference, Object newValue) {
String val = (String) newValue;
preference.setSummary(val);
model = val;
return true;
}
});
Then once the user presses done, I add the data to the database in the usual way with my SQLight database helper class.
When I load the values from the database, I simply use Preference.SetText(String), and Preference.SetSummary(String).
I guess it is still writing a preference file because if I don't set the preference's text it will load with whatever was set last, but I don't think this is a problem. I could also delete the preference file when I close the activity or something...
If you want a good example, just look at the source for the AlarmClock (now DeskClock) Look at SetAlarm.java and set_alarm.xml for the layout(Save and cancel keys) and alarm_prefs.xml for the actual preference layout.
I don't think that is such a hot idea, especially if you plan on having a tone of data in your database. How ever if you did want to do it, I would just extend the Preference widgets that you will use and have them interface with the database. For example, lets say you have 10 items in a table and you want to select one item (row in the database), you would override the ListPreference and fill it with the content of the applicable database row.