Android CheckBoxPreference: how to disable and enable other preferences on preference change - android

I have CheckBoxPreference and 2 others: one is Edit Test Pref. and another is ListBox Pref. How I can enable list box pref and disable edit text pref. when CheckBoxPreference is turned on?

As a variant, it's possible to put the "dependency" into the ListBoxPref.
<PreferenceCategory
android:key="key1"
android:title="#string/title1">
<SwitchPreference
android:key="parents"
android:summaryOff="#string/do_smthng"
android:summaryOn="#string/do_smthng"
android:switchTextOff="#string/i_am_sleeping"
android:switchTextOn="#string/i_have_free_time" />
<CheckBoxPreference
android:key="baby"
android:dependency="parents"
android:title="#string/basic_habbits"
android:summaryOff="#string/play_with_parents"
android:summaryOn="#string/play_with_parents"
android:defaultValue="true" />
</PreferenceCategory>
Basically, baby can't play with parents when they are sleeping =)

Seems it's duplicate of this question
You can override public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) and when it get called with key.equals(<Your key for setting>) do something like:
boolean isEnabled = sharedPreferences.getBoolean(key, true);
getPreferenceScreen().findPreference("list box preference key").setEnabled(isEnabled);
getPreferenceScreen().findPreference("list box preference key").setEnabled(!isEnabled);
Also, do the same in onCreate() to have Your preferences screen proper initial setup.

You can get the checkbox value. And then, to enable/disable the preferences, you can use pref.setEnabled(false); To enable, just use the same function and put the true value.

Just to simplify the answer from Sergio,
android:dependency="keyOfParent"
This makes the item dependent on the parent item, may need to be a switch.
Also adding a listener to the onSharedPreferenceChanged works, sometimes.. it sometimes doesn't work as desired ( not sure why )
Add
public class YourClass extends Whatever implements SharedPreferences.OnSharedPreferenceChangeListener
Then after OnCreate()
#Override
public void onSharedPreferenceChanged (SharedPreferences p1, String p2)
{
if (Your Arguments)
{
// getPreferenceScreen().findPreference("pref_key").setEnabled(false);
}
}

Related

DialogPreference is not saving the preference when I expect it to?

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.

Building Preference screen in code depending on another setting

I have searched here and looked at samples but haven't yet found an answer to what is essentially a simple problem. Depending on the choice made in a preceding ListPreference, I want to build a preference screen of CheckBoxPreferences dynamically in code, which is then shown when I click on a simple preference with a click listener. The list and number of check boxes will be different in each case.
This is where I have got to so far - just a simple bit of code to test the concept in the onClick listener, but how to get the check box preference screen to appear? There must be a simple explanation why it doesn't. What am I doing wrong?
Part of my xml code:
<PreferenceCategory android:title="Filters">
<PreferenceScreen android:key="FilterScreen"
android:title="Filters" android:summary="Click to change filter settings">
<ListPreference android:title="Filter type"
android:summary="Set to: Gliding"
android:key="filterType"
android:defaultValue="0"
android:entries="#array/filterTypeOptions"
android:entryValues="#array/filterTypeValues" />
<CheckBoxPreference android:title=""
android:summary="Include Aerodrome Notams"
android:defaultValue="false" android:key="filterIncludeAerodrome" />
<CheckBoxPreference android:title=""
android:summary="Delete night-time Notams"
android:defaultValue="true" android:key="filterDeleteNighttime" />
<ListPreference android:title="Select category to change"
android:summary="Set to: Airspace organisation"
android:key="filterCategory"
android:defaultValue="0"
android:entries="#array/filterCategoryOptions"
android:entryValues="#array/filterCategoryValues" />
<Preference android:title="Show filters for category"
android:summary="Click to choose subjects to delete"
android:key="filterShow" />
</PreferenceScreen>
</PreferenceCategory>
The contents of "Show filters for category" will depend on the "Filter type" and "Select category to change" settings.
This is the simple test code I have for the "Show filters" click listener (cut down just to show essentials):
public class Settings extends PreferenceActivity
implements OnSharedPreferenceChangeListener
{
------
public static final String KEY_FILTER_SHOW = "filterShow";
------
private Preference mFilterShow;
------
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.settings);
// Get a reference to the preferences
------
mFilterShow = (Preference)findPreference(KEY_FILTER_SHOW);
------
// Set the click listener for Show Filter options
mFilterShow.setOnPreferenceClickListener(new OnPreferenceClickListener()
{
public boolean onPreferenceClick(Preference pref)
{
Context ctx = pref.getContext();
PreferenceScreen screen =
pref.getPreferenceManager().createPreferenceScreen(ctx);
CheckBoxPreference cb1 = new CheckBoxPreference(ctx);
cb1.setTitle("This is cb1");
cb1.setKey("cb1_key");
cb1.setDefaultValue(false);
screen.addPreference(cb1);
return true;
}
});
OK, I have solved the problem myself, through a process of iteration! Others might find this useful.
Just create an empty PreferenceScreen in the xml:
<PreferenceScreen android:title="Show filters for category"
android:summary="Click to choose subjects to delete"
android:key="filterShow">
</PreferenceScreen>
Then in the code there is no need for the onClick listener - the contents of the screen are created in the onCreate function. Actually, since the contents of the screen need to change when the choice made in the Category list preference (see original code) changes, this needs to go in a separate function which is called both from onCreate and onSharedPreferenceChanged:
public static final String KEY_FILTER_SHOW = "filterShow";
...
private PreferenceScreen mFilterShow;
...
// In onCreate:
// Get a reference to the PreferenceScreen
mFilterShow =
(PreferenceScreen)getPreferenceScreen().findPreference(KEY_FILTER_SHOW);
// Now the code to create the contents of the screen
mFilterShow.removeAll();
CheckBoxPreference cb1 = new CheckBoxPreference(this);
cb1.setTitle("This is cb1");
cb1.setKey("cb1_key");
cb1.setDefaultValue(true);
mFilterShow.addPreference(cb1);
The above is just "proof of concept". It works exactly as you would expect. In my final version, I will create an array of CheckBoxPreferences with 'new' initially, then re-use them (changing title and default) when setting up the contents of the screen for each Category choice as it changes. The number of check boxes required may be different for each category - I will create an array for the maximum number required, then add as many as I need in each case.

