Android CheckBoxPreference java.lang.StackOverflowError - android

I have this CheckBoxPreference in my code. I have implemented onSharedPreferenceChanged() in my code to perform some action. The problem is that when i click on the checkbox preference, the function gets called in a loop with same value. Can anyone help me with this?
Here are the relevant code snippets:
onSharedPreferenceChanged() section in preference activity:
if(key.equals(LOCATION_UPDATE_KEY)) {
boolean update = sharedPreferences.getBoolean(LOCATION_UPDATE_KEY, false);
Log.v("preferences", update + "");
editor.putBoolean(LOCATION_UPDATE_KEY, update);
editor.commit();
}
preference activity's xml section:
<PreferenceCategory
android:title="Location">
<CheckBoxPreference
android:title="Track Location"
android:defaultValue="false"
android:summary="Keep track of handset location (Consumes Battery)"
android:key="track_option" />
<ListPreference
android:title="Location Update Source"
android:summary=""
android:key="track_source"
android:defaultValue="2"
android:entries="#array/location_sources"
android:entryValues="#array/location_sources_values"
android:dependency="track_option" />
<ListPreference
android:title="Location Update Interval"
android:summary=""
android:key="track_interval"
android:defaultValue="2"
android:entries="#array/location_update_interval"
android:entryValues="#array/location_update_interval_values"
android:dependency="track_option" />
</PreferenceCategory>

simple: if you change the SharedPreference in onSharedPreferenceChanged you create a loop because you trigger yourself. The loop is actually a recursion and if you call yourself endlessly you fill up the memory (not the normal one - the "stack") until you get a stackoverflow.
a normal (somewhat useful) recursion looks like this:
public int sumAllNumbersUpTo (int number) {
if (number > 0) {
return number + sumAllNumbersUpTo(number - 1);
} else {
return 0;
}
}
int result = sumAllNumbersUpTo(3);
// result is 3 + ( 2 + ( 1 + ( 0 ) ) )
it is calling itself until some condition is met. If you remove that condition then this method will never end.

Related

PreferenceCategory.removePreference() returning false and thus not deleting preference

I have seen all the questions related to this issue on SO but none of them worked for me. I am trying to remove a preference loaded from xml file in PreferenceFragment class but it is not removing that preference.
Here is my pref_screen.xml file
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory>
<! ------------ />
<Preferencecategory>
<PreferenceCategory
android:key="device_section">
<Preference
android:key="sound"
android:icon="#drawable/ic_settings_icon"
android:title="#string/sound">
</Preference>
<Preference
android:key="storage_settings"
android:title="#string/storage_settings"
android:icon="#drawable/ic_settings_storage">
<Preference>
</PreferenceCategory>
<PreferenceCategory>
<! ------------------ />
</PreferenceCategory>
</PreferenceScreen>
Here is my PreferenceFragment class :
public static class PrefFragment extends PreferenceFragment {
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_screen);
final Preference storage = getPreferenceScreen().findPreference("storage_settings");
if(storage != null){
Log.d("SettingsActivity : ", "findPreference(storage_settings) not returning null.");
}
final PreferenceCategory device_prefCat =
(PreferenceCategory) getPreferenceScreen().findPreference("device_section");
if(device_prefCat != null){
Log.d("SettingsActivity : " , "findPreference(device_section) not returning null.");
}
if(device_prefCat.removePreference(storage)){
Log.d("SettingsActivity : ", "device_prefCat() returns true.");
}else{
Log.d("SettingsActivity : ", "device_prefCat() returns false");
}
}
I have checked log. findPreference() is not returning null but removePreference() is returning false. Please tell me what I am doing wrong.
Based on what I found out in this thread the following should work for you:
Preference storage = findPreference("storage_settings");
PreferenceCategory device_prefCat = (PreferenceCategory) findPreference("device_section");
device_prefCat.removePreference(storage)
Please note that I am not using getPreferenceScreen() here, intentionally.

Add link to Preferences?

