Saving User Preferences
Because an activity’s state is not saved automatically during its lifecycle, you need to save user preferences so that you can redisplay an activity in the correct state. Let’s see how to do that.
Creating a New Activity
Applications often consist of more than one activity. Let’s create a new Settings activity to enable and disable vibration and create the best possible experience for the user.
- Create a new activity called activity_settings via the Android New Activity wizard. Select BlankActivity as the template, Settings as the activity name, and activity_settings as the layout file. Type Settings as the title.
Open the activity_settings file. Change the RelativeLayout to a LinearLayout with a vertical orientation:
<LinearLayout
xmlns:android
=http://schemas.android.com/apk/res/android
xmlns:tools
=http://schemas.android.com/tools
android:layout_width
="match_parent"
android:layout_height
="match_parent"
android:orientation
="vertical"
>
- Add a new checkbox view inside the linear layout. Give it a new id of vibrate_check, and set the layout_width and layout_height to wrap_content. Set a resource text to the value @string/vibrate_checkbox:
<CheckBox
android:id
="@+id/vibrate_checkbox"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:text
="@string/vibrate_checkbox"
/>
Add the new resource string vibrate_checkbox to the strings.xml file:
<string
name
="vibrate_checkbox"
>Vibrate
</string>
Showing a New Activity
To show a new activity, you first need to create an intent. Intents, in their simplest form, are a description of an activity that you want to occur. (You can also start activities in other applications, as covered later in the book.)
Next, you’ll create a new intent to display the Settings activity.
Open the activity_timer layout. To launch the new activity, add a new button to the linear layout. Give the button an ID of settings_button, and a click handler to call the method clickedSettings when the button is pressed:
<Button
android:id
="@+id/settings_button"
android:layout_width
="wrap_content"
android:layout_height
="wrap_content"
android:layout_weight
="1"
android:text
="@string/settings_button"
android:onClick
="clickedSettings"
/>
Add the new resource string for the Settings button:
<string
name
="settings_button"
>
Settings</string>
In the TimerActivity.java file, add a new clickedSettings method:
public
void
clickedSettings(View view) { Log.d(CLASS_NAME
,"clickedSettings"
); }- Debug the application, and check that the clickedSettings call is logged in the LogCat view. If an RTE occurs, double-check that the onClick contains exactly the same method name as the new method just added.
In the clickedSettings method, create a new Intent. Then pass the application context and the class property of the SettingsActivity. Run Quick fix to add the import statement for the Intent class:
Intent settingsIntent =
new
Intent(getApplicationContext(), SettingsActivity.class
);Display the new activity by calling startActivity, passing the intent you just created:
startActivity(settingsIntent);
Run the application again, and click the Settings button. The setting activity (displaying a checkbox) with a single checkbox will replace the timer activity, as shown in Figure 4.9.
Figure 4.9 The new Settings activity
Saving an Application’s State
Application state can be stored in many ways, either as static properties stored globally in the application or through the use of the singleton pattern. This pattern is designed to control object creation, limiting the number of objects to one. Because there is only ever one instance of the application class, you can use that to act as a singleton.
Here’s how to create a class to save and retrieve the application settings.
Create a new Java class called Settings. Add a private static (of type String) CLASS_NAME, and assign the class name in the class constructor:
public
class
Settings {private
static
StringCLASS_NAME
;public
Settings() {CLASS_NAME
= getClass().getName(); } }Create a private property to store whether or not the vibrate setting is turned on:
protected
boolean
vibrateOn
;Create a method to return this property. Run Quick fix to import the Log class:
public
boolean
isVibrateOn() { Log.d(CLASS_NAME
,"isVibrateOn"
);return
vibrateOn; }Create a method to set the value of the property:
public
void
setVibrate(boolean
vibrate) { Log.d(CLASS_NAME
,"setVibrate"
);vibrateOn
= vibrate; }Create a new class called OnYourBike that extends Application. Add a settings property of type Settings to this class:
public
class
OnYourBikeextends
Application {protected
Settingssettings
; }Add a method named getSettings that creates an instance of Settings if it hasn’t already been created, and return the settings property:
public
Settings getSettings() {if
(settings
==null
) {settings
=new
Settings(); }return
settings
; }Add a method named setSettings that changes the settings property to the settings value passed in:
public
void
setSettings(Settings settings) {this
.settings
= settings; }Change the Android manifest file so that the application uses this class as its application by setting the android:name attribute to ".OnYourBike":
<application
android:name
=".OnYourBike"
android:allowBackup
="true"
android:icon
="@drawable/ic_launcher"
android:label
="@string/app_name"
android:theme
="@style/AppTheme"
>
Open SettingActivity.java, and add a vibrate checkbox property. Run Quick fix to import the CheckBox class:
private
CheckBoxvibrate
;In the onCreate method, after the call to setContentView, obtain access to the checkbox by calling findViewById:
vibrate
= (CheckBox) findViewById(R.id.vibrate_checkbox
);Obtain the settings by calling the getSettings method just created:
Settings settings = ((OnYourBike)getApplication()).getSettings();
Just after that, set the state of the checkbox according to the setting:
vibrate
.setChecked(settings.isVibrateOn());Override the onStop method to save the settings:
@Override
public
void
onStop() {super
.onStop(); Settings settings = ((OnYourBike) getApplication()).getSettings(); settings.setVibrate(vibrate
.isChecked()); }- Run the application, click the Settings button, change the settings checkbox, and press the back button. Go back into the setting activity again by clicking the Settings button. The vibrate checkbox should still be ticked.
Notice that there was no need to add a handler to the checkbox for the state to be saved when the activity was stopped. Depending on how the activity is used in your application, you may want to save the setting right away rather than wait until the activity is stopped.
Using Shared Preferences
The settings class you created saves the application’s state only while it is running. If the application is stopped and restarted, it won’t remember the previous state. To fix that, you need to use shared preferences to save the application’s state. Shared preferences allow you to save key value pairs on a device.
You can save the vibration setting—whether it’s turned on or off—as a preference:
Open Settings.java, and add a private static string called VIBRATE:
private static
StringVIBRATE
="vibrate"
;In the isVibrateOn method, obtain an instance of shared preferences by calling activity.getPreferences:
SharedPreferences preferences = activity.getPreferences(Activity.
MODE_PRIVATE
);Run Quick fix to import the SharedPreferences and Activity classes.
Check whether the VIBRATE keys exist, and, if they do, set vibrateOn to be the saved value:
if
(preferences.contains(VIBRATE
)) {vibrateOn
= preferences.getBoolean(VIBRATE
,false
); }Change the isVibrateOn method to take a single parameter of type Activity:
public
boolean
isVibrateOn(Activity activity)In the setVibrate method, after the existing code, save the vibrate property by getting access to the shared preferences, creating an editor, saving the property by calling putBoolean, and committing the changes by calling apply:
SharedPreferences preferences = activity.getPreferences(Activity.
MODE_PRIVATE
); Editor editor = preferences.edit(); editor.putBoolean(VIBRATE
, vibrate); editor.apply();Run Quick fix to import the Editor class.
Change the setVibrate method to take an additional parameter of type Activity:
public
void
setVibrate(Activity activity,boolean
vibrate)Open SettingsActivity.java, and fix the two errors by passing this to the isVibrateOn and setVibrate methods:
vibrate
.setChecked(settings.isVibrateOn(this
)); settings.setVibrate(this
,vibrate
.isChecked());- Run the application, click Settings, check the vibrate checkbox, and press the back button. Click Menu, and select all apps. Select your application, and click force stop. Run the application again, and click the Settings button. The vibrate checkbox should still be checked.