android preference change diplayed preferences in same PreferenceActivity

I have two preference xml files. The first one (pref2.xml) contains 2 preferences:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="key"
android:title="title"
android:defaultValue="false"
/>
<CheckBoxPreference
android:key="key2"
android:title="title"
android:defaultValue="false"
/>
</PreferenceScreen>
and the other one (pref1.xml) contains 1 preference:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="key"
android:title="title"
android:defaultValue="false"
/>
</PreferenceScreen>
in my preference activity I am trying to change them:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref1);
this.getPreferenceScreen().removeAll();
addPreferencesFromResource(R.xml.pref2);
final ListAdapter adapter = getPreferenceScreen().getRootAdapter();
Log.d("LOG", "size of sharedPreferences"+adapter.getCount()+"(should be 2)");
actually here i get the right output and everything is displayed correctly. But I want to change the displayed preferences concerning one preference. Therefore I implemented the OnSharedPreferenceChangeListener in the preference activity:
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
super.onSharedPreferenceChanged(sharedPreferences, key);
//switch the widget type
if (key.equals("key")){
this.getPreferenceScreen().removeAll();
addPreferencesFromResource(R.xml.pref1);
final ListAdapter adapter = getPreferenceScreen().getRootAdapter();
Log.d("LOG", "size of sharedPreferences "+adapter.getCount()+" (should be 1)");
}
}
Well, the pref1 preferences are displayed correctly, but the output of
"size of sharedPreferences 2"
indicated that in the background there are still the old preferences applied. If i iterate over the listAdapter i get also the old preferences.
Any idea how I could solve this?
I found out that the listadapter is some how outdated. If I get the count of items via getPreferenceScreen().getPreferenceCount() I get the right amount. But how do I access these preferences ?
I'm fairly sure that problem lies with your having two preferences with a key of "key" even though they are on different screens. A preference is identified by its key and this needs to be unique to avoid conflicts. The listener only knows which preference is being changed from the key. I would change the keys to have unique values.
EDIT: Well, wasn't right :S
...
Okey, I finally found the solution. After changing the PreferenceScreen I need to bind manually the listView again:
getPreferenceScreen().bind((ListView)findViewById(android.R.id.list));

Leaving PreferenceActivity, calling Activity still reads old preference?

