- •Contents
- •Acknowledgments
- •Preface
- •What Makes Android Special?
- •Who Should Read This Book?
- •Online Resources
- •Fast-Forward >>
- •Introducing Android
- •Quick Start
- •Installing the Tools
- •Creating Your First Program
- •Running on the Emulator
- •Running on a Real Phone
- •Key Concepts
- •The Big Picture
- •Building Blocks
- •Using Resources
- •Safe and Secure
- •Android Basics
- •Designing the User Interface
- •Introducing the Sudoku Example
- •Designing by Declaration
- •Creating the Opening Screen
- •Using Alternate Resources
- •Implementing an About Box
- •Applying a Theme
- •Adding a Menu
- •Adding Settings
- •Starting a New Game
- •Debugging
- •Exiting the Game
- •Exploring 2D Graphics
- •Learning the Basics
- •Adding Graphics to Sudoku
- •Handling Input
- •The Rest of the Story
- •Making More Improvements
- •Multimedia
- •Playing Audio
- •Playing Video
- •Adding Sounds to Sudoku
- •Storing Local Data
- •Adding Options to Sudoku
- •Continuing an Old Game
- •Remembering the Current Position
- •Accessing the Internal File System
- •Accessing SD Cards
- •Beyond the Basics
- •The Connected World
- •Browsing by Intent
- •Web with a View
- •From JavaScript to Java and Back
- •Using Web Services
- •Locating and Sensing
- •Location, Location, Location
- •Set Sensors to Maximum
- •Putting SQL to Work
- •Introducing SQLite
- •Hello, Database
- •Data Binding
- •Using a ContentProvider
- •Implementing a ContentProvider
- •3D Graphics in OpenGL
- •Understanding 3D Graphics
- •Introducing OpenGL
- •Building an OpenGL Program
- •Rendering the Scene
- •Building a Model
- •Lights, Camera, ...
- •Action!
- •Applying Texture
- •Peekaboo
- •Measuring Smoothness
- •Fast-Forward >>
- •The Next Generation
- •Multi-Touch
- •Building the Touch Example
- •Understanding Touch Events
- •Setting Up for Image Transformation
- •Implementing the Drag Gesture
- •Implementing the Pinch Zoom Gesture
- •Hello, Widget
- •Live Wallpaper
- •Write Once, Test Everywhere
- •Gentlemen, Start Your Emulators
- •Building for Multiple Versions
- •Evolving with Android APIs
- •Bug on Parade
- •All Screens Great and Small
- •Installing on the SD Card
- •Publishing to the Android Market
- •Preparing
- •Signing
- •Publishing
- •Updating
- •Closing Thoughts
- •Appendixes
- •Bibliography
- •Index
Chapter 6
Storing Local Data
So far, we’ve concentrated on writing applications that don’t need to keep data around when they exit. They start up, run, and go away, leaving no trace that they were ever there. However, most real programs need persistent state, whether it’s a simple font size setting, an embarrassing photo from your last office party, or next week’s meal plan. Whatever it is, Android lets you permanently store it on your mobile device for later use and protects it from accidental or malicious access by other programs.
Your application can store data using several different techniques depending on the size of the data, its structure, its lifetime, and whether it will be shared with other programs. In this chapter, we’ll take a look at three simple methods to keep local data: the preferences API, instance state bundles, and flash memory files. In Chapter 9, Putting SQL to Work, on page 178, we’ll delve into more advanced techniques using the built-in SQLite database engine.
6.1Adding Options to Sudoku
In Section 3.7, Adding a Menu, on page 64, we used the onCreateOptionsMenu( ) method to add a menu containing one item to the main Sudoku screen. When the user presses the Menu key and selects the Settings...
item, the code starts the Prefs activity, which lets the user change the options for the game. Because Prefs extends PreferenceActivity, the values for the settings are stored in the program’s preferences area, but originally we didn’t do anything with them. Now we’re going to implement them.
ADDING OPTIONS TO SUDOKU 121
Sudoku Trivia
There are 6,670,903,752,021,072,936,960 possible classic Sudoku solution grids. If you eliminate duplicates that are just rotations, reflections, or relabelings of each other, you’re left with “only” 5,472,730,538 solutions.
First let’s modify the Prefs class to add a couple of getter methods that retrieve the current values of our two options. Here’s the new definition:
Download Sudokuv4/src/org/example/sudoku/Prefs.java
package org.example.sudoku;
import android.content.Context; import android.os.Bundle;
import android.preference.PreferenceActivity; import android.preference.PreferenceManager;
public class Prefs extends PreferenceActivity { // Option names and default values
private static final String OPT_MUSIC = "music"; private static final boolean OPT_MUSIC_DEF = true; private static final String OPT_HINTS = "hints"; private static final boolean OPT_HINTS_DEF = true;
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings);
}
/** Get the current value of the music option */ public static boolean getMusic(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(OPT_MUSIC, OPT_MUSIC_DEF);
}
/** Get the current value of the hints option */ public static boolean getHints(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(OPT_HINTS, OPT_HINTS_DEF);
}
}
Be careful that the option keys (music and hints) match the keys used in res/xml/settings.xml.
CONTINUING AN OLD GAME 122
Music.play( ) has to be modified to check for the music preference:
Download Sudokuv4/src/org/example/sudoku/Music.java
public static void play(Context context, int resource) { stop(context);
// Start music only if not disabled in preferences if (Prefs.getMusic(context)) {
mp = MediaPlayer.create(context, resource); mp.setLooping(true);
mp.start();
}
}
And PuzzleView.onDraw( ) also needs to be modified to check for the hints preference:
Download Sudokuv4/src/org/example/sudoku/PuzzleView.java
if (Prefs.getHints(getContext())) { // Draw the hints...
}
If getHints( ) returns true, we draw the highlights for the hints, as shown in Figure 4.6, on page 92. Otherwise, we just skip that part.
Next I’ll show you how to use the preferences API to store things other than just options.
6.2Continuing an Old Game
At any time the player can decide to quit playing our Sudoku game and go do something else. Maybe their boss walked in, or they got a phone call or a notification of an important appointment. Whatever the reason, we want to allow the player to come back later and continue where they left off.
First we need to save the current state of the puzzle somewhere. The preferences API can be used for more than just options; it can store any small stand-alone bits of information that go with your program. In this case, the state of the puzzle can be saved as a string of eightyone characters, one for each tile.
In the Game class, we’ll start by defining a couple of constants—one for the puzzle data key and one for a flag to tell us to continue the previous game rather than start a new one.
CONTINUING AN OLD GAME 123
Download Sudokuv4/src/org/example/sudoku/Game.java
private static final String PREF_PUZZLE = "puzzle" ; protected static final int DIFFICULTY_CONTINUE = -1;
Next we need to save the current puzzle whenever the game is paused. See Section 2.2, It’s Alive!, on page 35 for a description of onPause( ) and the other life-cycle methods.
Download Sudokuv4/src/org/example/sudoku/Game.java
@Override
protected void onPause() { super.onPause(); Log.d(TAG, "onPause"); Music.stop(this);
// Save the current puzzle getPreferences(MODE_PRIVATE).edit().putString(PREF_PUZZLE,
toPuzzleString(puzzle)).commit();
}
Now the puzzle is saved, but how do we read the saved data? Remember that when the game is started, the getPuzzle( ) method is called, and the difficulty level is passed in. We’ll use that for continuing as well.
Download Sudokuv4/src/org/example/sudoku/Game.java
private int[] getPuzzle(int diff) { String puz;
switch (diff) {
case DIFFICULTY_CONTINUE:
puz = getPreferences(MODE_PRIVATE).getString(PREF_PUZZLE, easyPuzzle);
break; // ...
}
return fromPuzzleString(puz);
}
All we need to do is add a check for DIFFICULTY_CONTINUE. If that is set, then instead of starting with a fresh puzzle, we read the one we stuffed into the preferences.
Next, we need to make the Continue button on the main screen (see Figure 3.4, on page 53) actually do something. Here is where we set that up.