I have preferences in an Android Live wallpaper app as below. (These are checkboxes). I want to add a link to a Facebook page to this list. Looking at Android PreferenceCategory on the net, I don't see anything like "LinkPreference" or "ButtonPreference", but then again, a link or button isn't really a preference, so maybe I'm trying to fit a square peg in a round hole. Is this possible and if so, how?
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
android:title="#string/livewallpaper_settings">
<PreferenceCategory android:title="#string/livewallpaper_settings" >
<CheckBoxPreference
android:defaultValue="true"
android:key="showred"
android:summary="Display red."
android:title="Display red" />
<CheckBoxPreference
android:defaultValue="true"
android:key="showgreen"
android:summary="Display green."
android:title="Display green" />
</PreferenceCategory>
</PreferenceScreen>
This question has been asked before:
Android Add Link to a preference activity - how?
but not answered.
[Edit]
So now have the code below. It does go to Facebook, but only after first clicking on one of the checkbox preferences.
In livewallpaper_settings.xml:
<PreferenceCategory android:title="#string/livewallpaper_settings" >
<Preference
android:key="facebook"
android:summary="#string/facebook"
android:title="#string/facebook" />
</PreferenceCategory>
LiveWallpaperSettings.java:
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
Log.d("LiveWallpaperSettings.onSharedPreferenceChanged()", "key: " + key);
final Preference mypref = (Preference) findPreference("facebook");
mypref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference arg0) {
Log.d("LiveWallpaperSettings", "mypref: " + mypref.getKey());
if (mypref.getKey().equals("facebook")) {
Log.d("LiveWallpaperSettings", "LINK TO FACEBOOK");
openWebURL("http://www.facebook.com");
return false;
}
return false;
} });
return;
}
public void openWebURL( String inURL ) {
Log.d("openWebURL", inURL);
Intent browse = new Intent( Intent.ACTION_VIEW , Uri.parse( inURL ) );
startActivity( browse );
}
How about an EditTextPreference? You can use the same attributes as an EditText in your EditTextPreference so you can restrict the input to a single line and display the correct IME for email input etc.

Adding a TextView inside a PreferenceScreen's Preference

In this preferenceScreen the user unlinks the device from his account. At the moment I just have it as Unlink device, once the user clicks it, the unlinking happens.
But I would like to add a piece text like this:
Joe Foo's Device (joefoo#gmail.com) - Unlink Device
Hoe would I do this? I also need to add the user name dynamically from settingsActivity.
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<Preference android:title="#string/pref_title_advanced_unlink" >
<TextView somehow must be in here
android:id="#id/user_name_and_email" />
<intent android:action="android.intent.action.VIEW"
android:targetPackage="com.example.tvrplayer"
android:targetClass="com.example.tvrplayer.UnlinkActivity"
android.setflags="FLAG_ACTIVITY_CLEAR_TOP"/>
</Preference>
</PreferenceScreen>
preferences.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
android:key="pref_title_advanced"
android:title="Advanced" >
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_title_advanced_link"
android:title="Link Device" />
</PreferenceCategory>
</PreferenceScreen>
PrefsActivity.java
private SharedPreferences mPreferences;
private SharedPreferences.OnSharedPreferenceChangeListener mPrefListener;
private CheckBoxPreference mCheckBoxPref;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
mCheckBoxPref = (CheckBoxPreference) getPreferenceScreen().findPreference(
"pref_title_advanced_link");
/*
* set initial summary as you desire. For example, userIdCurrent can be:
* "No Devices linked."
*/
mCheckBoxPref.setSummary(userIdCurrent);
mPrefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs,
String key) {
if (key.equals("pref_title_advanced_link")) {
/*
* set post-click summary as you desire. For example,
* userIdPost can be:
* "Joe Foo's Device (joefoo#gmail.com)".
*/
mCheckBoxPref.setSummary(userIdPost);
}
}
};
mPreferences.registerOnSharedPreferenceChangeListener(mPrefListener);
}
Preferences have a subtitle called summary. Give your preference a key, then you can use findPreference(CharSequence key) in your PreferenceFragment to get a reference to your preference object, sort of like calling findViewById to get references to Views. Then call setSummary(int) or setSummary(CharSequence) on the preference object.
Alternatively, you could do something entirely more complex by providing a custom layout for your preference objects and/or subclass Preference and implement some custom data binding. But I think the above should do what you want.

getPreferenceScreen()

