Remove ImageCache from PhotoProvider in favor of MediaCache.

Change-Id: I4e75a094d1dc033ac892c2b95bdc6b6a4a165304
diff --git a/src/com/android/photos/data/MediaCache.java b/src/com/android/photos/data/MediaCache.java
index 7b5eca5..9cf69d6 100644
--- a/src/com/android/photos/data/MediaCache.java
+++ b/src/com/android/photos/data/MediaCache.java
@@ -276,7 +276,7 @@
         mCacheDir = cacheDir;
     }
 
-    private File getCacheDir() {
+    public File getCacheDir() {
         synchronized (mContext) {
             if (mCacheDir == null) {
                 String state = Environment.getExternalStorageState();
@@ -398,6 +398,10 @@
         File file = null;
         if (cachedId != null) {
             file = createCacheImagePath(cachedId);
+            if (!file.exists()) {
+                mDatabaseHelper.delete(contentUri, size, mDeleteFile);
+                file = null;
+            }
         }
         return file;
     }
@@ -479,9 +483,9 @@
         }
         size = retriever.normalizeMediaSize(uri, size);
 
-        Long cachedId = mDatabaseHelper.getCached(uri, size);
-        if (cachedId != null) {
-            addNotification(complete, createCacheImagePath(cachedId));
+        File cachedFile = getCachedFile(uri, size);
+        if (cachedFile != null) {
+            addNotification(complete, cachedFile);
             return;
         }
         String differentiator = getDifferentiator(uri.getScheme(), uri.getAuthority());
@@ -540,10 +544,9 @@
     }
 
     private void processTask(ProcessingJob job) {
-        Long cachedId = mDatabaseHelper.getCached(job.contentUri, job.size);
-        if (cachedId != null) {
-            File file = createCacheImagePath(cachedId);
-            addNotification(job.complete, file);
+        File cachedFile = getCachedFile(job.contentUri, job.size);
+        if (cachedFile != null) {
+            addNotification(job.complete, cachedFile);
             return;
         }
 
diff --git a/src/com/android/photos/data/MediaCacheDatabase.java b/src/com/android/photos/data/MediaCacheDatabase.java
index 16265b5..c92ac0f 100644
--- a/src/com/android/photos/data/MediaCacheDatabase.java
+++ b/src/com/android/photos/data/MediaCacheDatabase.java
@@ -46,7 +46,7 @@
     }
 
     static interface Action {
-        void execute(Uri uri, long id, MediaRetriever.MediaSize size, Object parameter);
+        void execute(Uri uri, long id, MediaSize size, Object parameter);
     }
 
     private static final String[] PROJECTION_ID = {
@@ -89,10 +89,10 @@
     static class QueryCacheResults {
         public QueryCacheResults(long id, int sizeVal) {
             this.id = id;
-            this.size = MediaRetriever.MediaSize.fromInteger(sizeVal);
+            this.size = MediaSize.fromInteger(sizeVal);
         }
         public long id;
-        public MediaRetriever.MediaSize size;
+        public MediaSize size;
     }
 
     public MediaCacheDatabase(Context context) {
@@ -111,7 +111,7 @@
         MediaCache.getInstance().clearCacheDir();
     }
 
-    public Long getCached(Uri uri, MediaRetriever.MediaSize size) {
+    public Long getCached(Uri uri, MediaSize size) {
         String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " = ?";
         SQLiteDatabase db = getWritableDatabase();
         String[] whereArgs = {
@@ -140,7 +140,7 @@
         return id;
     }
 
-    public MediaRetriever.MediaSize executeOnBestCached(Uri uri, MediaRetriever.MediaSize size, Action action) {
+    public MediaSize executeOnBestCached(Uri uri, MediaSize size, Action action) {
         String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " < ?";
         String orderBy = Columns.MEDIA_SIZE + " DESC";
         SQLiteDatabase db = getReadableDatabase();
@@ -148,10 +148,10 @@
                 uri.toString(), String.valueOf(size.getValue()),
         };
         Cursor cursor = db.query(TABLE, PROJECTION_CACHED, where, whereArgs, null, null, orderBy);
-        MediaRetriever.MediaSize bestSize = null;
+        MediaSize bestSize = null;
         if (cursor.moveToNext()) {
             long id = cursor.getLong(0);
-            bestSize = MediaRetriever.MediaSize.fromInteger(cursor.getInt(1));
+            bestSize = MediaSize.fromInteger(cursor.getInt(1));
             long fileSize = cursor.getLong(2);
             action.execute(uri, id, bestSize, fileSize);
         }
@@ -159,7 +159,7 @@
         return bestSize;
     }
 
-    public long insert(Uri uri, MediaRetriever.MediaSize size, Action action, File tempFile) {
+    public long insert(Uri uri, MediaSize size, Action action, File tempFile) {
         SQLiteDatabase db = getWritableDatabase();
         db.beginTransaction();
         try {
@@ -195,20 +195,34 @@
         }
     }
 
+    public void delete(Uri uri, MediaSize size, Action action) {
+        String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " = ?";
+        String[] whereArgs = {
+                uri.toString(), String.valueOf(size.getValue()),
+        };
+        deleteRows(uri, where, whereArgs, action);
+    }
+
     public void delete(Uri uri, Action action) {
-        SQLiteDatabase db = getWritableDatabase();
         String where = Columns.URI + " = ?";
         String[] whereArgs = {
             uri.toString()
         };
+        deleteRows(uri, where, whereArgs, action);
+    }
+
+    private void deleteRows(Uri uri, String where, String[] whereArgs, Action action) {
+        SQLiteDatabase db = getWritableDatabase();
+        // Make this an atomic operation
+        db.beginTransaction();
         Cursor cursor = db.query(TABLE, PROJECTION_CACHED, where, whereArgs, null, null, null);
         while (cursor.moveToNext()) {
             long id = cursor.getLong(0);
-            MediaRetriever.MediaSize size = MediaRetriever.MediaSize.fromInteger(cursor.getInt(1));
-            action.execute(uri, id, size, null);
+            MediaSize size = MediaSize.fromInteger(cursor.getInt(1));
+            long length = cursor.getLong(2);
+            action.execute(uri, id, size, length);
         }
         cursor.close();
-        db.beginTransaction();
         try {
             db.delete(TABLE, where, whereArgs);
             db.setTransactionSuccessful();
diff --git a/src/com/android/photos/data/MediaCacheUtils.java b/src/com/android/photos/data/MediaCacheUtils.java
index 1463d52..e3ccd14 100644
--- a/src/com/android/photos/data/MediaCacheUtils.java
+++ b/src/com/android/photos/data/MediaCacheUtils.java
@@ -21,9 +21,12 @@
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
 import android.util.Log;
+import android.util.Pools.SimplePool;
+import android.util.Pools.SynchronizedPool;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.data.DecodeUtils;
 import com.android.gallery3d.data.MediaItem;
 import com.android.gallery3d.util.ThreadPool.CancelListener;
@@ -33,10 +36,15 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 
 public class MediaCacheUtils {
     private static final String TAG = MediaCacheUtils.class.getSimpleName();
     private static int QUALITY = 80;
+    private static final int BUFFER_SIZE = 4096;
+    private static final SimplePool<byte[]> mBufferPool = new SynchronizedPool<byte[]>(5);
+
     private static final JobContext sJobStub = new JobContext() {
 
         @Override
@@ -136,4 +144,24 @@
         }
         return success;
     }
+
+    public static int copyStream(InputStream in, OutputStream out) throws IOException {
+        byte[] buffer = mBufferPool.acquire();
+        if (buffer == null) {
+            buffer = new byte[BUFFER_SIZE];
+        }
+        try {
+            int totalWritten = 0;
+            int bytesRead;
+            while ((bytesRead = in.read(buffer)) >= 0) {
+                out.write(buffer, 0, bytesRead);
+                totalWritten += bytesRead;
+            }
+            return totalWritten;
+        } finally {
+            Utils.closeSilently(in);
+            Utils.closeSilently(out);
+            mBufferPool.release(buffer);
+        }
+    }
 }
diff --git a/src/com/android/photos/data/PhotoProvider.java b/src/com/android/photos/data/PhotoProvider.java
index 880c671..cffd123 100644
--- a/src/com/android/photos/data/PhotoProvider.java
+++ b/src/com/android/photos/data/PhotoProvider.java
@@ -85,6 +85,12 @@
      * Contains columns that can be accessed via Photos.CONTENT_URI.
      */
     public static interface Photos extends BaseColumns {
+        /**
+         * The image_type query parameter required for requesting a specific
+         * size of image.
+         */
+        public static final String MEDIA_SIZE_QUERY_PARAMETER = "media_size";
+
         /** Internal database table used for basic photo information. */
         public static final String TABLE = "photos";
         /** Content URI for basic photo and video information. */
@@ -203,49 +209,6 @@
         public static final String KEY_EXIF_ISO = ExifInterface.TAG_ISO;
     }
 
-    /**
-     * Contains columns and Uri for maintaining the image cache.
-     */
-    public static interface ImageCache extends BaseColumns {
-        /** Internal database table used for the image cache */
-        public static final String TABLE = "image_cache";
-
-        /**
-         * The image_type query parameter required for accessing a specific
-         * image
-         */
-        public static final String IMAGE_TYPE_QUERY_PARAMETER = "image_type";
-
-        // ImageCache.IMAGE_TYPE values
-        public static final int IMAGE_TYPE_ALBUM_COVER = 1;
-        public static final int IMAGE_TYPE_THUMBNAIL = 2;
-        public static final int IMAGE_TYPE_PREVIEW = 3;
-        public static final int IMAGE_TYPE_ORIGINAL = 4;
-
-        /**
-         * Content URI for retrieving image paths. The
-         * IMAGE_TYPE_QUERY_PARAMETER must be used in queries.
-         */
-        public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, TABLE);
-
-        /**
-         * Content URI for retrieving the album cover art. The album ID must be
-         * appended to the URI.
-         */
-        public static final Uri ALBUM_COVER_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI,
-                Albums.TABLE);
-
-        /**
-         * An _ID from Albums or Photos, depending on whether IMAGE_TYPE is
-         * IMAGE_TYPE_ALBUM or not. Long value.
-         */
-        public static final String REMOTE_ID = "remote_id";
-        /** One of IMAGE_TYPE_* values. */
-        public static final String IMAGE_TYPE = "image_type";
-        /** The String path to the image. */
-        public static final String PATH = "path";
-    };
-
     // SQL used within this class.
     protected static final String WHERE_ID = BaseColumns._ID + " = ?";
     protected static final String WHERE_METADATA_ID = Metadata.PHOTO_ID + " = ? AND "
@@ -284,10 +247,8 @@
     protected static final int MATCH_ALBUM_ID = 4;
     protected static final int MATCH_METADATA = 5;
     protected static final int MATCH_METADATA_ID = 6;
-    protected static final int MATCH_IMAGE = 7;
-    protected static final int MATCH_ALBUM_COVER = 8;
-    protected static final int MATCH_ACCOUNT = 9;
-    protected static final int MATCH_ACCOUNT_ID = 10;
+    protected static final int MATCH_ACCOUNT = 7;
+    protected static final int MATCH_ACCOUNT_ID = 8;
 
     static {
         sUriMatcher.addURI(AUTHORITY, Photos.TABLE, MATCH_PHOTO);
@@ -299,11 +260,6 @@
         sUriMatcher.addURI(AUTHORITY, Metadata.TABLE, MATCH_METADATA);
         // match against metadata/<Metadata._ID>
         sUriMatcher.addURI(AUTHORITY, Metadata.TABLE + "/#", MATCH_METADATA_ID);
-        // match against image_cache/<ImageCache.PHOTO_ID>
-        sUriMatcher.addURI(AUTHORITY, ImageCache.TABLE + "/#", MATCH_IMAGE);
-        // match against image_cache/album/<Albums._ID>
-        sUriMatcher.addURI(AUTHORITY, ImageCache.TABLE + "/" + Albums.TABLE + "/#",
-                MATCH_ALBUM_COVER);
         sUriMatcher.addURI(AUTHORITY, Accounts.TABLE, MATCH_ACCOUNT);
         // match against Accounts._ID
         sUriMatcher.addURI(AUTHORITY, Accounts.TABLE + "/#", MATCH_ACCOUNT_ID);
@@ -476,9 +432,6 @@
         if (match == UriMatcher.NO_MATCH) {
             throw unknownUri(uri);
         }
-        if (match == MATCH_IMAGE || match == MATCH_ALBUM_COVER) {
-            throw new IllegalArgumentException("Operation not allowed on image cache database");
-        }
         return match;
     }
 
diff --git a/tests/src/com/android/photos/data/MediaCacheTest.java b/tests/src/com/android/photos/data/MediaCacheTest.java
index df990ed..9e71128 100644
--- a/tests/src/com/android/photos/data/MediaCacheTest.java
+++ b/tests/src/com/android/photos/data/MediaCacheTest.java
@@ -157,7 +157,7 @@
     public void testRetrieveOriginal() throws IOException {
         copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
         Uri uri = Uri.fromFile(mImage);
-        mMediaCache.retrieveOriginal(uri, mReady, mReady);
+        mMediaCache.retrieveOriginal(uri, mReady, null);
         assertTrue(mReady.waitForNotification());
         assertNull(mReady.mInputStream);
         assertEquals(mImage, mReady.mOriginalFile);
@@ -236,13 +236,13 @@
         copyResourceToFile(R.raw.android_lawn, mImage.getPath());
         Uri uri = Uri.fromFile(mImage);
 
-        mMediaCache.retrieveOriginal(uri, mReady, mReady);
+        mMediaCache.retrieveOriginal(uri, mReady, null);
         assertTrue(mReady.waitForNotification());
         assertNull(mReady.mInputStream);
         assertNotNull(mReady.mOriginalFile);
 
         mReady = new ReadyCollector();
-        mMediaCache.retrievePreview(uri, mReady, mReady);
+        mMediaCache.retrievePreview(uri, mReady, null);
         assertTrue(mReady.waitForNotification());
         assertNotNull(mReady.mInputStream);
         assertNull(mReady.mOriginalFile);
@@ -254,7 +254,7 @@
         assertTrue(maxDimension < (targetSize * 2));
 
         mReady = new ReadyCollector();
-        mMediaCache.retrieveThumbnail(uri, mReady, mReady);
+        mMediaCache.retrieveThumbnail(uri, mReady, null);
         assertTrue(mReady.waitForNotification());
         assertNotNull(mReady.mInputStream);
         assertNull(mReady.mOriginalFile);
@@ -269,11 +269,11 @@
     public void testFastImage() throws IOException {
         copyResourceToFile(R.raw.galaxy_nexus, mImage.getPath());
         Uri uri = Uri.fromFile(mImage);
-        mMediaCache.retrieveThumbnail(uri, mReady, mReady);
+        mMediaCache.retrieveThumbnail(uri, mReady, null);
         mReady.waitForNotification();
         mReady.mInputStream.close();
 
-        mMediaCache.retrieveOriginal(uri, mReady, mReady);
+        mMediaCache.retrieveOriginal(uri, mReady, null);
         assertTrue(mReady.waitForNotification());
         assertNotNull(mReady.mInputStream);
         mReady.mInputStream.close();
@@ -282,7 +282,7 @@
     public void testBadRetriever() {
         Uri uri = Photos.CONTENT_URI;
         try {
-            mMediaCache.retrieveOriginal(uri, mReady, mReady);
+            mMediaCache.retrieveOriginal(uri, mReady, null);
             fail("Expected exception");
         } catch (IllegalArgumentException e) {
             // expected
@@ -295,7 +295,7 @@
         copyResourceToFile(R.raw.android_lawn, mImage.getPath());
         Uri uri = Uri.fromFile(mImage);
 
-        mMediaCache.retrieveThumbnail(uri, mReady, mReady);
+        mMediaCache.retrieveThumbnail(uri, mReady, null);
         assertTrue(mReady.waitForNotification());
         mReady.mInputStream.close();
         assertNotNull(mMediaCache.getCachedFile(uri, MediaSize.Preview));
@@ -307,7 +307,7 @@
         mMediaCache.addRetriever(uri.getScheme(), uri.getAuthority(), retriever);
         retriever.setNullUri();
         try {
-            mMediaCache.retrieveOriginal(uri, mReady, mReady);
+            mMediaCache.retrieveOriginal(uri, mReady, null);
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException e) {
             // expected