diff -r 000000000000 -r 6474c204b198 mobile/android/base/db/ReadingListProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/db/ReadingListProvider.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,247 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.db; + +import org.mozilla.gecko.db.BrowserContract.ReadingListItems; +import org.mozilla.gecko.sync.Utils; + +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.text.TextUtils; + +public class ReadingListProvider extends SharedBrowserDatabaseProvider { + static final String TABLE_READING_LIST = ReadingListItems.TABLE_NAME; + + static final int ITEMS = 101; + static final int ITEMS_ID = 102; + static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); + + static { + URI_MATCHER.addURI(BrowserContract.READING_LIST_AUTHORITY, "items", ITEMS); + URI_MATCHER.addURI(BrowserContract.READING_LIST_AUTHORITY, "items/#", ITEMS_ID); + } + + /** + * Updates items that match the selection criteria. If no such items is found + * one is inserted with the attributes passed in. Returns 0 if no item updated. + * + * @return Number of items updated or inserted + */ + public int updateOrInsertItem(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + int updated = updateItems(uri, values, selection, selectionArgs); + if (updated <= 0) { + updated = insertItem(uri, values) != -1 ? 1 : 0; + } + return updated; + } + + /** + * Updates items that match the selection criteria. + * + * @return Number of items updated or inserted + */ + public int updateItems(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + trace("Updating ReadingListItems on URI: " + uri); + final SQLiteDatabase db = getWritableDatabase(uri); + if (!values.containsKey(ReadingListItems.DATE_MODIFIED)) { + values.put(ReadingListItems.DATE_MODIFIED, System.currentTimeMillis()); + } + return db.update(TABLE_READING_LIST, values, selection, selectionArgs); + } + + /** + * Inserts a new item into the DB. DATE_CREATED, DATE_MODIFIED + * and GUID fields are generated if they are not specified. + * + * @return ID of the newly inserted item + */ + long insertItem(Uri uri, ContentValues values) { + long now = System.currentTimeMillis(); + if (!values.containsKey(ReadingListItems.DATE_CREATED)) { + values.put(ReadingListItems.DATE_CREATED, now); + } + + if (!values.containsKey(ReadingListItems.DATE_MODIFIED)) { + values.put(ReadingListItems.DATE_MODIFIED, now); + } + + if (!values.containsKey(ReadingListItems.GUID)) { + values.put(ReadingListItems.GUID, Utils.generateGuid()); + } + + String url = values.getAsString(ReadingListItems.URL); + debug("Inserting item in database with URL: " + url); + return getWritableDatabase(uri) + .insertOrThrow(TABLE_READING_LIST, null, values); + } + + /** + * Deletes items. Item is marked as 'deleted' so that sync can + * detect the change. + * + * @return Number of deleted items + */ + int deleteItems(Uri uri, String selection, String[] selectionArgs) { + debug("Deleting item entry for URI: " + uri); + final SQLiteDatabase db = getWritableDatabase(uri); + + if (isCallerSync(uri)) { + return db.delete(TABLE_READING_LIST, selection, selectionArgs); + } + + debug("Marking item entry as deleted for URI: " + uri); + ContentValues values = new ContentValues(); + values.put(ReadingListItems.IS_DELETED, 1); + + cleanUpSomeDeletedRecords(uri, TABLE_READING_LIST); + return updateItems(uri, values, selection, selectionArgs); + } + + @Override + @SuppressWarnings("fallthrough") + public int updateInTransaction(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + trace("Calling update in transaction on URI: " + uri); + + int updated = 0; + int match = URI_MATCHER.match(uri); + + switch (match) { + case ITEMS_ID: + debug("Update on ITEMS_ID: " + uri); + selection = DBUtils.concatenateWhere(selection, TABLE_READING_LIST + "._id = ?"); + selectionArgs = DBUtils.appendSelectionArgs(selectionArgs, + new String[] { Long.toString(ContentUris.parseId(uri)) }); + + case ITEMS: { + debug("Updating ITEMS: " + uri); + updated = shouldUpdateOrInsert(uri) ? + updateOrInsertItem(uri, values, selection, selectionArgs) : + updateItems(uri, values, selection, selectionArgs); + break; + } + + default: + throw new UnsupportedOperationException("Unknown update URI " + uri); + } + + debug("Updated " + updated + " rows for URI: " + uri); + return updated; + } + + + @Override + @SuppressWarnings("fallthrough") + public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) { + trace("Calling delete in transaction on URI: " + uri); + + int numDeleted = 0; + int match = URI_MATCHER.match(uri); + + switch (match) { + case ITEMS_ID: + debug("Deleting on ITEMS_ID: " + uri); + selection = DBUtils.concatenateWhere(selection, TABLE_READING_LIST + "._id = ?"); + selectionArgs = DBUtils.appendSelectionArgs(selectionArgs, + new String[] { Long.toString(ContentUris.parseId(uri)) }); + + case ITEMS: + debug("Deleting ITEMS: " + uri); + numDeleted = deleteItems(uri, selection, selectionArgs); + break; + + default: + throw new UnsupportedOperationException("Unknown update URI " + uri); + } + + debug("Deleted " + numDeleted + " rows for URI: " + uri); + return numDeleted; + } + + @Override + public Uri insertInTransaction(Uri uri, ContentValues values) { + trace("Calling insert in transaction on URI: " + uri); + long id = -1; + int match = URI_MATCHER.match(uri); + + switch (match) { + case ITEMS: + trace("Insert on ITEMS: " + uri); + id = insertItem(uri, values); + break; + + default: + throw new UnsupportedOperationException("Unknown insert URI " + uri); + } + + debug("Inserted ID in database: " + id); + + if (id >= 0) { + return ContentUris.withAppendedId(uri, id); + } + + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + String groupBy = null; + SQLiteDatabase db = getReadableDatabase(uri); + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT); + + final int match = URI_MATCHER.match(uri); + switch (match) { + case ITEMS_ID: + trace("Query on ITEMS_ID: " + uri); + selection = DBUtils.concatenateWhere(selection, ReadingListItems._ID + " = ?"); + selectionArgs = DBUtils.appendSelectionArgs(selectionArgs, + new String[] { Long.toString(ContentUris.parseId(uri)) }); + + case ITEMS: + trace("Query on ITEMS: " + uri); + if (!shouldShowDeleted(uri)) + selection = DBUtils.concatenateWhere(ReadingListItems.IS_DELETED + " = 0", selection); + break; + + default: + throw new UnsupportedOperationException("Unknown query URI " + uri); + } + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = ReadingListItems.DEFAULT_SORT_ORDER; + } + + trace("Running built query."); + qb.setTables(TABLE_READING_LIST); + Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, null, sortOrder, limit); + cursor.setNotificationUri(getContext().getContentResolver(), uri); + + return cursor; + } + + @Override + public String getType(Uri uri) { + trace("Getting URI type: " + uri); + + final int match = URI_MATCHER.match(uri); + switch (match) { + case ITEMS: + trace("URI is ITEMS: " + uri); + return ReadingListItems.CONTENT_TYPE; + + case ITEMS_ID: + trace("URI is ITEMS_ID: " + uri); + return ReadingListItems.CONTENT_ITEM_TYPE; + } + + debug("URI has unrecognized type: " + uri); + return null; + } +}