From: gumartinm Date: Wed, 8 Feb 2012 06:33:40 +0000 (+0100) Subject: Finish my SQLite Provider. X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=ebd50ef31c6d618d9966c734be5f58585e838658;p=JavaForFun Finish my SQLite Provider. Next step: to test it. I did not have time to run my code probably it is not working right now. --- diff --git a/Android/Testing/Test3/src/de/android/test3/Indexer.java b/Android/Testing/Test3/src/de/android/test3/Indexer.java new file mode 100644 index 0000000..05791f9 --- /dev/null +++ b/Android/Testing/Test3/src/de/android/test3/Indexer.java @@ -0,0 +1,61 @@ +package de.android.test3; + +import android.net.Uri; +import android.provider.BaseColumns; + +public final class Indexer { + public static final String AUTHORITY = "de.android.test3.provider.IndexerProvider"; + + // This class cannot be instantiated + private Indexer() { + } + + /** + * Indexer table contract + */ + public static final class Index implements BaseColumns { + + // This class cannot be instantiated + private Index() {} + + /** + * The table name offered by this provider + */ + public static final String TABLE_NAME = "indexer"; + + /** + * Column name for the path of the file + *

Type: TEXT

+ */ + public static final String COLUMN_NAME_PATH = "path"; + + /** + * Column name for the ad unique identifier number + *

Type: REAL

+ */ + public static final String COLUMN_NAME_ID_AD = "idad"; + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "by " + Index._ID; + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of notes. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.index"; + + /** + * The MIME type of a {@link #CONTENT_URI} sub-directory of a single + * note. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.index"; + + /** + * The content URI base for a single note. Callers must + * append a numeric note id to this Uri to retrieve a note + */ + public static final Uri CONTENT_ID_URI_BASE + = Uri.parse("content://com.google.provider.NotePad/notes/"); + } +} diff --git a/Android/Testing/Test3/src/de/android/test3/IndexerOpenHelper.java b/Android/Testing/Test3/src/de/android/test3/IndexerOpenHelper.java index e62eb23..55a4e0d 100644 --- a/Android/Testing/Test3/src/de/android/test3/IndexerOpenHelper.java +++ b/Android/Testing/Test3/src/de/android/test3/IndexerOpenHelper.java @@ -9,17 +9,12 @@ public class IndexerOpenHelper extends SQLiteOpenHelper { // Used for debugging and logging private static final String TAG = "IndexerOpenHelper"; - private static final String DBNAME = "mobiads"; + private static final String DATABASE_NAME = "mobiads.db"; private static final int DATABASE_VERSION = 1; - private static final String TABLE_NAME = "indexer"; - private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + - "(_ID INTEGER NOT NULL, " + - "PATH TEXT(15) NOT NULL, " + - "PRIMARY KEY (_ID), " + - "UNIQUE (PATH), " + ")"; + IndexerOpenHelper(Context context) { - super(context, DBNAME, null, DATABASE_VERSION); + super(context, DATABASE_NAME, null, DATABASE_VERSION); } @@ -30,7 +25,11 @@ public class IndexerOpenHelper extends SQLiteOpenHelper { */ @Override public void onCreate(SQLiteDatabase db) { - db.execSQL(TABLE_CREATE); + db.execSQL("CREATE TABLE " + Indexer.Index.TABLE_NAME + " (" + + Indexer.Index._ID + " INTEGER PRIMARY KEY, " + + Indexer.Index.COLUMN_NAME_ID_AD + " REAL" + " UNIQUE" + "NOT NULL" + + Indexer.Index.COLUMN_NAME_PATH + " TEXT(15)" + " UNIQUE" + " NOT NULL" + + ");"); } @@ -48,7 +47,7 @@ public class IndexerOpenHelper extends SQLiteOpenHelper { + newVersion + ", which will destroy all old data"); // Kills the table and existing data - db.execSQL("DROP TABLE IF EXISTS indexer"); + db.execSQL("DROP TABLE IF EXISTS " + Indexer.Index.TABLE_NAME); // Recreates the database with a new version onCreate(db); diff --git a/Android/Testing/Test3/src/de/android/test3/IndexerProvider.java b/Android/Testing/Test3/src/de/android/test3/IndexerProvider.java index 52b1a74..3f456f3 100644 --- a/Android/Testing/Test3/src/de/android/test3/IndexerProvider.java +++ b/Android/Testing/Test3/src/de/android/test3/IndexerProvider.java @@ -1,14 +1,16 @@ -/** - * - */ package de.android.test3; +import java.util.HashMap; import android.content.ContentProvider; +import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; +import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; +import android.text.TextUtils; /** * @@ -17,6 +19,11 @@ public class IndexerProvider extends ContentProvider { // Creates a UriMatcher object. private static final UriMatcher sUriMatcher; + /** + * A projection map used to select columns from the database + */ + private static HashMap sIndexerProjectionMap; + // Handle to a new DatabaseHelper. private IndexerOpenHelper mOpenHelper; @@ -35,11 +42,11 @@ public class IndexerProvider extends ContentProvider { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // Add a pattern that routes URIs terminated with "notes" to a NOTES operation - sUriMatcher.addURI("de.android.test3.provider", "indexer", INDEXER); + sUriMatcher.addURI("de.android.test3.provider", Indexer.Index.TABLE_NAME, INDEXER); // Add a pattern that routes URIs terminated with "notes" plus an integer // to a note ID operation - sUriMatcher.addURI("de.android.test3.provider", "indexer/#", INDEXER_ID); + sUriMatcher.addURI("de.android.test3.provider", Indexer.Index.TABLE_NAME + "/#", INDEXER_ID); } @@ -86,82 +93,301 @@ public class IndexerProvider extends ContentProvider { // Does the delete based on the incoming URI pattern. switch (sUriMatcher.match(uri)) { - case INDEXER_ID: - /* - * Starts a final WHERE clause by restricting it to the - * desired note ID. - */ - finalWhere = - NotePad.Notes._ID + // The ID column name - " = " + // test for equality - uri.getPathSegments(). // the incoming note ID - get(NotePad.Notes.NOTE_ID_PATH_POSITION) - ; - - // If there were additional selection criteria, append them to the final - // WHERE clause - if (where != null) { - finalWhere = finalWhere + " AND " + where; - } - - // Performs the delete. - count = db.delete( - NotePad.Notes.TABLE_NAME, // The database table name. - finalWhere, // The final WHERE clause - whereArgs // The incoming where clause values. - ); - break; - - default: - throw new IllegalArgumentException("Unknown URI " + uri); + case INDEXER: + // If the incoming pattern matches the general pattern for notes, does a delete + // based on the incoming "where" columns and arguments. + count = db.delete( + Indexer.Index.TABLE_NAME, // The database table name + selection, // The incoming where clause column names + selectionArgs // The incoming where clause values + ); + break; + case INDEXER_ID: + /* + * Starts a final WHERE clause by restricting it to the + * desired note ID. + */ + finalWhere = + Indexer.Index._ID + // The ID column name + " = " + // test for equality + uri.getPathSegments().get(1); // the incoming note ID + + + // If there were additional selection criteria, append them to the final + // WHERE clause + if (selection != null) { + finalWhere = finalWhere + " AND " + selection; + } + + // Performs the delete. + count = db.delete( + Indexer.Index.TABLE_NAME, // The database table name. + finalWhere, // The final WHERE clause + selectionArgs // The incoming where clause values. + ); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); } - return 0; + return count; } - /* (non-Javadoc) - * @see android.content.ContentProvider#getType(android.net.Uri) - */ + /** + * 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. + * + * @param uri The URI whose MIME type is desired. + * @return The MIME type of the URI. + * @throws IllegalArgumentException if the incoming URI pattern is invalid. + */ @Override public String getType(Uri uri) { - // TODO Auto-generated method stub - return null; + /** + * 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 INDEXER: + return Indexer.Index.CONTENT_TYPE; + + // If the pattern is for note IDs, returns the note ID content type. + case INDEXER_ID: + return Indexer.Index.CONTENT_ITEM_TYPE; + + // If the URI pattern doesn't match any permitted patterns, throws an exception. + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } } - /* (non-Javadoc) - * @see android.content.ContentProvider#insert(android.net.Uri, android.content.ContentValues) - */ + + /** + * 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 Uri insert(Uri uri, ContentValues values) { - /* - * Gets a writeable database. This will trigger its creation if it doesn't already exist. - * - */ - db = mOpenHelper.getWritableDatabase(); + public Uri insert(Uri uri, ContentValues initialValues) { + // 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(); + } + + // If the values map doesn't contain the path or ad identifier number. + if ((values.containsKey(Indexer.Index.COLUMN_NAME_PATH) == false) || + (values.containsKey(Indexer.Index.COLUMN_NAME_ID_AD) == false)){ + throw new SQLException("Missed parameter. Failed to insert row into " + uri); + } + + // Opens the database object in "write" mode. + // This will trigger its creation if it doesn't already exist. + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + + // Performs the insert and returns the ID of the new note. + long rowId = db.insert( + Indexer.Index.COLUMN_NAME_ID_AD, // The table to insert into. + Indexer.Index.COLUMN_NAME_PATH, // 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) { + // Creates a URI with the note ID pattern and the new row ID appended to it. + Uri noteUri = ContentUris.withAppendedId(Indexer.Index.CONTENT_ID_URI_BASE, rowId); - return null; + // 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); } - - /* (non-Javadoc) - * @see android.content.ContentProvider#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) + /** + * 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) { - // TODO Auto-generated method stub - return null; + + // Constructs a new query builder and sets its table name + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(Indexer.Index.TABLE_NAME); + + /** + * Choose the projection and adjust the "where" clause based on URI pattern-matching. + */ + switch (sUriMatcher.match(uri)) { + // If the incoming URI is for notes, chooses the Indexer projection + case INDEXER: + qb.setProjectionMap(sIndexerProjectionMap); + + break; + + /* If the incoming URI is for a single note identified by its ID, chooses the + * note ID projection, and appends "_ID = " to the where clause, so that + * it selects that single note + */ + case INDEXER_ID: + qb.setProjectionMap(sIndexerProjectionMap); + qb.appendWhere( + Indexer.Index._ID + // the name of the ID column + "=" + + // the position of the note ID itself in the incoming URI + uri.getPathSegments().get(1)); + break; + + default: + // If the URI doesn't match any of the known patterns, throw an exception. + throw new IllegalArgumentException("Unknown URI " + uri); + } + + String orderBy; + // If no sort order is specified, uses the default + if (TextUtils.isEmpty(sortOrder)) { + orderBy = Indexer.Index.DEFAULT_SORT_ORDER; + } else { + // otherwise, uses the incoming sort order + orderBy = sortOrder; + } + + // Opens the database object in "read" mode, since no writes need to be done. + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + + /* + * 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; } - /* (non-Javadoc) - * @see android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) - */ + /** + * This is called when a client calls + * {@link android.content.ContentResolver#update(Uri,ContentValues,String,String[])} + * 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. + * + * @param uri The URI pattern to match and update. + * @param values A map of column names (keys) and new values (values). + * @param where An SQL "WHERE" clause that selects records based on their column values. If this + * is null, then all records that match the URI pattern are selected. + * @param whereArgs An array of selection criteria. If the "where" param contains value + * placeholders ("?"), then each placeholder is replaced by the corresponding element in the + * array. + * @return The number of rows updated. + * @throws IllegalArgumentException if the incoming URI pattern is invalid. + */ @Override - public int update(Uri uri, ContentValues values, String selection, - String[] selectionArgs) { - // TODO Auto-generated method stub - return 0; + public int update(Uri uri, ContentValues values, String where, + String[] whereArgs) { + + // Opens the database object in "write" mode. + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + int count; + String finalWhere; + + // Does the update based on the incoming URI pattern + switch (sUriMatcher.match(uri)) { + + // If the incoming URI matches the general notes pattern, does the update based on + // the incoming data. + case INDEXER: + + // Does the update and returns the number of rows updated. + count = db.update( + Indexer.Index.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 INDEXER_ID: + + // From the incoming URI, get the note ID + String noteId = uri.getPathSegments().get(1); + + /* + * Starts creating the final WHERE clause by restricting it to the incoming + * note ID. + */ + finalWhere = + Indexer.Index._ID + // The ID column name + " = " + // test for equality + noteId; // the incoming note ID + + // If there were additional selection criteria, append them to the final WHERE + // clause + if (where !=null) { + finalWhere = finalWhere + " AND " + where; + } + + + // Does the update and returns the number of rows updated. + count = db.update( + Indexer.Index.TABLE_NAME, // The database table name. + values, // A map of column names and new values to use. + finalWhere, // The final WHERE clause to use + // placeholders for whereArgs + whereArgs // The where clause column values to select on, or + // null if the values are in the where argument. + ); + break; + + 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; } }