| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.threadsample; |
| |
| import android.content.ContentProvider; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.UriMatcher; |
| import android.database.Cursor; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.database.sqlite.SQLiteException; |
| import android.database.sqlite.SQLiteOpenHelper; |
| import android.net.Uri; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| /** |
| * |
| * Defines a ContentProvider that stores URLs of Picasa featured pictures |
| * The provider also has a table that tracks the last time a picture URL was updated. |
| */ |
| public class DataProvider extends ContentProvider { |
| // Indicates that the incoming query is for a picture URL |
| public static final int IMAGE_URL_QUERY = 1; |
| |
| // Indicates that the incoming query is for a URL modification date |
| public static final int URL_DATE_QUERY = 2; |
| |
| // Indicates an invalid content URI |
| public static final int INVALID_URI = -1; |
| |
| // Constants for building SQLite tables during initialization |
| private static final String TEXT_TYPE = "TEXT"; |
| private static final String PRIMARY_KEY_TYPE = "INTEGER PRIMARY KEY"; |
| private static final String INTEGER_TYPE = "INTEGER"; |
| |
| // Defines an SQLite statement that builds the Picasa picture URL table |
| private static final String CREATE_PICTUREURL_TABLE_SQL = "CREATE TABLE" + " " + |
| DataProviderContract.PICTUREURL_TABLE_NAME + " " + |
| "(" + " " + |
| DataProviderContract.ROW_ID + " " + PRIMARY_KEY_TYPE + " ," + |
| DataProviderContract.IMAGE_THUMBURL_COLUMN + " " + TEXT_TYPE + " ," + |
| DataProviderContract.IMAGE_URL_COLUMN + " " + TEXT_TYPE + " ," + |
| DataProviderContract.IMAGE_THUMBNAME_COLUMN + " " + TEXT_TYPE + " ," + |
| DataProviderContract.IMAGE_PICTURENAME_COLUMN + " " + TEXT_TYPE + |
| ")"; |
| |
| // Defines an SQLite statement that builds the URL modification date table |
| private static final String CREATE_DATE_TABLE_SQL = "CREATE TABLE" + " " + |
| DataProviderContract.DATE_TABLE_NAME + " " + |
| "(" + " " + |
| DataProviderContract.ROW_ID + " " + PRIMARY_KEY_TYPE + " ," + |
| DataProviderContract.DATA_DATE_COLUMN + " " + INTEGER_TYPE + |
| ")"; |
| |
| // Identifies log statements issued by this component |
| public static final String LOG_TAG = "DataProvider"; |
| |
| // Defines an helper object for the backing database |
| private SQLiteOpenHelper mHelper; |
| |
| // Defines a helper object that matches content URIs to table-specific parameters |
| private static final UriMatcher sUriMatcher; |
| |
| // Stores the MIME types served by this provider |
| private static final SparseArray<String> sMimeTypes; |
| |
| /* |
| * Initializes meta-data used by the content provider: |
| * - UriMatcher that maps content URIs to codes |
| * - MimeType array that returns the custom MIME type of a table |
| */ |
| static { |
| |
| // Creates an object that associates content URIs with numeric codes |
| sUriMatcher = new UriMatcher(0); |
| |
| /* |
| * Sets up an array that maps content URIs to MIME types, via a mapping between the |
| * URIs and an integer code. These are custom MIME types that apply to tables and rows |
| * in this particular provider. |
| */ |
| sMimeTypes = new SparseArray<String>(); |
| |
| // Adds a URI "match" entry that maps picture URL content URIs to a numeric code |
| sUriMatcher.addURI( |
| DataProviderContract.AUTHORITY, |
| DataProviderContract.PICTUREURL_TABLE_NAME, |
| IMAGE_URL_QUERY); |
| |
| // Adds a URI "match" entry that maps modification date content URIs to a numeric code |
| sUriMatcher.addURI( |
| DataProviderContract.AUTHORITY, |
| DataProviderContract.DATE_TABLE_NAME, |
| URL_DATE_QUERY); |
| |
| // Specifies a custom MIME type for the picture URL table |
| sMimeTypes.put( |
| IMAGE_URL_QUERY, |
| "vnd.android.cursor.dir/vnd." + |
| DataProviderContract.AUTHORITY + "." + |
| DataProviderContract.PICTUREURL_TABLE_NAME); |
| |
| // Specifies the custom MIME type for a single modification date row |
| sMimeTypes.put( |
| URL_DATE_QUERY, |
| "vnd.android.cursor.item/vnd."+ |
| DataProviderContract.AUTHORITY + "." + |
| DataProviderContract.DATE_TABLE_NAME); |
| } |
| |
| // Closes the SQLite database helper class, to avoid memory leaks |
| public void close() { |
| mHelper.close(); |
| } |
| |
| /** |
| * Defines a helper class that opens the SQLite database for this provider when a request is |
| * received. If the database doesn't yet exist, the helper creates it. |
| */ |
| private class DataProviderHelper extends SQLiteOpenHelper { |
| /** |
| * Instantiates a new SQLite database using the supplied database name and version |
| * |
| * @param context The current context |
| */ |
| DataProviderHelper(Context context) { |
| super(context, |
| DataProviderContract.DATABASE_NAME, |
| null, |
| DataProviderContract.DATABASE_VERSION); |
| } |
| |
| |
| /** |
| * Executes the queries to drop all of the tables from the database. |
| * |
| * @param db A handle to the provider's backing database. |
| */ |
| private void dropTables(SQLiteDatabase db) { |
| |
| // If the table doesn't exist, don't throw an error |
| db.execSQL("DROP TABLE IF EXISTS " + DataProviderContract.PICTUREURL_TABLE_NAME); |
| db.execSQL("DROP TABLE IF EXISTS " + DataProviderContract.DATE_TABLE_NAME); |
| } |
| |
| /** |
| * Does setup of the database. The system automatically invokes this method when |
| * SQLiteDatabase.getWriteableDatabase() or SQLiteDatabase.getReadableDatabase() are |
| * invoked and no db instance is available. |
| * |
| * @param db the database instance in which to create the tables. |
| */ |
| @Override |
| public void onCreate(SQLiteDatabase db) { |
| // Creates the tables in the backing database for this provider |
| db.execSQL(CREATE_PICTUREURL_TABLE_SQL); |
| db.execSQL(CREATE_DATE_TABLE_SQL); |
| |
| } |
| |
| /** |
| * Handles upgrading the database from a previous version. Drops the old tables and creates |
| * new ones. |
| * |
| * @param db The database to upgrade |
| * @param version1 The old database version |
| * @param version2 The new database version |
| */ |
| @Override |
| public void onUpgrade(SQLiteDatabase db, int version1, int version2) { |
| Log.w(DataProviderHelper.class.getName(), |
| "Upgrading database from version " + version1 + " to " |
| + version2 + ", which will destroy all the existing data"); |
| |
| // Drops all the existing tables in the database |
| dropTables(db); |
| |
| // Invokes the onCreate callback to build new tables |
| onCreate(db); |
| } |
| /** |
| * Handles downgrading the database from a new to a previous version. Drops the old tables |
| * and creates new ones. |
| * @param db The database object to downgrade |
| * @param version1 The old database version |
| * @param version2 The new database version |
| */ |
| @Override |
| public void onDowngrade(SQLiteDatabase db, int version1, int version2) { |
| Log.w(DataProviderHelper.class.getName(), |
| "Downgrading database from version " + version1 + " to " |
| + version2 + ", which will destroy all the existing data"); |
| |
| // Drops all the existing tables in the database |
| dropTables(db); |
| |
| // Invokes the onCreate callback to build new tables |
| onCreate(db); |
| |
| } |
| } |
| /** |
| * Initializes the content provider. Notice that this method simply creates a |
| * the SQLiteOpenHelper instance and returns. You should do most of the initialization of a |
| * content provider in its static initialization block or in SQLiteDatabase.onCreate(). |
| */ |
| @Override |
| public boolean onCreate() { |
| |
| // Creates a new database helper object |
| mHelper = new DataProviderHelper(getContext()); |
| |
| return true; |
| } |
| /** |
| * Returns the result of querying the chosen table. |
| * @see android.content.ContentProvider#query(Uri, String[], String, String[], String) |
| * @param uri The content URI of the table |
| * @param projection The names of the columns to return in the cursor |
| * @param selection The selection clause for the query |
| * @param selectionArgs An array of Strings containing search criteria |
| * @param sortOrder A clause defining the order in which the retrieved rows should be sorted |
| * @return The query results, as a {@link android.database.Cursor} of rows and columns |
| */ |
| @Override |
| public Cursor query( |
| Uri uri, |
| String[] projection, |
| String selection, |
| String[] selectionArgs, |
| String sortOrder) { |
| |
| SQLiteDatabase db = mHelper.getReadableDatabase(); |
| // Decodes the content URI and maps it to a code |
| switch (sUriMatcher.match(uri)) { |
| |
| // If the query is for a picture URL |
| case IMAGE_URL_QUERY: |
| // Does the query against a read-only version of the database |
| Cursor returnCursor = db.query( |
| DataProviderContract.PICTUREURL_TABLE_NAME, |
| projection, |
| null, null, null, null, null); |
| |
| // Sets the ContentResolver to watch this content URI for data changes |
| returnCursor.setNotificationUri(getContext().getContentResolver(), uri); |
| return returnCursor; |
| |
| // If the query is for a modification date URL |
| case URL_DATE_QUERY: |
| returnCursor = db.query( |
| DataProviderContract.DATE_TABLE_NAME, |
| projection, |
| selection, |
| selectionArgs, |
| null, |
| null, |
| sortOrder); |
| |
| // No notification Uri is set, because the data doesn't have to be watched. |
| return returnCursor; |
| |
| case INVALID_URI: |
| |
| throw new IllegalArgumentException("Query -- Invalid URI:" + uri); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the mimeType associated with the Uri (query). |
| * @see android.content.ContentProvider#getType(Uri) |
| * @param uri the content URI to be checked |
| * @return the corresponding MIMEtype |
| */ |
| @Override |
| public String getType(Uri uri) { |
| |
| return sMimeTypes.get(sUriMatcher.match(uri)); |
| } |
| /** |
| * |
| * Insert a single row into a table |
| * @see android.content.ContentProvider#insert(Uri, ContentValues) |
| * @param uri the content URI of the table |
| * @param values a {@link android.content.ContentValues} object containing the row to insert |
| * @return the content URI of the new row |
| */ |
| @Override |
| public Uri insert(Uri uri, ContentValues values) { |
| |
| // Decode the URI to choose which action to take |
| switch (sUriMatcher.match(uri)) { |
| |
| // For the modification date table |
| case URL_DATE_QUERY: |
| |
| // Creates a writeable database or gets one from cache |
| SQLiteDatabase localSQLiteDatabase = mHelper.getWritableDatabase(); |
| |
| // Inserts the row into the table and returns the new row's _id value |
| long id = localSQLiteDatabase.insert( |
| DataProviderContract.DATE_TABLE_NAME, |
| DataProviderContract.DATA_DATE_COLUMN, |
| values |
| ); |
| |
| // If the insert succeeded, notify a change and return the new row's content URI. |
| if (-1 != id) { |
| getContext().getContentResolver().notifyChange(uri, null); |
| return Uri.withAppendedPath(uri, Long.toString(id)); |
| } else { |
| |
| throw new SQLiteException("Insert error:" + uri); |
| } |
| case IMAGE_URL_QUERY: |
| |
| throw new IllegalArgumentException("Insert: Invalid URI" + uri); |
| } |
| |
| return null; |
| } |
| /** |
| * Implements bulk row insertion using |
| * {@link SQLiteDatabase#insert(String, String, ContentValues) SQLiteDatabase.insert()} |
| * and SQLite transactions. The method also notifies the current |
| * {@link android.content.ContentResolver} that the {@link android.content.ContentProvider} has |
| * been changed. |
| * @see android.content.ContentProvider#bulkInsert(Uri, ContentValues[]) |
| * @param uri The content URI for the insertion |
| * @param insertValuesArray A {@link android.content.ContentValues} array containing the row to |
| * insert |
| * @return The number of rows inserted. |
| */ |
| @Override |
| public int bulkInsert(Uri uri, ContentValues[] insertValuesArray) { |
| |
| // Decodes the content URI and choose which insert to use |
| switch (sUriMatcher.match(uri)) { |
| |
| // picture URLs table |
| case IMAGE_URL_QUERY: |
| |
| // Gets a writeable database instance if one is not already cached |
| SQLiteDatabase localSQLiteDatabase = mHelper.getWritableDatabase(); |
| |
| /* |
| * Begins a transaction in "exclusive" mode. No other mutations can occur on the |
| * db until this transaction finishes. |
| */ |
| localSQLiteDatabase.beginTransaction(); |
| |
| // Deletes all the existing rows in the table |
| localSQLiteDatabase.delete(DataProviderContract.PICTUREURL_TABLE_NAME, null, null); |
| |
| // Gets the size of the bulk insert |
| int numImages = insertValuesArray.length; |
| |
| // Inserts each ContentValues entry in the array as a row in the database |
| for (int i = 0; i < numImages; i++) { |
| |
| localSQLiteDatabase.insert(DataProviderContract.PICTUREURL_TABLE_NAME, |
| DataProviderContract.IMAGE_URL_COLUMN, insertValuesArray[i]); |
| } |
| |
| // Reports that the transaction was successful and should not be backed out. |
| localSQLiteDatabase.setTransactionSuccessful(); |
| |
| // Ends the transaction and closes the current db instances |
| localSQLiteDatabase.endTransaction(); |
| localSQLiteDatabase.close(); |
| |
| /* |
| * Notifies the current ContentResolver that the data associated with "uri" has |
| * changed. |
| */ |
| |
| getContext().getContentResolver().notifyChange(uri, null); |
| |
| // The semantics of bulkInsert is to return the number of rows inserted. |
| return numImages; |
| |
| // modification date table |
| case URL_DATE_QUERY: |
| |
| // Do inserts by calling SQLiteDatabase.insert on each row in insertValuesArray |
| return super.bulkInsert(uri, insertValuesArray); |
| |
| case INVALID_URI: |
| |
| // An invalid URI was passed. Throw an exception |
| throw new IllegalArgumentException("Bulk insert -- Invalid URI:" + uri); |
| |
| } |
| |
| return -1; |
| |
| } |
| /** |
| * Returns an UnsupportedOperationException if delete is called |
| * @see android.content.ContentProvider#delete(Uri, String, String[]) |
| * @param uri The content URI |
| * @param selection The SQL WHERE string. Use "?" to mark places that should be substituted by |
| * values in selectionArgs. |
| * @param selectionArgs An array of values that are mapped to each "?" in selection. If no "?" |
| * are used, set this to NULL. |
| * |
| * @return the number of rows deleted |
| */ |
| @Override |
| public int delete(Uri uri, String selection, String[] selectionArgs) { |
| |
| throw new UnsupportedOperationException("Delete -- unsupported operation " + uri); |
| } |
| |
| /** |
| * Updates one or more rows in a table. |
| * @see android.content.ContentProvider#update(Uri, ContentValues, String, String[]) |
| * @param uri The content URI for the table |
| * @param values The values to use to update the row or rows. You only need to specify column |
| * names for the columns you want to change. To clear the contents of a column, specify the |
| * column name and NULL for its value. |
| * @param selection An SQL WHERE clause (without the WHERE keyword) specifying the rows to |
| * update. Use "?" to mark places that should be substituted by values in selectionArgs. |
| * @param selectionArgs An array of values that are mapped in order to each "?" in selection. |
| * If no "?" are used, set this to NULL. |
| * |
| * @return int The number of rows updated. |
| */ |
| @Override |
| public int update(Uri uri, ContentValues values, String selection, |
| String[] selectionArgs) { |
| |
| // Decodes the content URI and choose which insert to use |
| switch (sUriMatcher.match(uri)) { |
| |
| // A picture URL content URI |
| case URL_DATE_QUERY: |
| |
| // Creats a new writeable database or retrieves a cached one |
| SQLiteDatabase localSQLiteDatabase = mHelper.getWritableDatabase(); |
| |
| // Updates the table |
| int rows = localSQLiteDatabase.update( |
| DataProviderContract.DATE_TABLE_NAME, |
| values, |
| selection, |
| selectionArgs); |
| |
| // If the update succeeded, notify a change and return the number of updated rows. |
| if (0 != rows) { |
| getContext().getContentResolver().notifyChange(uri, null); |
| return rows; |
| } else { |
| |
| throw new SQLiteException("Update error:" + uri); |
| } |
| |
| case IMAGE_URL_QUERY: |
| |
| throw new IllegalArgumentException("Update: Invalid URI: " + uri); |
| } |
| |
| return -1; |
| } |
| } |