I need check a value and enable or disable programatically "checkboxpreference". I using this code but getPreferenceScreen() wants a method and I don't know which method use. (I use this on android 2.1).
<CheckBoxPreference
android:enabled="true"
android:title="Now"
android:defaultValue="false"
android:key="keep" />
protected void check(){
// read values
if (values){
getPreferenceScreen().findPreference("checkbox-preference-key").setEnabled(true);
}
else {
getPreferenceScreen().findPreference("checkbox-preference-key").setEnabled(false);
Use getPreferenceManager() instead.
So your code now looks like:
getPreferenceManager().findPreference("keep").setEnabled(values);
This is the equivalent of
if (values){
getPreferenceManager().findPreference("keep").setEnabled(true);
}
else {
getPreferenceManager().findPreference("keep").setEnabled(false);
}

Android SharedPreferences How is it mean to be used?

I am struggling to restore the default values I specified in the preferences.xml, Here is my code:
Preference reset = findPreference(res.getString(R.string.DEFAULT_PREFS));
reset.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(Preferences.this);
sp.edit().clear().commit();
PreferenceManager.setDefaultValues(Preferences.this, R.layout.preferences, true);
return true;
}
});
This code is my understanding of the android's developers reference for the function setDefaultValues(context, resId, readAgain):
Parameters
context The context of the shared preferences.
resId The resource ID of the preference hierarchy XML file.
readAgain Whether to re-read the default values.
Note: this will NOT reset preferences back to their default values.
For that functionality, use getDefaultSharedPreferences(Context)
and clear it followed by a call to this method with
this parameter set to true.
Well, it does not work, the preferences values are the same after this code is executed.
Then I looked into the SharedPreferences variable sp, and it points to a system generated file in the path:
/data/data/<packagename>/shared_prefs/<packagename>_preferences.xml
which I can only assume is the same xml I provided when I created the activity.
addPreferencesFromResource(R.layout.preferences);
Also inspecting the sp variable, the hash table has all the preferences, but there is no field for default value.
EDIT:
Before I am asked to, here is an excerpt from the preferences.xml file
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<EditTextPreference
android:defaultValue="5000"
android:key="#string/MAX_MESSAGES"
android:numeric="integer"
android:summary="#string/MAX_MESSAGES_desc"
android:title="#string/MAX_MESSAGES_title" />
<EditTextPreference
android:defaultValue="10"
android:key="#string/VIEW_EDGE_ROWS"
android:numeric="integer"
android:summary="#string/VIEW_EDGE_ROWS_desc"
android:title="#string/VIEW_EDGE_ROWS_title" />
<ListPreference
android:defaultValue="0"
android:entries="#array/level_list"
android:entryValues="#array/level_values"
android:key="#string/INITIAL_ORG"
android:summary="#string/INITIAL_ORG_desc"
android:title="#string/INITIAL_ORG_title" />
<ListPreference
android:defaultValue="2"
android:entries="#array/view_list"
android:entryValues="#array/view_values"
android:key="#string/INITIAL_VIEW"
android:summary="#string/INITIAL_VIEW_desc"
android:title="#string/INITIAL_VIEW_title" />
<CheckBoxPreference
android:defaultValue="true"
android:key="#string/AUTOSCROLL"
android:summary="#string/AUTOSCROLL_desc"
android:title="#string/AUTOSCROLL_title" />
<CheckBoxPreference
android:defaultValue="true"
android:key="#string/SEND_THEN_EXIT"
android:summary="#string/SEND_THEN_EXIT_desc"
android:title="#string/SEND_THEN_EXIT_title" />
<Preference
android:key="#string/DEFAULT_PREFS"
android:summary="#string/DEFAULT_PREFS_desc"
android:title="#string/DEFAULT_PREFS_title" />
</PreferenceScreen>
This is my solution so far. After debugging into the Android source code I find out that setDefaultValues() is not reliable and does not work as intended (at least according to my expectations).
I restore the default values manually now. I have a map where I can fetch from the default values.
Here is an interesting note: Inspecting the Preference class shows it has a field called mDefaultValue which contains the default value for the preference. But this field can only be set by setDefaultValue() method, and there is no method to get it. It would have save me the need for a Map
This is the code I use now, tested and working:
Preference reset = findPreference(getResources().getString(R.string.DEFAULT_PREFS));
reset.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(Preferences.this);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
// PreferenceManager.setDefaultValues(Preferences.this, R.layout.preferences, false);
for(int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
restoreDefault(editor, getPreferenceScreen().getPreference(i));
}
editor.commit();
return true;
}
}
private void restoreDefault(Editor editor, Preference p) {
if(p instanceof PreferenceCategory) {
PreferenceCategory pCat = (PreferenceCategory) p;
for(int i = 0; i < pCat.getPreferenceCount(); i++) {
restoreDefault(editor, pCat.getPreference(i));
}
}
if(p instanceof ListPreference) {
editor.putString(p.getKey(), resMap.get(p.getKey())._default);
}
if(p instanceof EditTextPreference) {
editor.putString(p.getKey(), resMap.get(p.getKey())._default);
}
if(p instanceof CheckBoxPreference) {
editor.putBoolean(p.getKey(), resMap.get(p.getKey())._default.equals("true"));
}
}
Another note: editor.commit() updates the preferences file, but does not update the preferences screen. You have to update each preference by using the listener (OnSharedPreferenceChangeListener()).

Categories

Resources