Adding Preference Settings using Preference Fragment Compat

It is very much likely that one needs to add preferences to their app which span the entire application and therefore can be accessed anywhere in the app without storing anything in database or making global variables. For an instance, in Open Event Organizer App we added the preferences to store the privacy policy, cookie policy etc. The user can access these items in Settings Preference which in device settings. In this blog post we will see how to add preference settings to the app by storing the data in shared preferences.

Specifications

The benefit of storing the data in shared preference and not in local storage is that the access time for the data is drastically reduced and the data persists even when the app is closed. We will use this library which is built on top of official preference-v7 library.

Firstly, we will make a preference resource layout file and add the preference for privacy policy and cookie policy in the preference screen.

<PreferenceScreen xmlns:android=”http://schemas.android.com/apk/res/android”>

<Preference
android:key=”@string/privacy_policy_key”
android:title=”@string/privacy_policy” />

<Preference
android:key=”@string/cookie_policy_key”
android:title=”@string/cookie_policy” />

</PreferenceScreen>

Make a separate preference fragment class named LegalPreferenceFragment which extends PreferenceFragmentCompat. Then we will override onCreatePreferenceFix() method.

Inside this, we will create an instance of Preference Manager and set shared preference name for it and set the preference using the layout file. This enables us to use findPreference() method to retrieve the layout preferences by their key. After, retrieving the preference we will set onClick listener to launch activity with an intent to open browser for the url passed in data bundle.

@Override
public void onCreatePreferencesFix(@Nullable Bundle bundle, String rootKey) {
PreferenceManager manager = getPreferenceManager();
manager.setSharedPreferencesName(Constants.FOSS_PREFS);

setPreferencesFromResource(R.xml.legal_preferences, rootKey);

findPreference(getString(R.string.privacy_policy_key)).setOnPreferenceClickListener(preference -> {
BrowserUtils.launchUrl(getContext(), PRIVACY_POLICY_URL);
return true;
});
findPreference(getString(R.string.cookie_policy_key)).setOnPreferenceClickListener(preference -> {
BrowserUtils.launchUrl(getContext(), COOKIE_POLICY_URL);
return true;
});
}

References

  1. Preference Fragment Compat library by Takisoft https://github.com/Gericop/Android-Support-Preference-V7-Fix
  2. Android Preference Documentation https://developer.android.com/reference/android/preference/PreferenceGroup

Implementing Country Preference in Orga App

In the Open Event Orga App, there was no option earlier to save the country preference in the shared preference. So every time the user had to select the country while creating events. Hence an option to select a country was added to the Event Settings. So any value which gets selected here acts as a default country while creation of events.

Steps

  • Add the constant key to the Constants.java class.
public static final String PREF_PAYMENT_COUNTRY = “key”;
  • Create a class CountryPreference.java which extends DialogPreference. It is in this class that all the code related to the dialog which appears in selecting the Country preference will appear. First we create a layout for the dialog box. Following is the XML file for the same.
<?xml version=“1.0” encoding=“utf-8”?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  android:layout_margin=“@dimen/spacing_small”
  android:padding=“@dimen/spacing_small”
  android:orientation=“vertical”>

  <android.support.v7.widget.AppCompatSpinner
      android:id=“@+id/country_spinner”
      android:layout_width=“match_parent”
      android:layout_height=“wrap_content”
      android:layout_marginTop=“@dimen/spacing_small” />

</LinearLayout>
  • Now we create the CountryPreference constructor where we specify the UI Of the dialog box. It would include the positive and negative button.
private int layoutResourceId = R.layout.dialog_payment_country;
private int savedIndex;

public CountryPreference(Context context, AttributeSet attrs) {
  super(context, attrs, R.attr.preferenceStyle);
  setDialogLayoutResource(R.layout.dialog_payment_country);
  setPositiveButtonText(android.R.string.ok);
  setNegativeButtonText(android.R.string.cancel);
  setDialogIcon(null);
}
  • We override the method onSetInitialValue where we set the preference of the country in the shared preference. We call the method setCountry and pass the persisted value.
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
  setCountry(restorePersistedValue ? getPersistedInt(savedIndex) : (int) defaultValue);
  super.onSetInitialValue(restorePersistedValue, defaultValue);
}

 

public void setCountry(int index) {
  savedIndex = index;
  persistInt(index);
}
  • We create a class CountryPreferenceCompat which extends PreferenceDialogFragmentCompat. It is here that we initialize the spinner and set the adapter. It is here that we override the method onDialogClosed which should happen when the dialog box is closed. Following is the code for the same.
@Override
public void onDialogClosed(boolean positiveResult) {
  if (positiveResult) {
      DialogPreference preference = getPreference();
      if (preference instanceof CountryPreference) {
          CountryPreference countryPreference = ((CountryPreference) preference);
          countryPreference.setCountry(index);
      }
  }
}
  • In the PaymentPrefsFragment the code for initialization of the dialog is added. We override the onDisplayPreferenceDialog.
@Override
public void onDisplayPreferenceDialog(Preference preference) {
  CountryPreferenceFragmentCompat dialogFragment = null;
  if (preference instanceof CountryPreference)
      dialogFragment = CountryPreferenceFragmentCompat.newInstance(Constants.PREF_PAYMENT_COUNTRY);

  if (dialogFragment != null) {
      dialogFragment.setTargetFragment(this, 1);
      dialogFragment.show(this.getFragmentManager(),
          “android.support.v7.preference” +
              “.PreferenceFragment.DIALOG”);
  } else {
      super.onDisplayPreferenceDialog(preference);
  }
}
  • Now the PaymentCountry spinner can be seen on testing.

References

  1. Building Custom Preference https://www.hidroh.com/2015/11/30/building-custom-preferences-v7/
  2. StackOverflow solution https://stackoverflow.com/questions/16577173/spinnerpreference-how-to-embed-a-spinner-in-a-preferences-screen