[Из песочницы] RESTful API под Android: pattern B
public class FinchVideoContentProvider extends RESTfulContentProvider { public static final String VIDEO = «video»; public static final String DATABASE_NAME = VIDEO + ».db»; static int DATABASE_VERSION = 2;
public static final String VIDEOS_TABLE_NAME = «video»;
private static final String FINCH_VIDEO_FILE_CACHE = «finch_video_file_cache»;
private static final int VIDEOS = 1; private static final int VIDEO_ID = 2; private static final int THUMB_VIDEO_ID = 3; private static final int THUMB_ID = 4;
private static UriMatcher sUriMatcher;
// Statically construct a uri matcher that can detect URIs referencing // more than 1 video, a single video, or a single thumb nail image. static { sUriMatcher = new UriMatcher (UriMatcher.NO_MATCH); sUriMatcher.addURI (FinchVideo.AUTHORITY, FinchVideo.Videos.VIDEO, VIDEOS); // use of the hash character indicates matching of an id sUriMatcher.addURI (FinchVideo.AUTHORITY, FinchVideo.Videos.VIDEO + »/#», VIDEO_ID); sUriMatcher.addURI (FinchVideo.AUTHORITY, FinchVideo.Videos.THUMB + »/#», THUMB_VIDEO_ID); sUriMatcher.addURI (FinchVideo.AUTHORITY, FinchVideo.Videos.THUMB + »/*», THUMB_ID); }
/** uri for querying video, expects appending keywords. */ private static final String QUERY_URI = «http://gdata.youtube.com/feeds/api/videos?» + «max-results=15&format=1&q=»;
private DatabaseHelper mOpenHelper; private SQLiteDatabase mDb;
private static class DatabaseHelper extends SQLiteOpenHelper { private DatabaseHelper (Context context, String name, SQLiteDatabase.CursorFactory factory) { super (context, name, factory, DATABASE_VERSION); }
@Override public void onCreate (SQLiteDatabase sqLiteDatabase) { createTable (sqLiteDatabase); }
private void createTable (SQLiteDatabase sqLiteDatabase) { String createvideoTable = «CREATE TABLE » + VIDEOS_TABLE_NAME + » (» + BaseColumns._ID + » INTEGER PRIMARY KEY AUTOINCREMENT,» + FinchVideo.Videos.TITLE + » TEXT,» + FinchVideo.Videos.DESCRIPTION + » TEXT,» + FinchVideo.Videos.THUMB_URI_NAME + » TEXT,» + FinchVideo.Videos.THUMB_WIDTH_NAME + » TEXT,» + FinchVideo.Videos.THUMB_HEIGHT_NAME + » TEXT,» + FinchVideo.Videos.TIMESTAMP + » TEXT,» + FinchVideo.Videos.QUERY_TEXT_NAME + » TEXT,» + FinchVideo.Videos.MEDIA_ID_NAME + » TEXT UNIQUE,» + FinchVideo.Videos.THUMB_CONTENT_URI_NAME + » TEXT UNIQUE,» + FinchVideo.Videos._DATA + » TEXT UNIQUE» + »);»; sqLiteDatabase.execSQL (createvideoTable); }
@Override public void onUpgrade (SQLiteDatabase sqLiteDatabase, int oldv, int newv) { sqLiteDatabase.execSQL («DROP TABLE IF EXISTS » + VIDEOS_TABLE_NAME + »;»); createTable (sqLiteDatabase); } }
public FinchVideoContentProvider () { }
public FinchVideoContentProvider (Context context) { }
@Override public boolean onCreate () { FileHandlerFactory fileHandlerFactory = new FileHandlerFactory (new File (getContext ().getFilesDir (), FINCH_VIDEO_FILE_CACHE)); setFileHandlerFactory (fileHandlerFactory);
mOpenHelper = new DatabaseHelper (getContext (), DATABASE_NAME, null); mDb = mOpenHelper.getWritableDatabase ();
return true; }
@Override public SQLiteDatabase getDatabase () { return mDb; }
/** * Content provider query method that converts its parameters into a YouTube * RESTful search query. * * @param uri a reference to the query for videos, the query string can * contain, «q='key_words'». The keywords are sent to the google YouTube * API where they are used to search the YouTube video database. * @param projection * @param where not used in this provider. * @param whereArgs not used in this provider. * @param sortOrder not used in this provider. * @return a cursor containing the results of a YouTube search query. */ @Override public Cursor query (Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder) { Cursor queryCursor;
int match = sUriMatcher.match (uri); switch (match) { case VIDEOS: // the query is passed out of band of other information passed // to this method — its not an argument. String queryText = uri. getQueryParameter (FinchVideo.Videos.QUERY_PARAM_NAME);
if (queryText == null) { // A null cursor is an acceptable argument to the method, // CursorAdapter.changeCursor (Cursor c), which interprets // the value by canceling all adapter state so that the // component for which the cursor is adapting data will // display no content. return null; }
String select = FinchVideo.Videos.QUERY_TEXT_NAME + » = '» + queryText + »'»;
// quickly return already matching data queryCursor = mDb.query (VIDEOS_TABLE_NAME, projection, select, whereArgs, null, null, sortOrder);
// make the cursor observe the requested query queryCursor.setNotificationUri ( getContext ().getContentResolver (), uri);
/** * Always try to update results with the latest data from the * network. * * Spawning an asynchronous load task thread, guarantees that * the load has no chance to block any content provider method, * and therefore no chance to block the UI thread. * * While the request loads, we return the cursor with existing * data to the client. * * If the existing cursor is empty, the UI will render no * content until it receives URI notification. * * Content updates that arrive when the asynchronous network * request completes will appear in the already returned cursor, * since that cursor query will match that of * newly arrived items. */ if (!».equals (queryText)) { asyncQueryRequest (queryText, QUERY_URI + encode (queryText)); } break; case VIDEO_ID: case THUMB_VIDEO_ID: long videoID = ContentUris.parseId (uri); queryCursor = mDb.query (VIDEOS_TABLE_NAME, projection, BaseColumns._ID + » = » + videoID, whereArgs, null, null, null); queryCursor.setNotificationUri ( getContext ().getContentResolver (), uri); break; case THUMB_ID: String uriString = uri.toString (); int lastSlash = uriString.lastIndexOf (»/»); String mediaID = uriString.substring (lastSlash + 1);
queryCursor = mDb.query (VIDEOS_TABLE_NAME, projection, FinchVideo.Videos.MEDIA_ID_NAME + » = » + mediaID, whereArgs, null, null, null); queryCursor.setNotificationUri ( getContext ().getContentResolver (), uri); break;
default: throw new IllegalArgumentException («unsupported uri:» + QUERY_URI); }
return queryCursor; }
/** * Provides a handler that can parse YouTube gData RSS content. * * @param requestTag unique tag identifying this request. * @return a YouTubeHandler object. */ @Override protected ResponseHandler newResponseHandler (String requestTag) { return new YouTubeHandler (this, requestTag); }
/** * Provides read only access to files that have been downloaded and stored * in the provider cache. Specifically, in this provider, clients can * access the files of downloaded thumbnail images. */ @Override public ParcelFileDescriptor openFile (Uri uri, String mode) throws FileNotFoundException { // only support read only files if (! «r».equals (mode.toLowerCase ())) { throw new FileNotFoundException («Unsupported mode,» + mode + », for uri:» + uri); }
return openFileHelper (uri, mode); }
@Override public String getType (Uri uri) { switch (sUriMatcher.match (uri)) { case VIDEOS: return FinchVideo.Videos.CONTENT_TYPE;
case VIDEO_ID: return FinchVideo.Videos.CONTENT_VIDEO_TYPE;
case THUMB_ID: return FinchVideo.Videos.CONTENT_THUMB_TYPE;
default: throw new IllegalArgumentException («Unknown video type:» + uri); } }
@Override public Uri insert (Uri uri, ContentValues initialValues) { // Validate the requested uri if (sUriMatcher.match (uri) != VIDEOS) { throw new IllegalArgumentException («Unknown URI » + uri); }
ContentValues values; if (initialValues!= null) { values = new ContentValues (initialValues); } else { values = new ContentValues (); }
SQLiteDatabase db = getDatabase (); return insert (uri, initialValues, db); }
private void verifyValues (ContentValues values) { if (! values.containsKey (FinchVideo.Videos.TITLE)) { Resources r = Resources.getSystem (); values.put (FinchVideo.Videos.TITLE, r.getString (android.R.string.untitled)); }
if (! values.containsKey (FinchVideo.Videos.DESCRIPTION)) { Resources r = Resources.getSystem (); values.put (FinchVideo.Videos.DESCRIPTION, r.getString (android.R.string.untitled)); }
if (! values.containsKey (FinchVideo.Videos.THUMB_URI_NAME)) { throw new IllegalArgumentException («Thumb uri not specified:» + values); }
if (! values.containsKey (FinchVideo.Videos.THUMB_WIDTH_NAME)) { throw new IllegalArgumentException («Thumb width not specified:» + values); }
if (! values.containsKey (FinchVideo.Videos.THUMB_HEIGHT_NAME)) { throw new IllegalArgumentException («Thumb height not specified:» + values); }
// Make sure that the fields are all set if (! values.containsKey (FinchVideo.Videos.TIMESTAMP)) { Long now = System.currentTimeMillis (); values.put (FinchVideo.Videos.TIMESTAMP, now); }
if (! values.containsKey (FinchVideo.Videos.QUERY_TEXT_NAME)) { throw new IllegalArgumentException («Query Text not specified:» + values); }
if (! values.containsKey (FinchVideo.Videos.MEDIA_ID_NAME)) { throw new IllegalArgumentException («Media ID not specified:» + values); } }
/** * The delegate insert method, which also takes a database parameter. Note * that this method is a direct implementation of a content provider method. */ @Override public Uri insert (Uri uri, ContentValues values, SQLiteDatabase db) { verifyValues (values);
// Validate the requested uri int m = sUriMatcher.match (uri); if (m!= VIDEOS) { throw new IllegalArgumentException («Unknown URI » + uri); }
// insert the values into a new database row String mediaID = (String) values.get (FinchVideo.Videos.MEDIA_ID_NAME);
Long rowID = mediaExists (db, mediaID); if (rowID == null) { long time = System.currentTimeMillis (); values.put (FinchVideo.Videos.TIMESTAMP, time); long rowId = db.insert (VIDEOS_TABLE_NAME, FinchVideo.Videos.VIDEO, values); if (rowId >= 0) { Uri insertUri = ContentUris.withAppendedId ( FinchVideo.Videos.CONTENT_URI, rowId); getContext ().getContentResolver ().notifyChange (insertUri, null); return insertUri; }
throw new IllegalStateException («could not insert » + «content values:» + values); }
return ContentUris.withAppendedId (FinchVideo.Videos.CONTENT_URI, rowID); }
private Long mediaExists (SQLiteDatabase db, String mediaID) { Cursor cursor = null; Long rowID = null; try { cursor = db.query (VIDEOS_TABLE_NAME, null, FinchVideo.Videos.MEDIA_ID_NAME + » = '» + mediaID + »'», null, null, null, null); if (cursor.moveToFirst ()) { rowID = cursor.getLong (FinchVideo.ID_COLUMN); } } finally { if (cursor!= null) { cursor.close (); } } return rowID; }
@Override public int delete (Uri uri, String where, String[] whereArgs) { int match = sUriMatcher.match (uri); int affected;
SQLiteDatabase db = mOpenHelper.getWritableDatabase (); switch (match) { case VIDEOS: affected = db.delete (VIDEOS_TABLE_NAME, (! TextUtils.isEmpty (where) ? » AND (» + where + ')' :»), whereArgs); break; case VIDEO_ID: long videoId = ContentUris.parseId (uri); affected = db.delete (VIDEOS_TABLE_NAME, BaseColumns._ID + »=» + videoId + (! TextUtils.isEmpty (where) ? » AND (» + where + ')' :»), whereArgs); getContext ().getContentResolver ().notifyChange (uri, null);
break; default: throw new IllegalArgumentException («unknown video element:» + uri); }
return affected; }
@Override public int update (Uri uri, ContentValues values, String where, String[] whereArgs) { getContext ().getContentResolver ().notifyChange (uri, null);
SQLiteDatabase db = mOpenHelper.getWritableDatabase (); int count; switch (sUriMatcher.match (uri)) { case VIDEOS: count = db.update (VIDEOS_TABLE_NAME, values, where, whereArgs); break;
case VIDEO_ID: String videoId = uri.getPathSegments ().get (1); count = db.update (VIDEOS_TABLE_NAME, values, BaseColumns._ID + »=» + videoId + (! TextUtils.isEmpty (where) ? » AND (» + where + ')' :»), whereArgs); break;
default: throw new IllegalArgumentException («Unknown URI » + uri); }
getContext ().getContentResolver ().notifyChange (uri, null); return count; } }