I am working to implement a dark mode into my app. Right now I try to switch the UI mode between light and dark using a PreferenceFragment nested inside an AppCompatActivity. The App behaves like this, starting from the light theme as a default value:
If I select 'dark' in the ListPreference, the summary of the
preference changes, but the UI stays light in this and all the other activities.
If I select 'dark' a second time, the activity switches to dark theme, as well as the other activities in the backstack.
The same thing happens in reverse, when I want to switch back to the light theme.
So basically everything works, but you have to select the desired value in the ListPreference twice. The code for the ListPreference:
final Preference listPreferenceDesign = findPreference(PREF_DESIGN);
listPreferenceDesign.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (((ListPreference) preference).getValue().equals("light")) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
} else if (((ListPreference) preference).getValue().equals("dark")) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
}
return true;
}
});
What I tried so far:
Call getActivity.recreate()before the return true; statement
Call getActivity.recreate()after a short delay using a Handler
Call this.recreate() in the onResume() method of the parent Activity when a boolean changedDesignSetting was true
I am thankful for further help.
I finally get it to work using an OnSharedPreferenceChangeListener in the parent activity. Now the code in the parent activity goes like this:
getFragmentManager().beginTransaction().replace(R.id.settingsPlaceholderID, preferenceFragment).commit();
SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(PREF_DESIGN)) {
if (sharedPreferences.getString(key, "light").equals("light")) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
}
}
}
};
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
And in the PreferenceFragment I only have:
final Preference listPreferenceDesign = findPreference(PREF_DESIGN);
listPreferenceDesign.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object o) {
return true;
}
});
Related
I've implemented SwitchPreference in PreferenceFragment and it works like a champ. But when I implement setOnPreferenceChangeListener on it its state does not change on the click event. Here is my implementation. Please have a look.
track_location = (CheckBoxPreference) findPreference("location_tracking");
track_location.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return false;
}
});
if I remove the listener line, it works perfectly fine.
You have to return true inside onPreferenceChange
Im trying to accomplish such a thing:
when I check CheckBoxPreference 'A' the other preference ('B') shows below the A,
when I uncheck 'A', preference 'B' hides...
So generally speaking it should work just like dependency but not only enabling/disabling th preference B, but hiding it.
This is what i came up with:
prefA = (CheckBoxPreference)findPreference("preference_A");
prefA.setChecked(false);
prefB = findPreference("preference_B");
category.removePreference(prefB);
prefA.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean switchedOn = (Boolean)newValue;
if (switchedOn)
{
Log.d("pref_test", "prefA checked");
category.addPreference(prefB);
}
else
{
Log.d("pref_test", "prefA UNchecked");
prefB = findPreference("preference_B");
category.removePreference(prefB);
}
return switchedOn;
}
});
prefA and prefB have been defined earlier as PreferenceFragment class fields.
The problem is that it works fine only for 2 clicks and my logs say:
prefA checked
prefA UNchecked
prefA UNchecked
Like it was calling onPreferenceChangeListener twice for unchecking (obviously resulting in .removePreference(prefB) method returning null).
Any idea on solving the issue?
Would it not work doing something like this?
prefA = (CheckBoxPreference)findPreference("preference_A");
prefA.setChecked(false);
prefB = findPreference("preference_B");
category.removePreference(prefB);
prefA.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean switchedOn = (Boolean)newValue;
if (switchedOn) {
Log.d("pref_test", "prefA checked");
category.addPreference(prefB);
} else {
Log.d("pref_test", "prefA UNchecked");
prefB = findPreference("preference_B");
category.removePreference(prefB);
}
return true;
}
});
In Android 4, I have a preference value that I want to appear in the standard form E2C56DB5-DFFB-48D2-B060-D0F5A71096E0. But the user may enter the value without dashes. I would like to allow the user to enter it with any combination of whitespace or dashes, and simply have my code normalize it.
I am using the code below, and I see the log line, but it does nothing. I am guessing this is because Android has another Editor object open that overwrites my changes. Is there any other way to accomplish this?
public class UuidFragment extends PreferenceFragment {
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
this.getPreferenceScreen().findPreference("pref_uuid").setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
if (newValue.toString().length() != 0) {
String normalizedUuid=normalizeUuid(newValue.toString());
// TODO: this code runs but does nothing, I think because after committing the change, there is a higher level editor that commits the old value
// thereby undoing this change
if (!normalizedUuid.equals(newValue.toString())) {
Log.d(TAG, "Adjusting uuid from "+newValue.toString()+" to "+normalizedUuid);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(UuidFragment.this.getActivity());
SharedPreferences.Editor editor = settings.edit();
editor.putString(preference.getKey(), normalizedUuid);
editor.commit();
}
return true;
}
}
});
}
}
Try subclassing EditTextPreference and overriding setText(). In your setText() method, fix up the passed-in string before chaining to the superclass. Then, reference your EditTextPreference subclass from your preference XML.
Is there XML attribute that does the exact opposite of android:dependency?
What I would like the dependent preference to be enabled when the other is NOT checked and disabled when it IS checked.
edit: maybe the issue isn't with android:dependency maybe there is an xml attribute that I can add to make the default for that preference disabled and then android:dependency will toggle it the opposite way like i want.
edit again:
I tried setting android:enabled="false" in the preference and it disables it like i want but even with it being dependent on the other preference it didn't enable it like i had hoped
Actually found it on my own and figured I'd just post it here to help anyone that might have this same issue:
android:disableDependentsState="true"
Put that in the controlling preference.
Dmytro Zarezenko asked what if you wanted some dependencies to be enabled when the preference on which they depend is true and some to be enabled when that preference is false.
Use the method described above to set the all the dependant preferences of one type (which ever have the greater number). Then (with the class having implements OnSharedPreferenceChangeListener) have code like this in the Preference Activity and/or Preference Fragment:
#Override
public void onResume()
{
super.onResume();
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onPause()
{
super.onPause();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
if (key.equals("pref_that_they_depend-upon")
{
// Iterate over the preferences that need to be enabled or disabled,
// lets say there is just one called the_awkward_one.
Preference preference = findPreference("the_awkward_one");
// Or preference.setEnabled(! sharedPreferences.getBoolean(("pref_that_they_depend-upon", defaultValue));
preference.setEnabled(sharedPreferences.getBoolean(("pref_that_they_depend-upon", defaultValue));
}
}
This is my code sample for doing this from code and not XML.
String eitherKey = "either";
String orKey = "or";
CheckBoxPreference either = new CheckBoxPreference(this);
either.setKey(eitherKey);
either.setTitle("Either");
either.setSummary("It is either one or");
either.setDefaultValue(false);
either.setDisableDependentsState(true);
inlinePrefCat.addPreference(either);
try
{
//Crossfade Time
CheckBoxPreference or = new CheckBoxPreference(this);
or.setKey(orKey);
or.setTitle("Or");
or.setSummary("the other");
inlinePrefCat.addPreference(or);
or.setDependency(eitherKey);
}
catch (Exception e)
{
}
I need to change value of dependent preference, so i post my code below, if anyone wants to do this:
#Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if(preference.getKey().equals("key_a")) {
((CheckBoxPreference)findPreference("key_b").setChecked(false);
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
Make your PreferenceActivity implement
SharedPreferences.OnSharedPreferenceChangeListener
declare in PreferenceActivity:
SharedPreferences prefs;
initialize in onCreate:
SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs = sPrefs;
and register on shared preference change listener
prefs.registerOnSharedPreferenceChangeListener(this);
do the same as Steve said in onResume and onPause methods.
implementation of onSharedPreferenceChanged listener:
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d("SettingsActivity","onSharedPreferenceChanged LISTENER FIRED");
if (key.equals(getString(R.string.key_call))) {
//if call true
if (sharedPreferences.getBoolean(getString(R.string.key_call), false)) {
Preference preference = findPreference(getString(R.string.key_record));
preference.setEnabled(false);
} else { // if call false
Preference preference = findPreference(getString(R.string.key_record));
preference.setEnabled(true);
}
}
if (key.equals(getString(R.string.key_record))) {
//if record true
if (sharedPreferences.getBoolean(getString(R.string.key_record), false)) {
Preference preference = findPreference(getString(R.string.key_call));
preference.setEnabled(false);
} else { // if record false
Preference preference = findPreference(getString(R.string.key_call));
preference.setEnabled(true);
}
}
}
In this case, I have 2 mutually exclusive Preferences in PreferenceActivity.
Call and Record.
When both are unchecked, both can be checked, but as user checks one of them, the other becomes disabled (greyed out).
As user unchecks the checked preference, the user can check the other one.
On both of them other preferences can depend and that can be worked out with android:dependancy attribute in XML file.
My Preferences all trigger the onSharedPreferenceChanged event upon a change. It works for all preferences: Checkbox, List, custom, etc. But it won't be called if I select a ringtone from the RingtonePreference. I have this code:
<CheckBoxPreference android:title="#string/pref_notification"
android:defaultValue="true" android:summary="#string/pref_notification_summary"
android:key="prefNotification" />
<RingtonePreference android:title="#string/pref_ringtone"
android:key="prefRingtone"
android:summary="#string/pref_ringtone_summary" android:ringtoneType="all" />
<CheckBoxPreference android:title="#string/pref_vibrate"
android:defaultValue="true" android:summary="#string/pref_vibrate_summary"
android:key="prefVibrationOn" />
<ListPreference android:title="#string/pref_notification_interval"
android:summary="#string/pref_notification_interval_summary"
android:key="prefNotificationInterval" android:defaultValue="60"
android:entries="#array/prefs_interval" android:entryValues="#array/prefs_interval_values" />
And my class:
public class TimePrefsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
addPreferencesFromResource(R.layout.preferences);
Preference dbPref = (Preference) findPreference("prefDeleteDb");
dbPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference)
{
showWipeDbDialog();
return true;
}
});
}
#Override
public void onResume() {
super.onResume();
prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
toggleEnableList();
}
#Override
public void onPause() {
prefs.unregisterOnSharedPreferenceChangeListener(this);
super.onPause();
}
#Override
public void onSharedPreferenceChanged(SharedPreferences arg0, String arg1)
{
boolean enabled = toggleEnableList();
if (enabled)
{
OnBootReceiver.setAlarm(TimePrefsActivity.this);
}
else
{
OnBootReceiver.cancelAlarm(TimePrefsActivity.this);
}
}
}
All the preferences, except the RingtonePreference, reach method onSharedPreferenceChanged. Does anyone have an idea? Thanks.
I struggled with the same problem which seems to be a bug in the android system.
After debugging the code I noticed the listener is not added to our RingtonePreference listeners map, unlike other classes like ListPreference.
I opened a ticket, but for now I found a way to overcome it using OnPreferenceChangeListener.
My code sets the preference summary to the selected ringtone, you can use your logic instead.
First make your activity implement OnPreferenceChangeListener and write the onPreferenceChange method
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
updateRingtoneSummary((RingtonePreference) preference, Uri.parse((String) newValue));
return true;
}
private void updateRingtoneSummary(RingtonePreference preference, Uri ringtoneUri) {
Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
if (ringtone != null)
preference.setSummary(ringtone.getTitle(this));
else
preference.setSummary("Silent");
}
Notice that unlike onSharedPreferenceChanged, onPreferenceChange is called before the preference is updated, so you must use the newValue parameter to get the selected data instead of getting it from the preference.
Then, set the listener on OnResume:
#Override
protected void onResume() {
super.onResume();
// A patch to overcome OnSharedPreferenceChange not being called by RingtonePreference bug
RingtonePreference pref = (RingtonePreference) findPreference(getString(R.string.pref_ringtone));
pref.setOnPreferenceChangeListener(this);
}
Hope this helps.
I also thought this was a bug in the system at first, but the issue is actually more subtle. RingtonePreference launches a new activity via an intent. This means that your PreferenceActivity gets paused. And you're unregistering your listener in onPause(). If you don't do this, it'll work fine (at least it did for me).
But naturally, you can't have your handler stay registered forever. I compromised by using onStart()/onStop() instead of onResume()/onPause().
I had to manually set the OnPreferenceChangeListener myself on my SettingsFragment:
Preference notificationSoundPref = findPreference(Constants.PREFS_NOTIFICATION_SOUND);
notificationSoundPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// do what you need here
return true;
}
});