MyContactsproviderjava

MyContactsProvider has the following responsibilities:

1. Identify the incoming URI that looks like content://com.ai.livefolders.contacts/ contacts.

2. Make an internal call to the Android-supplied contacts content provider identified by content://contacts/people/.

3. Read every row from the cursor and map it back to a cursor like MatrixCursor with proper column names required by the live-folder framework.

4. Wrap the MatrixCursor in another cursor so that the requery on this wrapped cursor will make calls to the contacts content provider when needed.

The code for MyContactsProvider is shown in Listing 13-14. Significant items are highlighted.

Listing 13-14. MyContactsProvider Source Code public class MyContactsProvider extends ContentProvider {

public static final String AUTHORITY = "com.ai.livefolders.contacts";

//Uri that goes as input to the live-folder creation public static final Uri CONTACTS_URI = Uri.parse("content://" + AUTHORITY + "/contacts" );

//To distinguish this URI private static final int TYPE_MY_URI = 0; private static final UriMatcher URI_MATCHER; static{

URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); URI_MATCHER.addURI(AUTHORITY, "contacts", TYPE_MY_URI);

^Override public boolean onCreate() { return true;

^Override public int bulkInsert(Uri arg0, ContentValues[] values) { return 0; //nothing to insert

//Set of columns needed by a live folder //This is the live-folder contract private static final String[] CURSOR_COLUMNS = new String[] {

BaseColumns._ID,

LiveFolders.NAME,

LiveFolders.DESCRIPTION,

LiveFolders.INTENT,

LiveFolders.ICON_PACKAGE,

LiveFolders.ICON_RESOURCE

//In case there are no rows

//use this stand-in as an error message

//Notice it has the same set of columns of a live folder private static final String[] CURSOR_ERROR_COLUMNS = new String[] {

BaseColumns._ID,

LiveFolders.NAME,

LiveFolders.DESCRIPTION

//The error message row private static final Object[] ERROR_MESSAGE_ROW =

"No contacts found", //name "Check your contacts database" //description };

//The error cursor to use private static MatrixCursor sErrorCursor = new MatrixCursor(CURSOR_ERROR_COLUMNS);

static {

sErrorCursor.addRow(ERROR_MESSAGE_ROW);

//Columns to be retrieved from the contacts database private static final String[] CONTACTS_COLUMN_NAMES = new String[] {

People._ID, People.DISPLAY_NAME, People.TIMES_CONTACTED, People.STARRED

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder)

//Figure out the uri and return error if not matching int type = URI_MATCHER.match(uri);

return sErrorCursor;

MatrixCursor mc = loadNewData(this); mc.setNotificationUri(getContext().getContentResolver(),

Uri.parse("content://contacts/people/"))j MyCursor wmc = new MyCursor(mc,this);

return wmc;

catch (Throwable e) {

return sErrorCursor;

public static MatrixCursor loadNewData(ContentProvider cp) {

MatrixCursor mc = new MatrixCursor(CURSOR_COLUMNS); Cursor allContacts = null;

allContacts = cp.getContext().getContentResolver().query( People.CONTENT_URI, CONTACTS_COLUMN_NAMES, null, //row filter null,

People.DISPLAY_NAME); //order by while(allContacts.moveToNext()) {

String timesContacted = "Times contacted: "+allContacts.getInt(2);

allContacts.getLong(0), //id allContacts.getString(1), //name timesContacted, //description

Uri.parse("content://contacts/people/"

+allContacts.getLong(0)), //intent cp.getContext().getPackageName(), //package R.drawable.icon //icon

mc.addRow(rowObject);

return mc;

finally {

allContacts.close();

^Override public String getType(Uri uri) {

//indicates the MIME type for a given URI //targeted for this wrapper provider //This usually looks like // "vnd.android.cursor.dir/vnd.google.note" return People.CONTENT_TYPE;

public Uri insert(Uri uri, ContentValues initialValues) { throw new UnsupportedOperationException(

"no insert as this is just a wrapper");

^Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException( "no delete as this is just a wrapper");

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

throw new UnsupportedOperationException( "no update as this is just a wrapper");

The set of columns shown in Listing 13-15 includes the standard columns that a live folder needs.

Listing 13-15. Columns Needed to Fulfill the Live-Folder Contract private static final String[] CURSOR_COLUMNS = new String[] {

BaseColumns._ID,

LiveFolders.NAME,

LiveFolders.DESCRIPTION,

LiveFolders.INTENT,

LiveFolders.ICON_PACKAGE,

LiveFolders.ICON_RESOURCE

Most of these fields are self-explanatory, except for the INTENT item. This field points to an intent or a URI that needs to be invoked when a user clicks the item in the live folder.

Also note that the content provider executes the code in Listing 13-16 to tell the cursor that it needs to watch the data for any changes.

Listing 13-16. Registering a URI with a Cursor

MatrixCursor mc = loadNewData(this); mc.setNotificationUri(getContext().getContentResolver(),

Uri.parse("content://contacts/people/"));

It should be an interesting fact that the URI to watch is not the URI of our MyContactsProvider content provider, but the URI of the Android-supplied content provider for contacts. This is because MyContactsProvider is just a wrapper for the "real" content provider. So this cursor needs to watch the underlying content provider instead of the wrapper.

It is also important that we wrap the MatrixCursor in our own cursor, as shown in Listing 13-17.

Listing 13-17. Wrapping a Cursor

MatrixCursor mc = loadNewData(this); mc.setNotificationUri(getContext().getContentResolver(), Uri.parse("content://contacts/people/")); MyCursor wmc = new MyCursor(mc,this);

To understand why you need to wrap the cursor, you must examine how views operate to update changed content. A content provider typically tells a cursor that it needs to watch for changes by registering a URI as part of implementing the query method. This is done through cursor.setNotificationUri. The cursor then will register this URI and all its children URIs with the content provider. Then when an insert or delete happens on the content provider, the code for the insert and delete operations needs to raise an event signifying a change to the data in the rows identified by a particular URI.

This will trigger the cursor to get updated via requery, and the view will update accordingly. Unfortunately, the MatrixCursor is not geared for this requery. SOLiteCursor is geared for it, but we can't use SOLiteCursor here because we're mapping the columns to a new set of columns.

To accommodate this restriction, we have wrapped the MatrixCursor in a cursor wrapper and overridden the requery method to drop the internal MatrixCursor and create a new one with the updated data.

You will see this illustrated in the following two classes.

0 0

Responses

  • alexander
    How to use matrixcursor in contentprovider?
    7 years ago

Post a comment