Nested preferences.xml - android

Is it somehow possible to include one preferences.xml into another, like it can be done for layouts with the <include /> tag?
Let's say:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:title="#string/pref_group_title_visual">
<include
preferences_filename="xml/pref_visual"/>
</PreferenceScreen>
...

Solution here it is to inflate both preference files from PreferencesActivity. For example:
addPreferencesFromResource(R.xml.options);
addPreferencesFromResource(R.xml.additional_options);

The solution soul shows works. It can be expanded to only show preferences if you're the developer using an unsigned version of the app ;)
addPreferencesFromResource(R.xml.options);
addPreferencesFromResource(R.xml.additional_options);
if (BuildConfig.DEBUG) {
addPreferencesFromResource(R.xml.developer_options);
}
I created a blog post regarding this issue and have a complete working code example available for download.
http://androidfu.blogspot.com/2012/05/developer-debug-with-nested-preferences.html

To truly achieve the nesting effect you can use this technique to relocate the loaded preferences to a group already loaded.
PreferenceCategory notifications = (PreferenceCategory) getPreferenceScreen ().findPreference (PreferenceKey.pref_notifications.name ());
addPreferencesFromResource (R.xml.pref_notifications, notifications);
Where the enhanced addPreferencesFromResource is defined as:
private void addPreferencesFromResource (int id, PreferenceGroup newParent) {
PreferenceScreen screen = getPreferenceScreen ();
int last = screen.getPreferenceCount ();
addPreferencesFromResource (id);
while (screen.getPreferenceCount () > last) {
Preference p = screen.getPreference (last);
screen.removePreference (p); // decreases the preference count
newParent.addPreference (p);
}
}
It works for any PreferenceGroup such as PreferenceScreen and PreferenceCategory.

No, it seems to be impossible. But there's a simple workaround. You can make another PreferenceActivity that loads nested PreferenceScreen. And in the main preference.xml file you need to create a Preference object and set an Intent object for it in code (using setIntent() method). This Intent must be used to create the second PreferenceActivity.

Related

Nested preferenceScreen store its preference into the different shared preferences files

I am now creating a PreferenceFragment with nested PreferenceScreen.
The preferences_layout.xml looks like below
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="#string/preference_setting_title" >
<SwitchPreference
android:key="#string/pref_key_switch"
android:title="#string/title__preference"
android:summary="#string/summary_title_preference"
android:defaultValue="true" />
<CheckBoxPreference
android:key="#string/pref_key_auto_enable"
android:title="#string/title_auto_enable_preference"
android:summary="#string/summary_auto_enable_preference"
android:dependency="#string/pref_key_switch"
android:defaultValue="true" />
<PreferenceScreen
android:key="#string/pref_key_enabled_items"
android:title="#string/title_enabled_items"
android:summary="#string/summary_enabled_items"
android:dependency="#string/pref_key_switch" >
</PreferenceScreen>
</PreferenceScreen>
The nested PreferenceScreen perference items will be dynamically added on that PreferenceScreen onClick.
Due to need to check the items already exist before adding a new one, so i use the following ways to check:
int count = enabledPreferenceScreen.getPreferenceCount();
for (int i = 0 ; i < count ; ++i) {
//Do the check with new one
}
All works ok. But the problem is that when this setting activity is destroyed and relaunch again, enabledPreferenceScreen.getPreferenceCount() will become 0.
So i check with the app/shared_prefs/xxxx.xml, the values are saved, but the problem is that there is no nested screen hierarchy information in that store value xml.
How to solve this issue? Rebuild the PreferenceScreen from the preference_layout.xml cannot rebuild the daynamic add items preferences.
Is it possible to save the nested PreferenceScreen data into another /shared_prefs/xxx2.xml? How to achieve it?
Thanks a lot.

How can I reuse code when creating multiple preferences.xml files on Android?

