Adding List Preference Settings using Preference Fragment Compat

In this blog post we will see how we can add a Preference List in Settings which will display a list of radio buttons in UI which user can choose from. In Open Event Orga App, the Organizer had a choice to switch between viewing Net Sales or Gross Sales in the App’s Dashboard. We decided to use a preference list to allow the user to select between using Net or Gross Sales.

The benefit of using Preference List and not any other storage media (like SQLite) to store the information is that, Preference List stores the information as key-value pair in SharedPreferences which makes it easy to store and extract small amount of data with strong consistency guarantees and using less time. Let’s move on to the implementation.

Implementation

Firstly add the dependency in build.gradle.

implementation “com.takisoft.fix:preference-v7:27.1.0.0”

In the preferences layout file, we will use checkboxes.

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

<CheckBoxPreference
android:key=”@string/gross_sales_key”
android:title=”@string/gross_sales”
android:defaultValue=”true” />

<CheckBoxPreference
android:key=”@string/net_sales_key”
android:title=”@string/net_sales”
android:defaultValue=”false” />
</PreferenceScreen>

We will create SalesDataSettings class which extends PreferenceFragmentCompat and override onCreatePreferenceFix method. We will request PreferenceManager and set SharedPreferencesName. The manager will be used to store and retrieve key-value pairs from SharedPreferences. Using setPreferencesFromResource we will attach the layout file to the fragment.

PreferenceManager manager = getPreferenceManager();
manager.setSharedPreferencesName(Constants.FOSS_PREFS);

setPreferencesFromResource(R.xml.sales_data_display, rootKey);

We are using CheckBox Preferences and modifying their behaviour to work as a Radio Preference List because Radio reference is not provided by Android. We are initializing two checkboxes and attaching a preference listener to unset all other checkboxes which one is selected.

CheckBoxPreference netSales = (CheckBoxPreference) findPreference(NET_SALES);
CheckBoxPreference grossSales = (CheckBoxPreference) findPreference(GROSS_SALES);

Preference.OnPreferenceChangeListener listener = (preference, newValue) -> {
String key = preference.getKey();

switch (key) {
case GROSS_SALES:
netSales.setChecked(false);
break;
case NET_SALES:
grossSales.setChecked(false);
break;
default:
break;
}
return (Boolean) newValue;
};

netSales.setOnPreferenceChangeListener(listener);
grossSales.setOnPreferenceChangeListener(listener);

We can load SalesDataDisplay Fragment class when a preference button is clicked using fragment transactions as shown below.

findPreference(getString(R.string.sales_data_display_key)).setOnPreferenceClickListener(preference -> {
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, SalesDataSettings.newInstance())
.addToBackStack(null)
.commit();
return true;
});

References

  1. Shared Preferences Documentation https://developer.android.com/reference/android/content/SharedPreferences
  2. Gericop Takisoft Android-Support-Preference-V7-Fix https://github.com/Gericop/Android-Support-Preference-V7-Fix
  3. Codebase for Open Event Organizer App https://github.com/fossasia/open-event-orga-app
Continue Reading

Use PreferenceManager in place of SharedPreferences

SharedPreferences is used in android to store data in the form of a key-value pair in an application. But, sometimes we need to store data at many places in the application such as saving the login email or a particular information that remains the same for the entire use of the app. In SUSI.AI android app PrefManager.java class is made that uses the sharedpreferences in android and provides a custom wrapper that is used to store the data in the key-value pair form in the app.

To store data in sharedpreferences through the PreferenceManager class we just need to declare an instance of the PreferenceManager in the location in which we want to save the data. The SUSI.AI Android app opens up the login screen and on just viewing the welcome cards for the first time due to the incorrect implementation of the sharedpreferences, using the Preference Manager we can correctly implement the preferences in welcome activity.

Whenever we install the SUSI.AI Android app, on opening it for the first time we see welcome cards that give a basic overview of the app. So, now after swiping all the cards and on reaching the final card instead of clicking GOT IT to move to the login screen and on pressing the home button we put the app in the background. Now, what happened was when the app showed cards, it was due to the WelcomeActivity.java activity.

