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