I want to display different preference options in my app depending on the device SDK and screen size, but certain preferences will be displayed on all devices. I could accomplish this by creating a full preferences.xml file for each possible device, like this:
xml/preferences.xml:
<PreferenceScreen>
<!-- Preference 1 (all devices) -->
<!-- Preference 2 (all devices) -->
</PreferenceScreen>
xml-v21/preferences.xml:
<PreferenceScreen>
<!-- Preference 1 (all devices) -->
<!-- Preference 2 (all devices) -->
<!-- Preference 3 (SDK 21 only) -->
</PreferenceScreen>
But this will get unwieldy very quickly given the number of possible combinations of screen sizes and SDKs. What I'd really like to do would be to use the same basic list of preferences on all devices and dynamically mix in additional preferences that are specific to certain screen sizes and SDKs. I've gone through the Android Providing Resources guide, but it seems that using alternative resources in the manner described there would still require me to create a separate resource directory for every screen-size-and-SDK combination and would require a lot of code duplication. Is there a nice, elegant solution to this problem that I'm missing?
Have you read the Settings guide? My app also has a lot of changes dynamically, both in 1) which headers / fragments to show, and 2) which prefs each fragment contains. For both issues you can use different resource versions, as you describe, or you can implement the differences in code.
For issue #1, you can either call loadHeadersFromResource directly, and have different headers resource files, or you can have code that does something similar. For example, my PreferencesActivity uses a separate PreferenceFragment subclass for each prefs section, and makes a decision at run time about which fragments (headers) to show:
#Override
public void onBuildHeaders(List<Header> targets) {
// Build a list of PreferenceFragment class objects to show now
List<Class<? extends PreferenceFragment>> fragmentClasses = ...;
// Create a Header for each fragment to return to Android
for (Class<? extends PreferenceFragment> fragmentClass: fragmentClasses) {
try {
PreferenceFragment fragment = fragmentClass.newInstance();
Header header = new Header();
header.fragment = fragmentClass.getName();
header.titleRes = fragment.getTitleId();
targets.add(header);
this.headers = targets;
} catch (Exception e) {
}
}
}
For issue #2, you can start with preferences from a common resource file, and then add the conditional ones in code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load common prefs from an XML resource
addPreferencesFromResource(R.xml.preferences);
// Add conditional prefs in code
PreferenceScreen prefScreen = getPreferenceScreen();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Preference pref = ...; // create condition pref
prefScreen.addPreference(pref);
}
}
I actually add all prefs in code, common and conditional. However, I didn't see a way to create a PreferenceScreen from scratch, so I actually have an empty XML file that I load from resources, and then add all preferences in code. It works really well.

Custom “Select All” checkbox in ListPreference

I have implemented a custom ListPreference and managed to load a list of items along with checkboxes for each without an issue. However, I need to add a “Select All” checkbox on top in order to select all list of items. How would I achieve this with the following source I have implemented?
The layout:
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/Title_LOCATIONS">
<com.gm.settings.LocationsListPreference
android:defaultValue="null"
android:key="list_locations"
android:title="#string/LocationsListPreference_title"
android:dialogTitle="#string/LocationsListPreference_title"
android:summary="#string/LocationsListPreference_summary"
/>
The class:
public class LocationsListPreference extends ListPreference {
}
I have implemented the class by following a tutorial and it works fine. But it uses a default layout i think and if I were to add this addition checkbox, how would I achieve this?
Update:
I want to know as to how i can add the "Select All" checkbox to the layout? Or should i create a custom layout? Please provide a sample code. (Because i feel the way it is right now, i dont have the control over this checkbox)
What you could do is add a CheckBoxPreference in your PreferenceCategory and attach to it a OnPreferenceChangedListener that sets all of the values to being checked.
An example could probably look a little something like this:
<CheckBoxPreference
android:key="select_all"
android:defaultValue="false"
android:title="Select All"
/>
<com.gm.settings.LocationsListPreference
android:defaultValue="null"
android:key="list_locations"
android:title="#string/LocationsListPreference_title"
android:dialogTitle="#string/LocationsListPreference_title"
android:summary="#string/LocationsListPreference_summary"
/>
And then in your PreferenceFragment (or PreferenceActivity), you would have the following:
SharedPreferences shareprefs = getPreferenceManager().getSharedPreferences();
LocationsListPreference listPreference = getPreference("list_locations");
CheckBoxPreference selectAll = getPreference("select_all");
selectAll.setOnPreferenceChangeListener(new OnPreferenceChangeListener()
{
public boolean onPreferenceChanged(Preference preference, Object newValue)
{
//Do something with your listPreference and/or your sharedPrefs
}
}
Hope this helps, and if you get to a road block, I think this post does a slightly better job at explaining some of the concepts. Good luck!
Found a stackoverflow post which might help others if they come across this kind of implementation:
You can build your custom ListPreference layout.
Cheers!

MultiSelectListPreference not storing values?

I'm rather new to Android App developing so maybe I'm just making a simple newbie mistake, but here's my problem:
I have 2 simple Activities, MainActivity and SettingsActivity. In MainActivity I have a button which displays SettingsActivity. Within SettingsActivity I include a PreferenceFragment SettingsFragment and display a ButtonBar at the bottom of the Activity. Within the SettingsFragment I have a MultiSelectListPreference defined as follows:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
android:title="#string/title_schedule_settings">
<MultiSelectListPreference
android:key="#string/key_list_schedule"
android:title="#string/title_schedule_list"
android:dialogTitle="#string/title_schedule_list"
android:entries="#array/list_weekdays"
android:entryValues="#array/list_weekdays"
android:defaultValue="#array/empty_list"
android:persistent="true"/>
</PreferenceCategory>
</PreferenceScreen>
Now when I select that Preference it shows me the list with all the entries as defined in the array, I can select multiple entries and when I confirm the dialog the values are in fact stored in the SharedPreferences under the defined key. But if I now show the Preference again it will show me previously selected items as selected, but the values are no longer stored within the SharedPreferences, and after some fiddling around I had to realize that the values in the SharedPreferences apparently get wiped as soon as the dialog is shown.
So now my questions are: is this normal/intended behavior or is this a bug? And how can I work around this?
I already tried making my own implementation of MultiSelectListPreference and override the onPrepareDialogBuilder method like this
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder)
{
super.onPrepareDialogBuilder(builder);
Log.i("mmslp", Arrays.deepToString(PreferenceManager.getDefaultSharedPreferences(getContext()).getStringSet(getKey(), new HashSet<String>()).toArray()));
setValues(PreferenceManager.getDefaultSharedPreferences(getContext()).getStringSet(getKey(), new HashSet<String>()));
}
but the values are apparently wiped already at this point.
I spent some time yesterday on this and am now convinced that it is not intended like that, but actually broken - someone confused reference by pointer and by value. ;-)
Seems to be fixed in the more current versions of Android (since 4.1) though:
https://code.google.com/p/android/issues/detail?id=22807
The way I solved it now for the previous versions of Android is to override the setValues method in my implementation of MultiSelectListPreference and just copy the values into a new object:
#Override
public void setValues( Set<String> values ) {
//Workaround for https://code.google.com/p/android/issues/detail?id=22807
final Set<String> newValues = new HashSet<String>();
newValues.addAll( values );
super.setValues( newValues );
}