In the onCreate() method of this activity we check whether the WelcomeActivity is opened for the first time or not. In this case we make use of the PrefManager.java class.

if (PrefManager.getBoolean(“activity_executed”, false)) {
  Intent intent = new Intent(this, LoginActivity.class);
  startActivity(intent);
  finish();
} else {
  PrefManager.putBoolean(“activity_executed”, true);
}

This piece of code calls the getBoolean(<preferenceKey>,<preferenceDefaultValue>) of the PrefManager.java class and checks the boolean value against the preference key passed in the function.

If it is the first time when this function is called then the default value is accessed against the preference key: “activity_executed”, which is passed as false and no code within the if statement is executed and so the code in the else block is executed which puts the value true as the preference value against the key “activity_executed” and the next line is executed in the sequence after this line. In short, on opening the app for the first time the preference key “activity_executed” has a value of true against it.

When we close the app by destroying it while it is in the WelcomeActivity, the onDestroy() method is called and memory is cleared, but when we open the app again we expect to see the Welcome Cards, but instead we find that we are on the login screen, even though the GOT IT button on the final card was not pressed. This is because when we go through the onCreate() method again the if condition is true this time and it sends the user directly to the Login activity.

To, solve this problem the method that was followed was :

  1. Remove the else condition in the if condition where we check the value stored against the key “activity_executed” key.
  2. In the onCreate() method of the LoginActivity.kt file use the PrefManager class to put a boolean value equal to true against the key : “activity_executed”.So the code added in the LoginActivity.kt file’s onCreate() method was :
PrefManager.putBoolean(“activity_executed”, true)

Also, the else condition was removed in the Welcome activity. Now, with these changes in place unless we click GOT IT on the final card in the welcome activity we won’t be able to go to the Login Activity, and since we won’t be able to go to the final activity the value against the key “activity_executed” will be false. Also, once we move to the login activity we will not be able to see the cards as the if condition described above will be true and the intent will fire the user to the Login Activity forever.

Therefore, using the PrefManager class it became very easy to put values or get values stored in the sharedpreferences in android. The methods are created in the Preference manager for different types corresponding to the different return types present that can be used to store the values allowed by the SharedPreferences in Android.

Using the PrefManager class functions it was made easy in SUSI.AI android app to access the SharedPreferences and using it a flaw in the auth flow and opening of the app was resolved.

 

Resources

  1. How to launch an activity only once for the first time! – Divya Jain:  https://androidwithdivya.wordpress.com/2017/02/14/how-to-launch-an-activity-only-once-for-the-first-time/
  2. Save key-value data : ttps://developer.android.com/training/data-storage/shared-preferences
  3. PrefManager Class: https://github.com/fossasia/susi_android/blob/development/app/src/main/java/org/fossasia/susi/ai/helper/PrefManager.java
Continue Reading

Option to secure particular albums in Phimpme Android Application

In the Phimpme Android application, users can perform various operations on the albums available such as creating a zip file of the album, rename an album and many more. However, one another useful functionality that has been added to the Phimpme Android application is the option to secure particular albums. So in this post, I will be discussing the implementation of this security feature.

Step 1

Firstly, a view item for providing the option to enable security for particular albums is required to be added to the security settings layout. The two-state toggle switch widget provided by the Android framework along with a textview has been added as the required view item. A screenshot depicting the layout change is provided below.

The code snippet representing the operation is provided below.

<TextView
  android:id=“@+id/security_body_apply_folders_title”
  android:layout_width=“wrap_content”
  android:layout_height=“wrap_content”
  android:text=“@string/local_folder”
  android:textColor=“@color/md_dark_background”
  android:textSize=“@dimen/medium_text” />

<android.support.v7.widget.SwitchCompat
  android:id=“@+id/security_body_apply_folder_switch”
  android:layout_width=“wrap_content”
  android:layout_height=“wrap_content”
  android:layout_alignParentEnd=“true”
  android:layout_centerVertical=“true”
  android:layout_gravity=“center_vertical”
  android:button=“@null”
  android:hapticFeedbackEnabled=“true” />

