summaryrefslogtreecommitdiffstats
path: root/src/com/android/photos/data/SQLiteContentProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/photos/data/SQLiteContentProvider.java')
-rw-r--r--src/com/android/photos/data/SQLiteContentProvider.java265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/com/android/photos/data/SQLiteContentProvider.java b/src/com/android/photos/data/SQLiteContentProvider.java
new file mode 100644
index 000000000..daffa6e79
--- /dev/null
+++ b/src/com/android/photos/data/SQLiteContentProvider.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2013 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.android.photos.data;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * General purpose {@link ContentProvider} base class that uses SQLiteDatabase
+ * for storage.
+ */
+public abstract class SQLiteContentProvider extends ContentProvider {
+
+ @SuppressWarnings("unused")
+ private static final String TAG = "SQLiteContentProvider";
+
+ private SQLiteOpenHelper mOpenHelper;
+ private Set<Uri> mChangedUris;
+
+ private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
+ private static final int SLEEP_AFTER_YIELD_DELAY = 4000;
+
+ /**
+ * Maximum number of operations allowed in a batch between yield points.
+ */
+ private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500;
+
+ @Override
+ public boolean onCreate() {
+ Context context = getContext();
+ mOpenHelper = getDatabaseHelper(context);
+ mChangedUris = new HashSet<Uri>();
+ return true;
+ }
+
+ @Override
+ public void shutdown() {
+ getDatabaseHelper().close();
+ }
+
+ /**
+ * Returns a {@link SQLiteOpenHelper} that can open the database.
+ */
+ public abstract SQLiteOpenHelper getDatabaseHelper(Context context);
+
+ /**
+ * The equivalent of the {@link #insert} method, but invoked within a
+ * transaction.
+ */
+ public abstract Uri insertInTransaction(Uri uri, ContentValues values,
+ boolean callerIsSyncAdapter);
+
+ /**
+ * The equivalent of the {@link #update} method, but invoked within a
+ * transaction.
+ */
+ public abstract int updateInTransaction(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs, boolean callerIsSyncAdapter);
+
+ /**
+ * The equivalent of the {@link #delete} method, but invoked within a
+ * transaction.
+ */
+ public abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs,
+ boolean callerIsSyncAdapter);
+
+ /**
+ * Call this to add a URI to the list of URIs to be notified when the
+ * transaction is committed.
+ */
+ protected void postNotifyUri(Uri uri) {
+ synchronized (mChangedUris) {
+ mChangedUris.add(uri);
+ }
+ }
+
+ public boolean isCallerSyncAdapter(Uri uri) {
+ return false;
+ }
+
+ public SQLiteOpenHelper getDatabaseHelper() {
+ return mOpenHelper;
+ }
+
+ private boolean applyingBatch() {
+ return mApplyingBatch.get() != null && mApplyingBatch.get();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ Uri result = null;
+ boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
+ boolean applyingBatch = applyingBatch();
+ if (!applyingBatch) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ result = insertInTransaction(uri, values, callerIsSyncAdapter);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ onEndTransaction(callerIsSyncAdapter);
+ } else {
+ result = insertInTransaction(uri, values, callerIsSyncAdapter);
+ }
+ return result;
+ }
+
+ @Override
+ public int bulkInsert(Uri uri, ContentValues[] values) {
+ int numValues = values.length;
+ boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ for (int i = 0; i < numValues; i++) {
+ @SuppressWarnings("unused")
+ Uri result = insertInTransaction(uri, values[i], callerIsSyncAdapter);
+ db.yieldIfContendedSafely();
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ onEndTransaction(callerIsSyncAdapter);
+ return numValues;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ int count = 0;
+ boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
+ boolean applyingBatch = applyingBatch();
+ if (!applyingBatch) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ count = updateInTransaction(uri, values, selection, selectionArgs,
+ callerIsSyncAdapter);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ onEndTransaction(callerIsSyncAdapter);
+ } else {
+ count = updateInTransaction(uri, values, selection, selectionArgs, callerIsSyncAdapter);
+ }
+
+ return count;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ int count = 0;
+ boolean callerIsSyncAdapter = isCallerSyncAdapter(uri);
+ boolean applyingBatch = applyingBatch();
+ if (!applyingBatch) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ onEndTransaction(callerIsSyncAdapter);
+ } else {
+ count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter);
+ }
+ return count;
+ }
+
+ @Override
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws OperationApplicationException {
+ int ypCount = 0;
+ int opCount = 0;
+ boolean callerIsSyncAdapter = false;
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ mApplyingBatch.set(true);
+ final int numOperations = operations.size();
+ final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) {
+ throw new OperationApplicationException(
+ "Too many content provider operations between yield points. "
+ + "The maximum number of operations per yield point is "
+ + MAX_OPERATIONS_PER_YIELD_POINT, ypCount);
+ }
+ final ContentProviderOperation operation = operations.get(i);
+ if (!callerIsSyncAdapter && isCallerSyncAdapter(operation.getUri())) {
+ callerIsSyncAdapter = true;
+ }
+ if (i > 0 && operation.isYieldAllowed()) {
+ opCount = 0;
+ if (db.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) {
+ ypCount++;
+ }
+ }
+ results[i] = operation.apply(this, results, i);
+ }
+ db.setTransactionSuccessful();
+ return results;
+ } finally {
+ mApplyingBatch.set(false);
+ db.endTransaction();
+ onEndTransaction(callerIsSyncAdapter);
+ }
+ }
+
+ protected Set<Uri> onEndTransaction(boolean callerIsSyncAdapter) {
+ Set<Uri> changed;
+ synchronized (mChangedUris) {
+ changed = new HashSet<Uri>(mChangedUris);
+ mChangedUris.clear();
+ }
+ ContentResolver resolver = getContext().getContentResolver();
+ for (Uri uri : changed) {
+ boolean syncToNetwork = !callerIsSyncAdapter && syncToNetwork(uri);
+ notifyChange(resolver, uri, syncToNetwork);
+ }
+ return changed;
+ }
+
+ protected void notifyChange(ContentResolver resolver, Uri uri, boolean syncToNetwork) {
+ resolver.notifyChange(uri, null, syncToNetwork);
+ }
+
+ protected boolean syncToNetwork(Uri uri) {
+ return false;
+ }
+} \ No newline at end of file