Android Framework How to use and extend it
Lecture 5: Data management • How to store data persistently? ○ Files ○ User Preferences ○ Shared Preferences ○ App's DB • Show data sets through Lists
Alberto Panizzo
2
Lecture 6 • Manage user preferences in the right way • Expose data sets to other apps through a ContentProvider
Alberto Panizzo
3
PreferenceActivity Android gives a unified way to manage application's preferences through XML and the PreferenceActivity class: [3] • XML definitions resides in res/xml/preferences.xml • The custom PreferenceActivity looks like: public class PreferencesFromXml extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
}
// Load the preferences from an XML resource addPreferencesFromResource(R.xml.preferences);
} Alberto Panizzo
4
PreferenceActivity
Alberto Panizzo
5
XML Preferences definition The file res/xml/preferences.xml contains: ... Alberto Panizzo
6
XML Preferences definition The file res/xml/preferences.xml contains: Alberto Panizzo
7
XML Preferences definition ListPreference entries and values: res/values/arrays.xml: Alpha Option 01 Beta Option 02 Charlie Option 03 alpha beta charlie
Alberto Panizzo
8
XML Preferences definition We can create Sub preferences screens with:
Alberto Panizzo
9
XML Preferences definition We can create dependencies with:
Alberto Panizzo
10
Actions on preferences change Register an OnSharedPreferenceChangeListener object to the SharedPreferences reference for listening changes: private SharedPreferences mSettings; private OnSharedPreferenceChangeListener mListener = new OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences s, String key) { // Update application engine since key is changed } }; @Override protected void onCreate(Bundle savedInstanceState) { ... mSettings = PreferenceManager.getDefaultSharedPreferences(this); mSettings.registerOnSharedPreferenceChangeListener(mListener); } @Override protected void onDestroy() { super.onDestroy(); mSettings.unregisterOnSharedPreferenceChangeListener(mListener); }
Alberto Panizzo
11
Useful method to update summaries In some cases the summary element is used to prompt the user about the current preference value (ListPreference) The Preferences change listener will look like: private OnSharedPreferenceChangeListener mListener = new OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged( SharedPreferences settings, String key) { String val = mSettings.getString("list_preference", null); if (!TextUtils.isEmpty(val)) { getPreferenceScreen() .findPreference("list_preference") .setSummary(val); } } };
Alberto Panizzo
12
Lecture 6 • Manage user preferences in the right way • Expose data sets to other apps through a ContentProvider
Alberto Panizzo
13
ContentProviders • Used to expose data to other applications • Even an application's Widget is to be considered as other applications because these components executes on the Launcher process • If this is not the case, avoid content providers and use directly the chosen method to persist data
Alberto Panizzo
14
ContentProviders • Android ships with a number of default content providers which manages contents like: Audio, Video, Images, Contacts so have a look at the android.provider package before reinventing the wheel • In case we really need to expose our private data, ContentProvider is the class to extend
Alberto Panizzo
15
Reference contents: URI • Android use URI addresses to reference contents • URI are strings like: schema://authority/path_to/content_id
• A single content provider is identified by the following: content://content_provider_authority
• Where the authority is a string like: com.retis.weatherprovider
• And for convenience content_id is an integer
Alberto Panizzo
16
Implement a Content provider Must extend the ContentProvider class implementing the following methods: public class MyProvider extends ContentProvider { public boolean onCreate(); public Uri insert(Uri content, ContentValues values); public Cursor query(Uri content, String[] projection, String selection, String[] selArgs, String sort) public int update(Uri content, ContentValues values, String where, String[] whereArgs); public int delete(Uri content, String where, String[] whereArgs); }
public String getType(Uri content);
Alberto Panizzo
17
Implement a Content provider • Data can be thought as: _ID
TIME
FORECAST
1
30/11/2011-10:00
Sun shining
2
30/11/2011-10:00
Dark doom
mimeType
• Where _ID is a table column definition inherited by BaseColumns
Table: content://com.retis.weatherprovider/forecasts Row:
content://com.retis.weatherprovider/forecasts/2
Alberto Panizzo
18
Implement a Content provider Extend the class BaseColumns as a helper to describe the data contained in the Content provider public static final class WeatherForecasts implements BaseColumns { /** The content:// style URL for this table */ public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY + "/forecasts"); /** The MIME type of CONTENT_URI providing a directory of contents. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.retis.weatherinfo"; /** The MIME type of a CONTENT_URI referring a single record. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.retis.weatherinfo"; /** The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "time DESC";
}
/** Table names */ public static final String TIME = "time"; public static final String FORECAST = "forecast";
Alberto Panizzo
19
Implement a Content provider Create the DatabaseHelper like this: private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + FORECASTS_TABLE_NAME + " (" +WeatherForecasts._ID+" INTEGER PRIMARY KEY AUTOINCREMENT," + WeatherForecasts.TIME + " TEXT," + WeatherForecasts.FORECAST + " TEXT" + ");"); } ...
Alberto Panizzo
20
Implement a Content provider • Use UriMatcher objects to understand content URIs: private static final int FORECASTS = 1; private static final int FORECAST_ID = 2; private static final UriMatcher sUriMatcher; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(WeatherForecasts.AUTHORITY, "forecasts", FORECASTS); sUriMatcher.addURI(WeatherForecasts.AUTHORITY, "forecasts/#", FORECAST_ID); } public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case FORECASTS: return WeatherForecasts.CONTENT_TYPE; case FORECAST_ID: return WeatherForecasts.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI "+uri); } } 21 Alberto Panizzo
Implement a Content provider public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(FORECASTS_TABLE_NAME);
}
if (sUriMatcher.match(uri) == FORECAST_ID) { qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1)); } /* If no sort order is specified use the default */ String orderBy = TextUtils.isEmpty(sortOrder) ? WeatherForecasts.DEFAULT_SORT_ORDER : sortOrder; /* Get the database and run the query */ SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); /* Tell the cursor what uri to watch, so it knows when its source * data changes */ c.setNotificationUri(getContext().getContentResolver(), uri); return c; Alberto Panizzo
22
Implement a Content provider • The content providers declaration inside AndroidManifest.xml is done as follows:
Alberto Panizzo
23
Use a Content provider • Content providers are accessible through ContentResolver objects • We can get the default ContentResolver from an Activity as follows: ContentResolver cr = getContentResolver();
• On this object we can call every content provider's methods (query, insert, update, delete) all having the same sign
Alberto Panizzo
24
Use a Content provider • In case of queries inside an Activity you can use either the form: ContentResolver cr = getContentResolver(); Cursor c = cr.query(uri, null, null, null, null); startManagingCursor(c);
• Or the more convenient: Cursor c = managedQuery(uri, null, null, null, null);
Which returns an already managed cursor
Alberto Panizzo
25
Reference code • The Android example NotePad give a not trivial example on how to use Content providers
Alberto Panizzo
26
Lab exercise Develop an application which presents on screen the “Weather forecasts for tomorrow” • Use the contents defined here: http://retis.sssup.it/~panizzo/android/material/db-content.txt
as data to store in a private database. • Start showing the couple {Forecast, Day/Time} in list items • Get forecasts Icons from: http://retis.sssup.it/~panizzo/android/material/icons.png
And show the correct one on side of every forecast item • Let the user to choose the city to check for forecasts through a PreferenceActivity • If touched, the list item layout should “expand” showing the detailed description as well. Alberto Panizzo
27
Links of interest [1] ApiDemos/src/com/example/android/apis/app/PreferencesFromXml.java [2] http://developer.android.com/guide/topics/providers/content-providers.html
Alberto Panizzo
28