Step 2

Now we need to keep track of the albums selected by the user to secure. This can be done by storing the selected album/albums paths in an ArrayList<String> which can be referred later when required in the process.

The required code snippet to perform the above mentioned operation is provided below.

holder.foldercheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b){
securedfol.add(a.getPath());
a.setsecured(true);
}else{
securedfol.remove(a.getPath());
a.setsecured(false);
}
}
});

Step 3

Now we need to store the selected albums preference in the SharedPreference so that the user’s security preference persists even when the user exits the application and the user doesn’t have to redo the securing operation the next time user launches the application. The ArrayList<String> object containing the path of the user choice albums are converted to JSON representation by the use of the Gson Java library and the string key denoting the JSON representation of the list is saved in the SharedPreference thereafter.

if(securedfol.size()>0){
  SharedPreferences.Editor editor = SP.getEditor();
  Gson gson = new Gson();
  String securedfolders = gson.toJson(securedfol);
  editor.putString(getString(R.string.preference_use_password_secured_local_folders), securedfolders);
  editor.commit();}

Now at the time of performing other operations on the secured folders, the list containing the secured folder paths is retrieved from SharedPreference and the choosen folder’s path is searched in the obtained list, then the user is asked to authenticate accordingly.

This is how we have implemented the functionality to secure particular albums in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

Resources

1.Android Developer Guide –
https://developer.android.com/training/data-storage/shared-preferences

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Gson Java library tutorial –
http://www.vogella.com/tutorials/JavaLibrary-Gson/article.html

Continue Reading

Creating SharedPreferences Util in Open Event Android

In the Open Event Android we have the fragment for schedule, speakers which has the option to sort the list. Schedule Fragment have the option to sort by Title, Tracks and  Start Time. Speakers Fragment has the option to sort by Name, Organization and Country. If the user preferred to sort by name then it should always sort the list by name whenever the user uses the app. For this we need to store user preference for sorting list. Another part of the app like Live feed, About fragment also needs to store event id, facebook page id/name etc.

In Android there is a SharedPreferences class to store key value pair in the App specific storage. To store data in SharedPreferences we need to create SharedPreferences Object in different activities and fragment. In this post I explain how to create SharedPreferences Util which can be used to store key value pairs from all over the App.

1. Create SharedPreferencesUtil Class

The first step is to create SharedPreferncesUtil.java file which will contain static SharedPreferences object.

public class SharedPreferencesUtil {
    ...
}

2. Create static objects

Create static SharedPreferences and SharedPreferences.Editor object in the SharedPreferncesUtil.java file.

private static SharedPreferences sharedPreferences;
private static SharedPreferences.Editor editor;

3. Initialize objects

Now after creating objects initialize them in the static block. The code inside static block is executed only once: The first time you make an object of that class or the first time you access a static member of that class.

static {
        sharedPreferences = OpenEventApp.getAppContext().getSharedPreferences(ConstantStrings.FOSS_PREFS, Context.MODE_PRIVATE);
        editor = sharedPreferences.edit();
}

 

Here make sure to use the Application context to avoid a memory leak. The getSharedPreferences() method takes two arguments name of the shared preference and mode. Here we are using Context.MODE_PRIVATE File creation mode where the created file can only be accessed by the calling application.

4. Add methods

Now create static methods to store data so that we can use these methods directly from the other activities or classes. Here I am only adding methods for integer you can add more methods for String, long, boolean etc.

public static void putInt(String key, int value) {
        editor.putInt(key, value).apply();
}

public static int getInt(String key, int defaultValue) {
        return sharedPreferences.getInt(key, defaultValue);
}

5. Use SharedPreferencesUtil class

Now we are ready to use this Util class to store key value pair in SharedPreferences.

SharedPreferencesUtil.putInt(ConstantStrings.PREF_SORT, sortType);

Here the putInt() methods take two arguments one the key and second the value. To get the stored value use getInt() method.

SharedPreferencesUtil.getInt(ConstantStrings.PREF_SORT, 0);

To know more how I solved this issue in Open Event Project visit this link.

Continue Reading
Close Menu