Android Framework. How to use and extend it

Android Framework How to use and extend it Lecture 5: Data management • How to store data persistently? ○ Files ○ User Preferences ○ Shared Preferen...
3 downloads 1 Views 192KB Size
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