/* * Copyright (C) 2008 Torgny Bjers * * 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.xorcode.andtweet; import java.io.File; import java.util.Arrays; import java.util.HashMap; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDiskIOException; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.Environment; import android.text.TextUtils; import android.util.Log; import com.xorcode.andtweet.data.AndTweetDatabase; import com.xorcode.andtweet.data.AndTweetDatabase.DirectMessages; import com.xorcode.andtweet.data.AndTweetDatabase.Tweets; import com.xorcode.andtweet.data.AndTweetDatabase.Users; import com.xorcode.andtweet.data.AndTweetPreferences; /** * Database provider for the AndTweetDatabase database. * * @author torgny.bjers */ public class AndTweetProvider extends ContentProvider { private static final String TAG = AndTweetProvider.class.getSimpleName(); private static final String DATABASE_DIRECTORY = "andtweet"; private static final String DATABASE_NAME = "andtweet.sqlite"; private static final int DATABASE_VERSION = 8; private static final String TWEETS_TABLE_NAME = "tweets"; private static final String USERS_TABLE_NAME = "users"; private static final String DIRECTMESSAGES_TABLE_NAME = "directmessages"; private static final UriMatcher sUriMatcher; private DatabaseHelper mOpenHelper; private static HashMap<String, String> sTweetsProjectionMap; private static HashMap<String, String> sUsersProjectionMap; private static HashMap<String, String> sDirectMessagesProjectionMap; private static final int TWEETS = 1; private static final int TWEETS_COUNT = 8; private static final int TWEET_ID = 2; private static final int USERS = 3; private static final int USER_ID = 4; private static final int DIRECTMESSAGES = 5; private static final int DIRECTMESSAGE_ID = 6; private static final int DIRECTMESSAGES_COUNT = 9; private static final int TWEET_SEARCH = 7; /** * Database helper for AndTweetProvider. * * @author torgny.bjers */ private static class DatabaseHelper extends SQLiteOpenHelper { private static final String TAG = AndTweetProvider.class.getSimpleName(); private SQLiteDatabase mDatabase; private boolean mIsInitializing = false; private boolean mUseExternalStorage = false; DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); AndTweetPreferences.initialize(context, this); SharedPreferences sp = AndTweetPreferences.getDefaultSharedPreferences(); mUseExternalStorage = sp.getBoolean("storage_use_external", false); } @Override public synchronized SQLiteDatabase getWritableDatabase() { if (!mUseExternalStorage) { return super.getWritableDatabase(); } if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { throw new SQLiteDiskIOException("Cannot access external storage: not mounted"); } if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { throw new SQLiteDiskIOException("Cannot access external storage: mounted read only"); } if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) { return mDatabase; } if (mIsInitializing) { throw new IllegalStateException("getWritableDatabase called recursively"); } boolean success = false; SQLiteDatabase db = null; try { mIsInitializing = true; File storage = Environment.getExternalStorageDirectory(); String path = storage.getAbsolutePath(); File dir = new File(path, DATABASE_DIRECTORY); dir.mkdir(); File file = new File(dir.getAbsolutePath(), DATABASE_NAME); db = SQLiteDatabase.openOrCreateDatabase(file, null); int version = db.getVersion(); if (version != DATABASE_VERSION) { db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { onUpgrade(db, version, DATABASE_VERSION); } db.setVersion(DATABASE_VERSION); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } onOpen(db); success = true; return db; } finally { mIsInitializing = false; if (success) { if (mDatabase != null) { try { mDatabase.close(); } catch (Exception e) { } } mDatabase = db; } else { if (db != null) db.close(); } } } @Override public synchronized SQLiteDatabase getReadableDatabase() { if (!mUseExternalStorage) { return super.getReadableDatabase(); } if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { throw new SQLiteDiskIOException("Cannot access external storage: not mounted"); } if (mDatabase != null && mDatabase.isOpen()) { return mDatabase; } if (mIsInitializing) { throw new IllegalStateException("getReadableDatabase called recursively"); } try { return getWritableDatabase(); } catch (SQLiteException e) { Log.e(TAG, "Couldn't open " + DATABASE_NAME + " for writing (will try read-only):", e); } SQLiteDatabase db = null; try { mIsInitializing = true; File storage = Environment.getExternalStorageDirectory(); String path = storage.getAbsolutePath(); File dir = new File(path, DATABASE_DIRECTORY); dir.mkdir(); File file = new File(dir.getAbsolutePath(), DATABASE_NAME); db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY); if (db.getVersion() != DATABASE_VERSION) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + DATABASE_VERSION + ": " + file.getAbsolutePath()); } onOpen(db); Log.w(TAG, "Opened " + DATABASE_NAME + " in read-only mode"); mDatabase = db; return mDatabase; } finally { mIsInitializing = false; if (db != null && db != mDatabase) db.close(); } } @Override public synchronized void close() { super.close(); if (mUseExternalStorage && mDatabase != null && mDatabase.isOpen()) { mDatabase.close(); mDatabase = null; } } @Override public void onCreate(SQLiteDatabase db) { Log.d(TAG, "Creating tables"); db.execSQL("CREATE TABLE " + TWEETS_TABLE_NAME + " (" + Tweets._ID + " INTEGER PRIMARY KEY," + Tweets.AUTHOR_ID + " TEXT," + Tweets.MESSAGE + " TEXT," + Tweets.SOURCE + " TEXT," + Tweets.TWEET_TYPE + " INTEGER," + Tweets.IN_REPLY_TO_STATUS_ID + " INTEGER," + Tweets.IN_REPLY_TO_AUTHOR_ID + " TEXT," + Tweets.FAVORITED + " INTEGER," + Tweets.SENT_DATE + " INTEGER," + Tweets.CREATED_DATE + " INTEGER" + ");"); db.execSQL("CREATE TABLE " + DIRECTMESSAGES_TABLE_NAME + " (" + DirectMessages._ID + " INTEGER PRIMARY KEY," + DirectMessages.AUTHOR_ID + " TEXT," + DirectMessages.MESSAGE + " TEXT," + DirectMessages.SENT_DATE + " INTEGER," + DirectMessages.CREATED_DATE + " INTEGER" + ");"); db.execSQL("CREATE TABLE " + USERS_TABLE_NAME + " (" + Users._ID + " INTEGER PRIMARY KEY," + Users.AUTHOR_ID + " TEXT," + Users.FOLLOWING + " INTEGER," + Users.AVATAR_IMAGE + " BLOB," + Users.CREATED_DATE + " INTEGER," + Users.MODIFIED_DATE + " INTEGER" + ");"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.d(TAG, "Upgrading database from version " + oldVersion + " to version " + newVersion); if (oldVersion < 7) { db.beginTransaction(); try { /* * Upgrading tweets table: - Add column TWEET_TYPE */ db.execSQL("CREATE TEMPORARY TABLE " + TWEETS_TABLE_NAME + "_backup (" + Tweets._ID + " INTEGER PRIMARY KEY," + Tweets.AUTHOR_ID + " TEXT," + Tweets.MESSAGE + " TEXT," + Tweets.SOURCE + " TEXT," + Tweets.IN_REPLY_TO_STATUS_ID + " INTEGER," + Tweets.IN_REPLY_TO_AUTHOR_ID + " TEXT," + Tweets.SENT_DATE + " INTEGER," + Tweets.CREATED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + TWEETS_TABLE_NAME + "_backup SELECT " + Tweets._ID + ", " + Tweets.AUTHOR_ID + ", " + Tweets.MESSAGE + ", " + Tweets.SOURCE + ", " + Tweets.IN_REPLY_TO_AUTHOR_ID + ", " + Tweets.IN_REPLY_TO_STATUS_ID + ", " + Tweets.SENT_DATE + ", " + Tweets.CREATED_DATE + " FROM " + TWEETS_TABLE_NAME + ";"); db.execSQL("DROP TABLE " + TWEETS_TABLE_NAME + ";"); db.execSQL("CREATE TABLE " + TWEETS_TABLE_NAME + " (" + Tweets._ID + " INTEGER PRIMARY KEY," + Tweets.AUTHOR_ID + " TEXT," + Tweets.MESSAGE + " TEXT," + Tweets.SOURCE + " TEXT," + Tweets.TWEET_TYPE + " INTEGER," + Tweets.IN_REPLY_TO_STATUS_ID + " INTEGER," + Tweets.IN_REPLY_TO_AUTHOR_ID + " TEXT," + Tweets.FAVORITED + " INTEGER," + Tweets.SENT_DATE + " INTEGER," + Tweets.CREATED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + TWEETS_TABLE_NAME + " SELECT " + Tweets._ID + ", " + Tweets.AUTHOR_ID + ", " + Tweets.MESSAGE + ", " + Tweets.SOURCE + ", " + Tweets.TIMELINE_TYPE_FRIENDS + ", " + Tweets.IN_REPLY_TO_AUTHOR_ID + ", " + Tweets.IN_REPLY_TO_STATUS_ID + ", null, " + Tweets.SENT_DATE + ", " + Tweets.CREATED_DATE + " FROM " + TWEETS_TABLE_NAME + "_backup;"); db.execSQL("DROP TABLE " + TWEETS_TABLE_NAME + "_backup;"); /* * Upgrading users table: - Add column AVATAR_IMAGE */ db.execSQL("CREATE TEMPORARY TABLE " + USERS_TABLE_NAME + "_backup (" + Users._ID + " INTEGER PRIMARY KEY," + Users.AUTHOR_ID + " TEXT," + Users.CREATED_DATE + " INTEGER," + Users.MODIFIED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + USERS_TABLE_NAME + "_backup SELECT " + Users._ID + ", " + Users.AUTHOR_ID + ", " + Users.CREATED_DATE + ", " + Users.MODIFIED_DATE + " FROM " + USERS_TABLE_NAME + ";"); db.execSQL("DROP TABLE " + USERS_TABLE_NAME + ";"); db.execSQL("CREATE TABLE " + USERS_TABLE_NAME + " (" + Users._ID + " INTEGER PRIMARY KEY," + Users.AUTHOR_ID + " TEXT," + Users.AVATAR_IMAGE + " BLOB," + Users.FOLLOWING + " INTEGER," + Users.CREATED_DATE + " INTEGER," + Users.MODIFIED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + USERS_TABLE_NAME + " SELECT " + Users._ID + ", " + Users.AUTHOR_ID + ", null, null, " + Users.CREATED_DATE + ", " + Users.MODIFIED_DATE + " FROM " + USERS_TABLE_NAME + "_backup;"); db.execSQL("DROP TABLE " + USERS_TABLE_NAME + "_backup;"); db.setTransactionSuccessful(); Log.d(TAG, "Successfully upgraded database from version " + oldVersion + " to version " + newVersion + "."); } catch (SQLException e) { Log.e(TAG, "Could not upgrade database from version " + oldVersion + " to version " + newVersion, e); } finally { db.endTransaction(); } } else if (oldVersion < 8) { try { /* * Upgrading users table: - Add column FOLLOWING */ db.execSQL("CREATE TEMPORARY TABLE " + USERS_TABLE_NAME + "_backup (" + Users._ID + " INTEGER PRIMARY KEY," + Users.AUTHOR_ID + " TEXT," + Users.AVATAR_IMAGE + " BLOB," + Users.CREATED_DATE + " INTEGER," + Users.MODIFIED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + USERS_TABLE_NAME + "_backup SELECT " + Users._ID + ", " + Users.AUTHOR_ID + ", " + Users.AVATAR_IMAGE + ", " + Users.CREATED_DATE + ", " + Users.MODIFIED_DATE + " FROM " + USERS_TABLE_NAME + ";"); db.execSQL("DROP TABLE " + USERS_TABLE_NAME + ";"); db.execSQL("CREATE TABLE " + USERS_TABLE_NAME + " (" + Users._ID + " INTEGER PRIMARY KEY," + Users.AUTHOR_ID + " TEXT," + Users.FOLLOWING + " INTEGER," + Users.AVATAR_IMAGE + " BLOB," + Users.CREATED_DATE + " INTEGER," + Users.MODIFIED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + USERS_TABLE_NAME + " SELECT " + Users._ID + ", " + Users.AUTHOR_ID + ", null, " + Users.AVATAR_IMAGE + ", " + Users.CREATED_DATE + ", " + Users.MODIFIED_DATE + " FROM " + USERS_TABLE_NAME + "_backup;"); db.execSQL("DROP TABLE " + USERS_TABLE_NAME + "_backup;"); /* * Upgrading tweets table: - Add column FAVORITED */ db.execSQL("CREATE TEMPORARY TABLE " + TWEETS_TABLE_NAME + "_backup (" + Tweets._ID + " INTEGER PRIMARY KEY," + Tweets.AUTHOR_ID + " TEXT," + Tweets.MESSAGE + " TEXT," + Tweets.SOURCE + " TEXT," + Tweets.TWEET_TYPE + " INTEGER," + Tweets.IN_REPLY_TO_STATUS_ID + " INTEGER," + Tweets.IN_REPLY_TO_AUTHOR_ID + " TEXT," + Tweets.SENT_DATE + " INTEGER," + Tweets.CREATED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + TWEETS_TABLE_NAME + "_backup SELECT " + Tweets._ID + ", " + Tweets.AUTHOR_ID + ", " + Tweets.MESSAGE + ", " + Tweets.SOURCE + ", " + Tweets.TWEET_TYPE + ", " + Tweets.IN_REPLY_TO_AUTHOR_ID + ", " + Tweets.IN_REPLY_TO_STATUS_ID + ", " + Tweets.SENT_DATE + ", " + Tweets.CREATED_DATE + " FROM " + TWEETS_TABLE_NAME + ";"); db.execSQL("DROP TABLE " + TWEETS_TABLE_NAME + ";"); db.execSQL("CREATE TABLE " + TWEETS_TABLE_NAME + " (" + Tweets._ID + " INTEGER PRIMARY KEY," + Tweets.AUTHOR_ID + " TEXT," + Tweets.MESSAGE + " TEXT," + Tweets.SOURCE + " TEXT," + Tweets.TWEET_TYPE + " INTEGER," + Tweets.IN_REPLY_TO_STATUS_ID + " INTEGER," + Tweets.IN_REPLY_TO_AUTHOR_ID + " TEXT," + Tweets.FAVORITED + " INTEGER," + Tweets.SENT_DATE + " INTEGER," + Tweets.CREATED_DATE + " INTEGER" + ");"); db.execSQL("INSERT INTO " + TWEETS_TABLE_NAME + " SELECT " + Tweets._ID + ", " + Tweets.AUTHOR_ID + ", " + Tweets.MESSAGE + ", " + Tweets.SOURCE + ", " + Tweets.TWEET_TYPE + ", " + Tweets.IN_REPLY_TO_AUTHOR_ID + ", " + Tweets.IN_REPLY_TO_STATUS_ID + ", null, " + Tweets.SENT_DATE + ", " + Tweets.CREATED_DATE + " FROM " + TWEETS_TABLE_NAME + "_backup;"); db.execSQL("DROP TABLE " + TWEETS_TABLE_NAME + "_backup;"); db.setTransactionSuccessful(); Log.d(TAG, "Successfully upgraded database from version " + oldVersion + " to version " + newVersion + "."); } catch (SQLException e) { Log.e(TAG, "Could not upgrade database from version " + oldVersion + " to version " + newVersion, e); } finally { db.endTransaction(); } } } } /** * @see android.content.ContentProvider#onCreate() */ @Override public boolean onCreate() { mOpenHelper = new DatabaseHelper(getContext()); return (mOpenHelper == null) ? false : true; } /** * Get type of the Uri to make sure we use the right table * * @see android.content.ContentProvider#getType(android.net.Uri) */ @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case TWEETS: case TWEET_SEARCH: case TWEETS_COUNT: return Tweets.CONTENT_TYPE; case TWEET_ID: return Tweets.CONTENT_ITEM_TYPE; case DIRECTMESSAGES: return DirectMessages.CONTENT_TYPE; case DIRECTMESSAGE_ID: return DirectMessages.CONTENT_ITEM_TYPE; case USERS: return Users.CONTENT_TYPE; case USER_ID: return Users.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } } /** * Delete a record from the database. * * @see android.content.ContentProvider#delete(android.net.Uri, * java.lang.String, java.lang.String[]) */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case TWEETS: count = db.delete(TWEETS_TABLE_NAME, selection, selectionArgs); break; case TWEET_ID: String tweetId = uri.getPathSegments().get(1); count = db.delete(TWEETS_TABLE_NAME, Tweets._ID + "=" + tweetId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; case DIRECTMESSAGES: count = db.delete(DIRECTMESSAGES_TABLE_NAME, selection, selectionArgs); break; case DIRECTMESSAGE_ID: String messageId = uri.getPathSegments().get(1); count = db.delete(DIRECTMESSAGES_TABLE_NAME, DirectMessages._ID + "=" + messageId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; case USERS: count = db.delete(USERS_TABLE_NAME, selection, selectionArgs); break; case USER_ID: String userId = uri.getPathSegments().get(1); count = db.delete(USERS_TABLE_NAME, Users._ID + "=" + userId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } /** * Insert a new record into the database. * * @see android.content.ContentProvider#insert(android.net.Uri, * android.content.ContentValues) */ @Override public Uri insert(Uri uri, ContentValues initialValues) { ContentValues values; long rowId; // 2010-07-21 yvolk: "now" is calculated exactly like it is in other // parts of the code Long now = System.currentTimeMillis(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); String table; String nullColumnHack; Uri contentUri; if (initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } switch (sUriMatcher.match(uri)) { case TWEETS: table = TWEETS_TABLE_NAME; nullColumnHack = Tweets.MESSAGE; contentUri = Tweets.CONTENT_URI; if (values.containsKey(Tweets.CREATED_DATE) == false) values.put(Tweets.CREATED_DATE, now); if (values.containsKey(Tweets.SENT_DATE) == false) values.put(Tweets.SENT_DATE, now); if (values.containsKey(Tweets.AUTHOR_ID) == false) values.put(Tweets.AUTHOR_ID, ""); if (values.containsKey(Tweets.MESSAGE) == false) values.put(Tweets.MESSAGE, ""); if (values.containsKey(Tweets.SOURCE) == false) values.put(Tweets.SOURCE, ""); if (values.containsKey(Tweets.TWEET_TYPE) == false) values.put(Tweets.TWEET_TYPE, Tweets.TIMELINE_TYPE_FRIENDS); if (values.containsKey(Tweets.IN_REPLY_TO_AUTHOR_ID) == false) values.put(Tweets.IN_REPLY_TO_AUTHOR_ID, ""); if (values.containsKey(Tweets.FAVORITED) == false) values.put(Tweets.FAVORITED, 0); break; case DIRECTMESSAGES: table = DIRECTMESSAGES_TABLE_NAME; nullColumnHack = DirectMessages.MESSAGE; contentUri = DirectMessages.CONTENT_URI; if (values.containsKey(DirectMessages.CREATED_DATE) == false) values.put(DirectMessages.CREATED_DATE, now); if (values.containsKey(DirectMessages.SENT_DATE) == false) values.put(DirectMessages.SENT_DATE, now); if (values.containsKey(DirectMessages.AUTHOR_ID) == false) values.put(DirectMessages.AUTHOR_ID, ""); if (values.containsKey(DirectMessages.MESSAGE) == false) values.put(DirectMessages.MESSAGE, ""); break; case USERS: table = USERS_TABLE_NAME; nullColumnHack = Users.AUTHOR_ID; contentUri = Users.CONTENT_URI; if (values.containsKey(Users.MODIFIED_DATE) == false) values.put(Users.MODIFIED_DATE, now); if (values.containsKey(Users.CREATED_DATE) == false) values.put(Users.CREATED_DATE, now); if (values.containsKey(Users.AUTHOR_ID) == false) values.put(Users.AUTHOR_ID, ""); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } rowId = db.insert(table, nullColumnHack, values); if (rowId > 0) { Uri newUri = ContentUris.withAppendedId(contentUri, rowId); return newUri; } throw new SQLException("Failed to insert row into " + uri); } /** * Get a cursor to the database * * @see android.content.ContentProvider#query(android.net.Uri, * java.lang.String[], java.lang.String, java.lang.String[], * java.lang.String) */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String sql = ""; int matchedCode = sUriMatcher.match(uri); switch (matchedCode) { case TWEETS: qb.setTables(TWEETS_TABLE_NAME); qb.setProjectionMap(sTweetsProjectionMap); break; case TWEETS_COUNT: sql = "SELECT count(*) FROM " + TWEETS_TABLE_NAME; if (selection != null && selection.length() > 0) { sql += " WHERE " + selection; } break; case TWEET_ID: qb.setTables(TWEETS_TABLE_NAME); qb.setProjectionMap(sTweetsProjectionMap); qb.appendWhere(Tweets._ID + "=" + uri.getPathSegments().get(1)); break; case TWEET_SEARCH: qb.setTables(TWEETS_TABLE_NAME); qb.setProjectionMap(sTweetsProjectionMap); String s1 = uri.getLastPathSegment(); if (s1 != null) { // These two lines don't work: // qb.appendWhere(Tweets.AUTHOR_ID + " LIKE '%" + s1 + // "%' OR " + Tweets.MESSAGE + " LIKE '%" + s1 + "%'"); // qb.appendWhere(Tweets.AUTHOR_ID + " LIKE \"%" + s1 + // "%\" OR " + Tweets.MESSAGE + " LIKE \"%" + s1 + "%\""); // ...so we have to use selectionArgs // 1. This works: // qb.appendWhere(Tweets.AUTHOR_ID + " LIKE ? OR " + // Tweets.MESSAGE + " LIKE ?"); // 2. This works also, but yvolk likes it more :-) if (selection != null && selection.length() > 0) { selection = " AND (" + selection + ")"; } else { selection = ""; } selection = "(" + Tweets.AUTHOR_ID + " LIKE ? OR " + Tweets.MESSAGE + " LIKE ?)" + selection; selectionArgs = addBeforeArray(selectionArgs, "%" + s1 + "%"); selectionArgs = addBeforeArray(selectionArgs, "%" + s1 + "%"); } break; case DIRECTMESSAGES: qb.setTables(DIRECTMESSAGES_TABLE_NAME); qb.setProjectionMap(sDirectMessagesProjectionMap); break; case DIRECTMESSAGE_ID: qb.setTables(DIRECTMESSAGES_TABLE_NAME); qb.setProjectionMap(sDirectMessagesProjectionMap); qb.appendWhere(DirectMessages._ID + "=" + uri.getPathSegments().get(1)); break; case DIRECTMESSAGES_COUNT: sql = "SELECT count(*) FROM " + DIRECTMESSAGES_TABLE_NAME; if (selection != null && selection.length() > 0) { sql += " WHERE " + selection; } break; case USERS: qb.setTables(USERS_TABLE_NAME); qb.setProjectionMap(sUsersProjectionMap); break; case USER_ID: qb.setTables(USERS_TABLE_NAME); qb.setProjectionMap(sUsersProjectionMap); qb.appendWhere(Users._ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI \"" + uri + "\"; matchedCode=" + matchedCode); } // If no sort order is specified use the default String orderBy; if (TextUtils.isEmpty(sortOrder)) { switch (matchedCode) { case TWEETS: case TWEET_ID: orderBy = Tweets.DEFAULT_SORT_ORDER; break; case TWEETS_COUNT: case DIRECTMESSAGES_COUNT: orderBy = ""; break; case DIRECTMESSAGES: case DIRECTMESSAGE_ID: orderBy = DirectMessages.DEFAULT_SORT_ORDER; break; case USERS: case USER_ID: orderBy = Users.DEFAULT_SORT_ORDER; break; default: throw new IllegalArgumentException("Unknown URI \"" + uri + "\"; matchedCode=" + matchedCode); } } else { orderBy = sortOrder; } // Get the database and run the query SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = null; boolean logQuery = Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE); try { if (sql.length() > 0) { c = db.rawQuery(sql, selectionArgs); } else { c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); } } catch (Exception e) { logQuery = true; Log.e(TAG, "Database query failed"); e.printStackTrace(); } if (logQuery) { if (sql.length() > 0) { Log.v(TAG, "query, SQL=\"" + sql + "\""); if (selectionArgs != null && selectionArgs.length > 0) { Log.v(TAG, "; selectionArgs=" + Arrays.toString(selectionArgs)); } } else { Log.v(TAG, "query, uri=" + uri + "; projection=" + Arrays.toString(projection)); Log.v(TAG, "; selection=" + selection); Log.v(TAG, "; selectionArgs=" + Arrays.toString(selectionArgs) + "; sortOrder=" + sortOrder); Log.v(TAG, "; qb.getTables=" + qb.getTables() + "; orderBy=" + orderBy); } } if (c != null) { // Tell the cursor what Uri to watch, so it knows when its source data // changes c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } private static String[] addBeforeArray(String[] array, String s) { int length = 0; if (array != null) { length = array.length; } String ans[] = new String[length + 1]; if (length > 0) { System.arraycopy(array, 0, ans, 1, array.length); } ans[0] = s; return ans; } /** * Update a record in the database * * @see android.content.ContentProvider#update(android.net.Uri, * android.content.ContentValues, java.lang.String, java.lang.String[]) */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case TWEETS: count = db.update(TWEETS_TABLE_NAME, values, selection, selectionArgs); break; case TWEET_ID: String noteId = uri.getPathSegments().get(1); count = db.update(TWEETS_TABLE_NAME, values, Tweets._ID + "=" + noteId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; case DIRECTMESSAGES: count = db.update(DIRECTMESSAGES_TABLE_NAME, values, selection, selectionArgs); break; case DIRECTMESSAGE_ID: String messageId = uri.getPathSegments().get(1); count = db.update(DIRECTMESSAGES_TABLE_NAME, values, DirectMessages._ID + "=" + messageId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; case USERS: count = db.update(USERS_TABLE_NAME, values, selection, selectionArgs); break; case USER_ID: String userId = uri.getPathSegments().get(1); count = db.update(USERS_TABLE_NAME, values, Users._ID + "=" + userId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI \"" + uri + "\""); } return count; } // Static Definitions for UriMatcher and Projection Maps static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, TWEETS_TABLE_NAME, TWEETS); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, TWEETS_TABLE_NAME + "/#", TWEET_ID); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, TWEETS_TABLE_NAME + "/search/*", TWEET_SEARCH); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, TWEETS_TABLE_NAME + "/count", TWEETS_COUNT); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, USERS_TABLE_NAME, USERS); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, USERS_TABLE_NAME + "/#", USER_ID); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, DIRECTMESSAGES_TABLE_NAME, DIRECTMESSAGES); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, DIRECTMESSAGES_TABLE_NAME + "/#", DIRECTMESSAGE_ID); sUriMatcher.addURI(AndTweetDatabase.AUTHORITY, DIRECTMESSAGES_TABLE_NAME + "/count", DIRECTMESSAGES_COUNT); sTweetsProjectionMap = new HashMap<String, String>(); sTweetsProjectionMap.put(Tweets._ID, Tweets._ID); sTweetsProjectionMap.put(Tweets.AUTHOR_ID, Tweets.AUTHOR_ID); sTweetsProjectionMap.put(Tweets.MESSAGE, Tweets.MESSAGE); sTweetsProjectionMap.put(Tweets.SOURCE, Tweets.SOURCE); sTweetsProjectionMap.put(Tweets.TWEET_TYPE, Tweets.TWEET_TYPE); sTweetsProjectionMap.put(Tweets.IN_REPLY_TO_STATUS_ID, Tweets.IN_REPLY_TO_STATUS_ID); sTweetsProjectionMap.put(Tweets.IN_REPLY_TO_AUTHOR_ID, Tweets.IN_REPLY_TO_AUTHOR_ID); sTweetsProjectionMap.put(Tweets.FAVORITED, Tweets.FAVORITED); sTweetsProjectionMap.put(Tweets.SENT_DATE, Tweets.SENT_DATE); sTweetsProjectionMap.put(Tweets.CREATED_DATE, Tweets.CREATED_DATE); sDirectMessagesProjectionMap = new HashMap<String, String>(); sDirectMessagesProjectionMap.put(DirectMessages._ID, DirectMessages._ID); sDirectMessagesProjectionMap.put(DirectMessages.AUTHOR_ID, DirectMessages.AUTHOR_ID); sDirectMessagesProjectionMap.put(DirectMessages.MESSAGE, DirectMessages.MESSAGE); sDirectMessagesProjectionMap.put(DirectMessages.SENT_DATE, DirectMessages.SENT_DATE); sDirectMessagesProjectionMap.put(DirectMessages.CREATED_DATE, DirectMessages.CREATED_DATE); sUsersProjectionMap = new HashMap<String, String>(); sUsersProjectionMap.put(Users._ID, Users._ID); sUsersProjectionMap.put(Users.AUTHOR_ID, Users.AUTHOR_ID); sUsersProjectionMap.put(Users.AVATAR_IMAGE, Users.AVATAR_IMAGE); sUsersProjectionMap.put(Users.CREATED_DATE, Users.CREATED_DATE); sUsersProjectionMap.put(Users.MODIFIED_DATE, Users.MODIFIED_DATE); } }