Dynamically create CheckBoxPreferences

I am currently building out a list of rows with checkboxes dynamically using content from a web service. However, this ListView will need to do pretty much what a PreferenceActivity would accomplish.
I don't know the number of rows as the content is dynamic so I can't create each CheckBoxPreference in XML. How do I go about building a PreferenceActivity that will display an unknown number rows with a CheckBoxPreference dynamically?
I think you're looking for something like this:
public class MyPreferenceActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.my_preference_activity);
//fetch the item where you wish to insert the CheckBoxPreference, in this case a PreferenceCategory with key "targetCategory"
PreferenceCategory targetCategory = (PreferenceCategory)findPreference("targetCategory");
//create one check box for each setting you need
CheckBoxPreference checkBoxPreference = new CheckBoxPreference(this);
//make sure each key is unique
checkBoxPreference.setKey("keyName");
checkBoxPreference.setChecked(true);
targetCategory.addPreference(checkBoxPreference);
}
}
Well #Jodes, actually both of you are right, but the correct way of doing this would be using a ListPreference.
I would use a entire programmatic approach, from my experience it's easier to be consistent; either create an entire XML layout via code, or via XML, but mixing the 2 can be weird and you cannot alter everything set via XML...
onCreate(){
this.setPreferenceScreen(createPreferenceHierarchy());
}
public PreferenceScreen createPreferenceHierarchy(){
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
// category 1 created programmatically
PreferenceCategory cat1 = new PreferenceCategory(this);
cat1.setTitle("title");
root.addPreference(cat1);
ListPreference list1 = new ListPreference(this);
list1.setTitle(getResources().getString(R.string.some_string_title));
list1.setSummary(getResources().getString(R.string.some_string_text));
list1.setDialogTitle(getResources().getString(R.string.some_string_pick_title));
list1.setKey("your_key");
CharSequence[] entries = calendars.getCalenders(); //or anything else that returns the right data
list1.setEntries(entries);
int length = entries.length;
CharSequence[] values = new CharSequence[length];
for (int i=0; i<length; i++){
CharSequence val = ""+i+1+"";
values[i] = val;
}
list1.setEntryValues(values);
cat1.addPreference(list1);
return root;
}//end method
However, using this approach you will run into the platform's limitations of not having a multiple select ListPreference, and you'll probably want to implement something else.
I found this solution, which works great. You'll have to read the comments to find clues about how to debug the code though...
You need a ListView for that, a PreferenceActivity. As discussed in this link, PreferenceActivity should only be used for actually saving preferences.
Instead you could either create a simple dialog with single or multiple choice options:
http://developer.android.com/guide/topics/ui/dialogs.html
Or use a ListView as in the API examples Google provides, they give a simple example:
http://hi-android.info/docs/resources/samples/ApiDemos/src/com/example/android/apis/view/List10.html
Use PreferenceFragmentCompat from Preference Compat Library
compile 'com.android.support:preference-v7:23.4.0'
Check this article for the implementation details https://medium.com/#arasthel92/dynamically-creating-preferences-on-android-ecc56e4f0789#.71ssvjses

Categories

Resources