I feel I'm missing something obvious, but search took me to several different hits, all of which don't directly access my odd issue.
Have an app with a main activity and a preference activity. Add to that a 'preference' class, which simplifies reading and setting preferences. The main activity has an option menu to get to the preference activity:
Preferences class (included for relevance, same thing happens if I don't use this class to read settings).
public class Preferences
{
public static SharedPreferences getPrefs(Context context)
{
SharedPreferences retCont = PreferenceManager.getDefaultSharedPreferences(context);
return retCont;
}
/* Map Page: Show Satellite */
public static boolean getMapShowSatellite(Context context)
{
return Preferences.getPrefs(context).getBoolean(Preferences.getString(context, R.string.option_showSatellite), false);
}
public static void setMapShowSatellite(Context context, boolean newValue)
{
Editor prefsEditor = Preferences.getPrefs(context).edit();
prefsEditor.putBoolean(Preferences.getString(context, R.string.option_showSatellite), newValue);
prefsEditor.commit();
}
}
PreferencesActivity:
public class AppSettings extends PreferenceActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
ListPreference stationType = (ListPreference)this.findPreference(this.getString(R.string.option_filterStationType));
stationType.setOnPreferenceChangeListener(this.stationOrderEnable());
}
[...]
}
The last two lines hook up an event to enable/disable other preferences based on one's selection. That works, as expected.
The simple main activity, and related functions:
public class MainMapScreen extends MapActivity
{
private void launchSettings()
{
Intent prefsIntent = new Intent(this.getApplicationContext(), AppSettings.class);
this.startActivity(prefsIntent);
}
#Override
protected void onResume()
{
super.onResume();
Preferences.getMapShowSatellite(); // <-- Returns previous value.
// Re-start the MyLocation Layer from tracking.
this._mapView.requestLayout();
}
[...]
}
Okay, so what happens is, let's say we run the app. At app load, the getMapShowSatellite() returns True. Go into the PreferenceActivity, and change that option to False. Exit the PreferenceActivity by hitting the Back button. At this time, the main activity's onResume() is called. Getting the getMapShowSatellite() at this point returns the previous setting of True. Exiting and relaunching the app will then finally return the False expected.
I'm not calling .commit() manually - and don't think I need to, sicne the setting IS saving, I'm just not getting update values.
What'm I missing? :)
--Fox.
Edit 2: Small update. I thought the issue may be the static calls - so temporarily I changed over my Preferences class (above) to be a instantiated class, no more static. I also added the following code to my onResume() call in the main activity:
//Try reloading preferences?
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
String test = sp.getString(Preferences.OPTION_FILTERSTATIONTYPE, "---");
Log.e("BMMaps", test);
What is logged at this point, from leaving the PreferenceActivity, is the old setting. Manually reading the preferences file shows me that the .xml file is getting updated with the user's new setting.
Since it's not obvious, I am hooked into Google's Maps API. Because of this, I had to specify two ifferent processes - one for the Main activity (this one) and another for an activity not related to this issue. All other activities, including the PreferencesActivity have no specified android:process="" in their definition.
Edit 3:
As requested, here's the data preferences file:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="com.tsqmadness.bmmaps.filterStationType">V-?</string>
<boolean name="com.tsqmadness.bmmaps.deviceHasLocation" value="false" />
</map>
And here is the Preference storage XML file:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Map Options">
<CheckBoxPreference android:key="com.tsqmadness.bmmaps.mapShowSatellite" android:order="1" android:summary="Whether or not to show satellite imagery on the map." android:summaryOff="Standard road map will be shown." android:summaryOn="Satellite imagery will be show." android:title="Show Satellite Layer?" />
<CheckBoxPreference android:key="com.tsqmadness.bmmaps.mapShowScale" android:order="2" android:summary="Whether or not to show the distance bar on the map." android:summaryOff="The distance bar will not be shown on the map." android:summaryOn="The distance bar will be shown on the map." android:title="Show Map Scale?" />
<CheckBoxPreference android:defaultValue="false" android:key="com.tsqmadness.bmmaps.useMetric" android:order="3" android:summary="Whether to use Metric os SI values." android:summaryOff="SI units (mi/ft) will be shown." android:summaryOn="Metric units (km/m) will be shown." android:title="Use Metric?" />
<ListPreference android:dialogTitle="Station Load Delay" android:entries="#array/static_listDelayDisplay" android:entryValues="#array/static_listDelayValues" android:key="com.tsqmadness.bmmaps.mapBMDelay" android:negativeButtonText="Cancel" android:order="4" android:positiveButtonText="Save" android:summary="The delay after map panning before staions are loaded." android:title="Delay Before Loading" />
</PreferenceCategory>
<PreferenceCategory android:title="Control Station Filter">
<ListPreference android:dialogTitle="Station Type" android:entries="#array/static_listStationTypeDisplay" android:entryValues="#array/static_listStationTypeValues" android:key="com.tsqmadness.bmmaps.filterStationType" android:negativeButtonText="Cancel" android:positiveButtonText="Save" android:summary="The station type to filter on." android:title="Station Type" android:order="1" />
<ListPreference android:dialogTitle="Select Station Order" android:entries="#array/static_listStationHOrderDisplay" android:entryValues="#array/static_listStationHOrderValues" android:key="com.tsqmadness.bmmaps.filterStationOrder" android:negativeButtonText="Cancel" android:positiveButtonText="Save" android:summary="Station Order to filter by." android:title="Station Order" android:order="2" />
<ListPreference android:dialogTitle="Select Station Stability" android:entries="#array/static_listStationStabilityDisplay" android:entryValues="#array/static_listStationStabilityValues" android:key="com.tsqmadness.bmmaps.filterStationStability" android:negativeButtonText="Cancel" android:positiveButtonText="Save" android:summary="Station stability to filter by." android:title="Station Stability" android:order="3" />
<CheckBoxPreference android:key="com.tsqmadness.bmmaps.filterNonPub" android:summaryOff="Non-Publishable stations will not be shown." android:defaultValue="false" android:summaryOn="Non-Publishable stations will be shown on the map." android:order="4" android:title="Show Non-Publishable" />
</PreferenceCategory>
<Preference android:key="temp" android:title="Test" android:summary="Test Item">
<intent android:targetClass="com.tsqmadness.bmmaps.activities.MainMapScreen" android:targetPackage="com.tsqmadness.bmmaps" />
</Preference>
</PreferenceScreen>
When changing the filterStationType parameter, and hitting the back button out of PreferenceActivity changes the preferences file from the above from V-? to H-?, as it should. However, reading the value from the SharedPreferences on the main activity still gives the V-?, until app restart. Ah, also, I have a OnPreferenceChangeListnener() in the PreferenceActivity, and that is called when the value changes.
Final Edit: Apparently, it's the use of named android:process for the given activity. This is needed for Google maps API to allow two separate MapActivitys in the same app use different settings. If the PreferenceActivity is moved to the same named-process, then the code above, reading the setting in the onResume() returns the correct value.
Since everything checks out, my guess is that you have a typo, or incorrect or undefined, key in your PreferenceActivity's app_preferences.xml file for the key R.string.option_showSatellite (whatever that string is). This would result in two keys with [unbeknownst to you] different names that you think point to the same value. Really, the prefs activity is using one key and your Preference class is using the other -- resulting in two different keys and two different values.
Double check your keys. Make sure you are not also using the literal "R.string.options_showSatellite" as the key in the xml file, but rather, the actual string. If you want to use the localized version then #string/options_showSatellite would work for the key. However, keys need not be localized.
If you're curious, this can be double checked by opening the preference file that is created by the preference manager in your app's data directory in a standard text editor.
onResume you have to get a fresh reference to SharedPreferences, otherwise you're just using an object that is already in memory (and has your old values)
EDIT:
Rather than down-voting answers that you don't understand, why not ask for clarification instead?
What I mean is that you should pull your Prefs the correct way (rather than how you're doing it)...
SharedPreferences sp = getSharedPreferences(getPackageName(), MODE_PRIVATE);
And then see what happens.

Android - How to change texts in the preferences activity dynamically?

Hello fellow programmers, I have a little problem with Preferences activity.
http://developer.android.com/reference/android/preference/PreferenceActivity.html
I've got just one preference category and a listPreference:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceCategory android:title="#string/basic_settings" >
<ListPreference
android:defaultValue="70"
android:entries="#array/listArray"
android:entryValues="#array/listValues"
android:key="updates_interval"
android:persistent="true"
android:summary="#string/SOME_SUMMARY"
android:title="#string/SOME_TITLE" />
</PreferenceCategory>
I need to have the selected value (the default one or the user defined one) written in the summary of the listPreference, for example:
We will have at least 70 characters.
How can I do this from the code?
Any help is appreciated
Try like this..
Preference customPref = (Preference) findPreference("updates_interval");<-- your preferences key
customPref.setSummary("desired string");
here is a short example:
Preference etp = (Preference) findPreference("the_pref_key");
etp.setSummary("New summary");
This requires that you display your preferences either from a PreferenceActivity or from a PreferenceFragment, since findPreference() is a method of these classes. You most likely do that already.
To change the summary every time the user changes the actual preference, use a OnPreferenceChangeListener and check if the relevant key changed in the callback. After it has changed, just edit the summary like above.
You can create a subclass of ListPreference in which you set an OnPreferenceChangedListener from which you will have access to the new value, and set the text on your ListPreference. I think the setSummary() function on the ListPreference will update the text under the name of the preference. If that doesn't work you can also override getView() to implement your own custom view for the Preference on which you can set the text directly.

Categories

Resources