diff options
| author | Joe Malin <jmalin@google.com> | 2010-06-07 16:26:25 -0700 |
|---|---|---|
| committer | Joe Malin <jmalin@google.com> | 2010-08-09 15:30:01 -0700 |
| commit | 4124e0a1f07e4e54c37b0cfbb1b7438806ff02a6 (patch) | |
| tree | 044d57a235802491100ea89ace9c8f56c9471e30 /samples/NotePad/src/com/example/android/notepad/NotePadProvider.java | |
| parent | 4779ab6f9aa4d6b691f051e069ffac31475f850a (diff) | |
Revised Note Pad sample, new test app for Note Pad
Change-Id: Ia41a33d935ead704c1de439a0cfb0a55806cfe12
Diffstat (limited to 'samples/NotePad/src/com/example/android/notepad/NotePadProvider.java')
| -rw-r--r-- | samples/NotePad/src/com/example/android/notepad/NotePadProvider.java | 618 |
1 files changed, 428 insertions, 190 deletions
diff --git a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java index d79be2427..c091d87bc 100644 --- a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java +++ b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java @@ -16,15 +16,11 @@ package com.example.android.notepad; -import com.example.android.notepad.NotePad.Notes; - import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; -import android.content.ContentProvider.PipeDataWriter; -import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.database.Cursor; import android.database.SQLException; @@ -32,340 +28,582 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; import android.provider.LiveFolders; import android.text.TextUtils; import android.util.Log; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.util.HashMap; /** - * Provides access to a database of notes. Each note has a title, the note - * itself, a creation date and a modified data. + * This is a content provider for a table of notes. Each note has a title, the note + * itself, a creation date and a modified data. The underlying data source is an SQLite + * database. + * + * Notes: + * SQLite database method signatures usually include a "where" argument and "whereArgs" argument. + * "where" can either specify a full "where" clause in the format used by SQL "WHERE", or a + * "where" clause in which the column names are followed by " = ?", or a combination of the two. + * If the " = ?" form is present, then whereArgs must be non-null. It is a String array that + * contains one element for each "?" present. In order, the "?" symbols are replace by elements + * in whereArgs. This feature helps create selection criteria without needing to repeatedly + * create a where clause from concatenations. In the comments, "where" is always annotated as + * the where clause columns, and "whereArgs" as the where clause values, although "where" can + * contain values and "whereArgs" can be null. + * */ -public class NotePadProvider extends ContentProvider implements PipeDataWriter<Cursor> { +public class NotePadProvider extends ContentProvider { + // Used for debugging and logging private static final String TAG = "NotePadProvider"; + /** + * The database that the provider uses as its underlying data store + */ private static final String DATABASE_NAME = "note_pad.db"; + + /** + * The database version + */ private static final int DATABASE_VERSION = 2; - private static final String NOTES_TABLE_NAME = "notes"; + /** + * A projection map used to select columns from the database + */ private static HashMap<String, String> sNotesProjectionMap; + + /** + * A projection map used to select columns from the database + */ private static HashMap<String, String> sLiveFolderProjectionMap; + /* + * Constants used by the Uri matcher to choose an action based on the pattern + * of the incoming URI + */ + // The incoming URI matches the Notes URI pattern private static final int NOTES = 1; + + // The incoming URI matches the Note ID URI pattern private static final int NOTE_ID = 2; + + // The incoming URI matches the Live Folder URI pattern private static final int LIVE_FOLDER_NOTES = 3; + /** + * A UriMatcher instance + */ private static final UriMatcher sUriMatcher; + // Handle to a new DatabaseHelper. + private DatabaseHelper mOpenHelper; + + /** + * A block that instantiates and sets static objects + */ + static { + + /* + * Creates and initializes the URI matcher + */ + // Create a new instance + sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + + // Add a pattern that routes URIs terminated with "notes" to a NOTES operation + sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES); + + // Add a pattern that routes URIs terminated with "notes" plus an integer + // to a note ID operation + sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID); + + // Add a pattern that routes URIs terminated with live_folders/notes to a + // live folder operation + sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES); + + /* + * Creates and initializes a projection map that returns all columns + */ + + // Creates a new projection map instance. The map returns a column name + // given a string. The two are usually equal. + sNotesProjectionMap = new HashMap<String, String>(); + + // Maps the string "_ID" to the column name "_ID" + sNotesProjectionMap.put(NotePad.Notes._ID, NotePad.Notes._ID); + + // Maps "title" to "title" + sNotesProjectionMap.put(NotePad.Notes.COLUMN_NAME_TITLE, NotePad.Notes.COLUMN_NAME_TITLE); + + // Maps "note" to "note" + sNotesProjectionMap.put(NotePad.Notes.COLUMN_NAME_NOTE, NotePad.Notes.COLUMN_NAME_NOTE); + + // Maps "created" to "created" + sNotesProjectionMap.put(NotePad.Notes.COLUMN_NAME_CREATE_DATE, + NotePad.Notes.COLUMN_NAME_CREATE_DATE); + + // Maps "modified" to "modified" + sNotesProjectionMap.put( + NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE, + NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE); + + /* + * Creates an initializes a projection map for handling Live Folders + */ + + // Creates a new projection map instance + sLiveFolderProjectionMap = new HashMap<String, String>(); + + // Maps "_ID" to "_ID AS _ID" for a live folder + sLiveFolderProjectionMap.put(LiveFolders._ID, NotePad.Notes._ID + " AS " + LiveFolders._ID); + + // Maps "NAME" to "title AS NAME" + sLiveFolderProjectionMap.put(LiveFolders.NAME, NotePad.Notes.COLUMN_NAME_TITLE + " AS " + + LiveFolders.NAME); + } + + + /** - * This class helps open, create, and upgrade the database file. + * + * This class helps open, create, and upgrade the database file. Set to package visibility + * for testing purposes. */ - private static class DatabaseHelper extends SQLiteOpenHelper { + static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { + + // calls the super constructor, requesting the default cursor factory. super(context, DATABASE_NAME, null, DATABASE_VERSION); } + /** + * + * Creates the underlying database with table name and column names taken from the + * NotePad class. + */ @Override public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " (" - + Notes._ID + " INTEGER PRIMARY KEY," - + Notes.TITLE + " TEXT," - + Notes.NOTE + " TEXT," - + Notes.CREATED_DATE + " INTEGER," - + Notes.MODIFIED_DATE + " INTEGER" + db.execSQL("CREATE TABLE " + NotePad.Notes.TABLE_NAME + " (" + + NotePad.Notes._ID + " INTEGER PRIMARY KEY," + + NotePad.Notes.COLUMN_NAME_TITLE + " TEXT," + + NotePad.Notes.COLUMN_NAME_NOTE + " TEXT," + + NotePad.Notes.COLUMN_NAME_CREATE_DATE + " INTEGER," + + NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE + " INTEGER" + ");"); } + /** + * + * Demonstrates that the provider must consider what happens when the + * underlying datastore is changed. In this sample, the database is upgraded the database + * by destroying the existing data. + * A real application should upgrade the database in place. + */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + // Logs that the database is being upgraded Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); + + // Kills the table and existing data db.execSQL("DROP TABLE IF EXISTS notes"); + + // Recreates the database with a new version onCreate(db); } } - private DatabaseHelper mOpenHelper; - + /** + * + * Initializes the provider by creating a new DatabaseHelper. onCreate() is called + * automatically when Android creates the provider in response to a resolver request from a + * client. + */ @Override public boolean onCreate() { + + // Creates a new helper object. Note that the database itself isn't opened until + // something tries to access it, and it's only created if it doesn't already exist. mOpenHelper = new DatabaseHelper(getContext()); + + // Assumes that any failures will be reported by a thrown exception. return true; } + /** + * This method is called when a client calls + * {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)}. + * Queries the database and returns a cursor containing the results. + * + * @return A cursor containing the results of the query. The cursor exists but is empty if + * the query returns no results or an exception occurs. + * @throws IllegalArgumentException if the incoming URI pattern is invalid. + */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + + // Constructs a new query builder and sets its table name SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables(NOTES_TABLE_NAME); + qb.setTables(NotePad.Notes.TABLE_NAME); + /** + * Choose the projection and adjust the "where" clause based on URI pattern-matching. + */ switch (sUriMatcher.match(uri)) { - case NOTES: - qb.setProjectionMap(sNotesProjectionMap); - break; + // If the incoming URI is for notes, chooses the Notes projection + case NOTES: + qb.setProjectionMap(sNotesProjectionMap); + break; - case NOTE_ID: - qb.setProjectionMap(sNotesProjectionMap); - qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1)); - break; + /* If the incoming URI is for a single note identified by its ID, chooses the + * note ID projection, and appends "_ID = <noteID>" to the where clause, so that + * it selects that single note + */ + case NOTE_ID: + qb.setProjectionMap(sNotesProjectionMap); + qb.appendWhere( + NotePad.Notes._ID + // the name of the ID column + "=" + + // the position of the note ID itself in the incoming URI + uri.getPathSegments().get(NotePad.Notes.NOTE_ID_PATH_POSITION)); + break; - case LIVE_FOLDER_NOTES: - qb.setProjectionMap(sLiveFolderProjectionMap); - break; + case LIVE_FOLDER_NOTES: + // If the incoming URI is from a live folder, chooses the live folder projection. + qb.setProjectionMap(sLiveFolderProjectionMap); + break; - default: - throw new IllegalArgumentException("Unknown URI " + uri); + default: + // If the URI doesn't match any of the known patterns, throw an exception. + throw new IllegalArgumentException("Unknown URI " + uri); } - // If no sort order is specified use the default + String orderBy; + // If no sort order is specified, uses the default if (TextUtils.isEmpty(sortOrder)) { orderBy = NotePad.Notes.DEFAULT_SORT_ORDER; } else { + // otherwise, uses the incoming sort order orderBy = sortOrder; } - // Get the database and run the query + // Opens the database object in "read" mode, since no writes need to be done. 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 + /* + * Performs the query. If no problems occur trying to read the database, then a Cursor + * object is returned; otherwise, the cursor variable contains null. If no records were + * selected, then the Cursor object is empty, and Cursor.getCount() returns 0. + */ + Cursor c = qb.query( + db, // The database to query + projection, // The columns to return from the query + selection, // The columns for the where clause + selectionArgs, // The values for the where clause + null, // don't group the rows + null, // don't filter by row groups + orderBy // The sort order + ); + + // Tells the Cursor what URI to watch, so it knows when its source data changes c.setNotificationUri(getContext().getContentResolver(), uri); return c; } - @Override - public String getType(Uri uri) { - switch (sUriMatcher.match(uri)) { - case NOTES: - case LIVE_FOLDER_NOTES: - return Notes.CONTENT_TYPE; - - case NOTE_ID: - return Notes.CONTENT_ITEM_TYPE; - - default: - throw new IllegalArgumentException("Unknown URI " + uri); - } - } - -//BEGIN_INCLUDE(stream) /** - * Return the types of data streams we can return. Currently we only - * support URIs to specific notes, and can convert such a note to a - * plain text stream. + * This is called when a client calls {@link android.content.ContentResolver#getType(Uri)}. + * Returns the MIME data type of the URI given as a parameter. + * + * @return The MIME type of the URI. + * @throws IllegalArgumentException if the incoming URI pattern is invalid. */ @Override - public String[] getStreamTypes(Uri uri, String mimeTypeFilter) { + public String getType(Uri uri) { + + /** + * Chooses the MIME type based on the incoming URI pattern + */ switch (sUriMatcher.match(uri)) { + + // If the pattern is for notes or live folders, returns the general content type. case NOTES: case LIVE_FOLDER_NOTES: - return null; + return NotePad.Notes.CONTENT_TYPE; + // If the pattern is for note IDs, returns the note ID content type. case NOTE_ID: - if (compareMimeTypes("text/plain", mimeTypeFilter)) { - return new String[] { "text/plain" }; - } - return null; + return NotePad.Notes.CONTENT_ITEM_TYPE; + // If the pattern doesn't match any permitted patterns, throws an exception. default: throw new IllegalArgumentException("Unknown URI " + uri); - } - } - - /** - * Standard projection for the interesting columns of a normal note. - */ - private static final String[] READ_NOTE_PROJECTION = new String[] { - Notes._ID, // 0 - Notes.NOTE, // 1 - NotePad.Notes.TITLE, // 2 - }; - private static final int READ_NOTE_NOTE_INDEX = 1; - private static final int READ_NOTE_TITLE_INDEX = 2; - - /** - * Implement the other side of getStreamTypes: for each stream time we - * report to support, we need to actually be able to return a stream of - * data. This function simply retrieves a cursor for the URI of interest, - * and uses ContentProvider's openPipeHelper() to start the work of - * convering the data off into another thread. - */ - @Override - public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) - throws FileNotFoundException { - // Check if we support a stream MIME type for this URI. - String[] mimeTypes = getStreamTypes(uri, mimeTypeFilter); - if (mimeTypes != null) { - // Retrieve the note for this URI. - Cursor c = query(uri, READ_NOTE_PROJECTION, null, null, null); - if (c == null || !c.moveToFirst()) { - if (c != null) { - c.close(); - } - throw new FileNotFoundException("Unable to query " + uri); - } - // Start a thread to pipe the data back to the client. - return new AssetFileDescriptor( - openPipeHelper(uri, mimeTypes[0], opts, c, this), 0, - AssetFileDescriptor.UNKNOWN_LENGTH); } - return super.openTypedAssetFile(uri, mimeTypeFilter, opts); } /** - * Implementation of {@link android.content.ContentProvider.PipeDataWriter} - * to perform the actual work of converting the data in one of cursors to a - * stream of data for the client to read. + * This is called when a client calls + * {@link android.content.ContentResolver#insert(Uri, ContentValues)}. + * Inserts a new row into the database. This method sets up default values for any + * columns that are not included in the incoming map. + * If rows were inserted, then listeners are notified of the change. + * @return The row ID of the inserted row. + * @throws SQLException if the insertion fails. */ @Override - public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, - Bundle opts, Cursor c) { - // We currently only support conversion-to-text from a single note entry, - // so no need for cursor data type checking here. - FileOutputStream fout = new FileOutputStream(output.getFileDescriptor()); - PrintWriter pw = null; - try { - pw = new PrintWriter(new OutputStreamWriter(fout, "UTF-8")); - pw.println(c.getString(READ_NOTE_TITLE_INDEX)); - pw.println(""); - pw.println(c.getString(READ_NOTE_NOTE_INDEX)); - } catch (UnsupportedEncodingException e) { - Log.w(TAG, "Ooops", e); - } finally { - c.close(); - if (pw != null) { - pw.flush(); - } - try { - fout.close(); - } catch (IOException e) { - } - } - } -//END_INCLUDE(stream) - - @Override public Uri insert(Uri uri, ContentValues initialValues) { - // Validate the requested uri + + // Validates the incoming URI. Only the full provider URI is allowed for inserts. if (sUriMatcher.match(uri) != NOTES) { throw new IllegalArgumentException("Unknown URI " + uri); } + // A map to hold the new record's values. ContentValues values; + + // If the incoming values map is not null, uses it for the new values. if (initialValues != null) { values = new ContentValues(initialValues); + } else { + // Otherwise, create a new value map values = new ContentValues(); } + // Gets the current system time in milliseconds Long now = Long.valueOf(System.currentTimeMillis()); - // Make sure that the fields are all set - if (values.containsKey(NotePad.Notes.CREATED_DATE) == false) { - values.put(NotePad.Notes.CREATED_DATE, now); + // If the values map doesn't contain the creation date, sets the value to the current time. + if (values.containsKey(NotePad.Notes.COLUMN_NAME_CREATE_DATE) == false) { + values.put(NotePad.Notes.COLUMN_NAME_CREATE_DATE, now); } - if (values.containsKey(NotePad.Notes.MODIFIED_DATE) == false) { - values.put(NotePad.Notes.MODIFIED_DATE, now); + // If the values map doesn't contain the modification date, sets the value to the current + // time. + if (values.containsKey(NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE) == false) { + values.put(NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE, now); } - if (values.containsKey(NotePad.Notes.TITLE) == false) { + // If the values map doesn't contain a title, sets the value to the default title. + if (values.containsKey(NotePad.Notes.COLUMN_NAME_TITLE) == false) { Resources r = Resources.getSystem(); - values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled)); + values.put(NotePad.Notes.COLUMN_NAME_TITLE, r.getString(android.R.string.untitled)); } - if (values.containsKey(NotePad.Notes.NOTE) == false) { - values.put(NotePad.Notes.NOTE, ""); + // If the values map doesn't contain note text, sets the value to an empty string. + if (values.containsKey(NotePad.Notes.COLUMN_NAME_NOTE) == false) { + values.put(NotePad.Notes.COLUMN_NAME_NOTE, ""); } + // Opens the database object in "write" mode. SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values); + + // Performs the insert and returns the ID of the new note. + long rowId = db.insert( + NotePad.Notes.TABLE_NAME, // The table to insert into. + NotePad.Notes.COLUMN_NAME_NOTE, // A hack, SQLite sets this column value to null + // if values is empty. + values // A map of column names, and the values to insert + // into the columns. + ); + + // If the insert succeeded, the row ID exists. if (rowId > 0) { - Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId); + // Creates a URI with the note ID pattern and the new row ID appended to it. + Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_ID_URI_BASE, rowId); + + // Notifies observers registered against this provider that the data changed. getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } + // If the insert didn't succeed, then the rowID is <= 0. Throws an exception. throw new SQLException("Failed to insert row into " + uri); } + /** + * This is called when a client calls + * {@link android.content.ContentResolver#delete(Uri, String, String[])}. + * Deletes records from the database. If the incoming URI matches the note ID URI pattern, + * this method deletes the one record specified by the ID in the URI. Otherwise, it deletes a + * a set of records. The record or records must also match the input selection criteria + * specified by where and whereArgs. + * + * If rows were deleted, then listeners are notified of the change. + * @return If a "where" clause is used, the number of rows affected is returned, otherwise + * 0 is returned. To delete all rows and get a row count, use "1" as the where clause. + * @throws IllegalArgumentException if the incoming URI pattern is invalid. + */ @Override public int delete(Uri uri, String where, String[] whereArgs) { + + // Opens the database object in "write" mode. SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + int count; + + // Does the delete based on the incoming URI pattern. switch (sUriMatcher.match(uri)) { - case NOTES: - count = db.delete(NOTES_TABLE_NAME, where, whereArgs); - break; - case NOTE_ID: - String noteId = uri.getPathSegments().get(1); - count = db.delete(NOTES_TABLE_NAME, Notes._ID + "=" + noteId - + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); - break; + // If the incoming pattern matches the general pattern for notes, does a delete + // based on the incoming "where" columns and arguments. + case NOTES: + count = db.delete( + NotePad.Notes.TABLE_NAME, // The database table name + where, // The incoming where clause column names + whereArgs // The incoming where clause values + ); + break; + + // If the incoming URI matches a single note ID, does the delete based on the + // incoming data, but modifies the where clause to restrict it to the + // particular note ID. + case NOTE_ID: + // From the incoming URI, get the note ID + String noteId = uri.getPathSegments().get(NotePad.Notes.NOTE_ID_PATH_POSITION); + + // If no where clause was passed in, uses the note ID column name + // for a column and the note ID for a value. + if (TextUtils.isEmpty(where)) { + where = NotePad.Notes._ID + " = ?"; + whereArgs[0] = noteId; + + } else { + + /* + * If where clause columns were passed in, appends the note ID column name to + * the list of columns using a replaceable parameter. This works even if the + * other columns have actual values. + */ + // Appends the note ID column name as an AND condition using a replaceable + // parameter. + where = where + " AND " + NotePad.Notes._ID + " = ?"; + + // Appends the note ID value to the end of the where clause values. + whereArgs[whereArgs.length] = noteId; + } - default: - throw new IllegalArgumentException("Unknown URI " + uri); + // Performs the delete. + count = db.delete( + NotePad.Notes.TABLE_NAME, // The database table name. + where, // The incoming where clause column names. + whereArgs // The incoming where clause values. + ); + break; + + // If the incoming pattern is invalid, throws an exception. + default: + throw new IllegalArgumentException("Unknown URI " + uri); } + /*Gets a handle to the content resolver object for the current context, and notifies it + * that the incoming URI changed. The object passes this along to the resolver framework, + * and observers that have registered themselves for the provider are notified. + */ getContext().getContentResolver().notifyChange(uri, null); + + // Returns the number of rows deleted. return count; } + /** + * This is called when a client calls + * {@link android.content.ContentResolver#insert(Uri, ContentValues)} + * Updates records in the database. The column names specified by the keys in the values map + * are updated with new data specified by the values in the map. If the incoming URI matches the + * note ID URI pattern, then the method updates the one record specified by the ID in the URI; + * otherwise, it updates a set of records. The record or records must match the input + * selection criteria specified by where and whereArgs. + * If rows were updated, then listeners are notified of the change. + * @return The number of rows updated. + * @throws IllegalArgumentException if the incoming URI pattern is invalid. + */ @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { + + // Opens the database object in "write" mode. SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; + + // Does the update based on the incoming URI pattern switch (sUriMatcher.match(uri)) { - case NOTES: - count = db.update(NOTES_TABLE_NAME, values, where, whereArgs); - break; - case NOTE_ID: - String noteId = uri.getPathSegments().get(1); - count = db.update(NOTES_TABLE_NAME, values, Notes._ID + "=" + noteId - + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); - break; + // If the incoming URI matches the general notes pattern, does the update based on + // the incoming data. + case NOTES: - default: - throw new IllegalArgumentException("Unknown URI " + uri); + // Does the update and returns the number of rows updated. + count = db.update( + NotePad.Notes.TABLE_NAME, // The database table name. + values, // A map of column names and new values to use. + where, // The where clause column names. + whereArgs // The where clause column values to select on. + ); + break; + + // If the incoming URI matches a single note ID, does the update based on the incoming + // data, but modifies the where clause to restrict it to the particular note ID. + case NOTE_ID: + // From the incoming URI, get the note ID + String noteId = uri.getPathSegments().get(NotePad.Notes.NOTE_ID_PATH_POSITION); + + // If no where clause was passed in, uses the note ID column name + // for a column and the note ID for a value. + if (TextUtils.isEmpty(where)) { + where = NotePad.Notes._ID + " = ?"; + whereArgs[0] = noteId; + + // If where clause columns were passed in, appends the note ID to the where + // clause + } else { + + /* + * Appends the note ID column name to the list of columns, with a replaceable + * parameter. This will work even if the rest of the columns have been set with + * actual values. + */ + // Appends the note ID column name as an AND condition with a replaceable + // parameter. + where = where + " AND " + NotePad.Notes._ID + " = ?"; + + // Appends the note ID value to the end of the where clause values. + whereArgs[whereArgs.length] = noteId; + } + + // Does the update. + // Does the update and returns the number of rows updated. + count = db.update( + NotePad.Notes.TABLE_NAME, // The database table name. + values, // A map of column names and new values to use. + where, // The where clause column names. + whereArgs // The where clause column values to select on. + ); + break; + // If the incoming pattern is invalid, throws an exception. + default: + throw new IllegalArgumentException("Unknown URI " + uri); } + /*Gets a handle to the content resolver object for the current context, and notifies it + * that the incoming URI changed. The object passes this along to the resolver framework, + * and observers that have registered themselves for the provider are notified. + */ getContext().getContentResolver().notifyChange(uri, null); + + // Returns the number of rows updated. return count; } - static { - sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES); - sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID); - sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES); - - sNotesProjectionMap = new HashMap<String, String>(); - sNotesProjectionMap.put(Notes._ID, Notes._ID); - sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE); - sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE); - sNotesProjectionMap.put(Notes.CREATED_DATE, Notes.CREATED_DATE); - sNotesProjectionMap.put(Notes.MODIFIED_DATE, Notes.MODIFIED_DATE); - - // Support for Live Folders. - sLiveFolderProjectionMap = new HashMap<String, String>(); - sLiveFolderProjectionMap.put(LiveFolders._ID, Notes._ID + " AS " + - LiveFolders._ID); - sLiveFolderProjectionMap.put(LiveFolders.NAME, Notes.TITLE + " AS " + - LiveFolders.NAME); - // Add more columns here for more robust Live Folders. + /** + * A test package can call this to get a handle to the database underlying NotePadProvider, + * so it can insert test data into the database. The test case class is responsible for + * instantiating the provider in a test context; {@link android.test.ProviderTestCase2} does + * this during the call to setUp() + * + * @return a handle to the database helper object for the provider's data. + */ + DatabaseHelper getOpenHelperForTest() { + return mOpenHelper; } } |
