summaryrefslogtreecommitdiffstats
path: root/src/com/android/photos
diff options
context:
space:
mode:
authorGeorge Mount <mount@google.com>2013-08-06 13:39:38 -0700
committerGeorge Mount <mount@google.com>2013-08-06 14:10:34 -0700
commite58cb0d26c9fc8d1eb04d3cb86233b918053a47b (patch)
treeef771da0850e053493c41bba01899810aa0f4db5 /src/com/android/photos
parentd3423af9e06503aeae632b14cb98f86608cac7c7 (diff)
downloadandroid_packages_apps_Gallery2-e58cb0d26c9fc8d1eb04d3cb86233b918053a47b.tar.gz
android_packages_apps_Gallery2-e58cb0d26c9fc8d1eb04d3cb86233b918053a47b.tar.bz2
android_packages_apps_Gallery2-e58cb0d26c9fc8d1eb04d3cb86233b918053a47b.zip
Remove MediaCache and MediaCacheUtils.
Change-Id: Iebf4b10ecde56bc490a1b5a4add31a1cf766e9c9
Diffstat (limited to 'src/com/android/photos')
-rw-r--r--src/com/android/photos/data/FileRetriever.java109
-rw-r--r--src/com/android/photos/data/MediaCache.java676
-rw-r--r--src/com/android/photos/data/MediaCacheDatabase.java286
-rw-r--r--src/com/android/photos/data/MediaCacheUtils.java167
-rw-r--r--src/com/android/photos/data/MediaRetriever.java129
5 files changed, 0 insertions, 1367 deletions
diff --git a/src/com/android/photos/data/FileRetriever.java b/src/com/android/photos/data/FileRetriever.java
deleted file mode 100644
index eb7686ef6..000000000
--- a/src/com/android/photos/data/FileRetriever.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.graphics.Bitmap;
-import android.media.ExifInterface;
-import android.net.Uri;
-import android.util.Log;
-import android.webkit.MimeTypeMap;
-
-import com.android.gallery3d.common.BitmapUtils;
-
-import java.io.File;
-import java.io.IOException;
-
-public class FileRetriever implements MediaRetriever {
- private static final String TAG = FileRetriever.class.getSimpleName();
-
- @Override
- public File getLocalFile(Uri contentUri) {
- return new File(contentUri.getPath());
- }
-
- @Override
- public MediaSize getFastImageSize(Uri contentUri, MediaSize size) {
- if (isVideo(contentUri)) {
- return null;
- }
- return MediaSize.TemporaryThumbnail;
- }
-
- @Override
- public byte[] getTemporaryImage(Uri contentUri, MediaSize fastImageSize) {
-
- try {
- ExifInterface exif = new ExifInterface(contentUri.getPath());
- if (exif.hasThumbnail()) {
- return exif.getThumbnail();
- }
- } catch (IOException e) {
- Log.w(TAG, "Unable to load exif for " + contentUri);
- }
- return null;
- }
-
- @Override
- public boolean getMedia(Uri contentUri, MediaSize imageSize, File tempFile) {
- if (imageSize == MediaSize.Original) {
- return false; // getLocalFile should always return the original.
- }
- if (imageSize == MediaSize.Thumbnail) {
- File preview = MediaCache.getInstance().getCachedFile(contentUri, MediaSize.Preview);
- if (preview != null) {
- // Just downsample the preview, it is faster.
- return MediaCacheUtils.downsample(preview, imageSize, tempFile);
- }
- }
- File highRes = new File(contentUri.getPath());
- boolean success;
- if (!isVideo(contentUri)) {
- success = MediaCacheUtils.downsample(highRes, imageSize, tempFile);
- } else {
- // Video needs to extract the bitmap.
- Bitmap bitmap = BitmapUtils.createVideoThumbnail(highRes.getPath());
- if (bitmap == null) {
- return false;
- } else if (imageSize == MediaSize.Thumbnail
- && !MediaCacheUtils.needsDownsample(bitmap, MediaSize.Preview)
- && MediaCacheUtils.writeToFile(bitmap, tempFile)) {
- // Opportunistically save preview
- MediaCache mediaCache = MediaCache.getInstance();
- mediaCache.insertIntoCache(contentUri, MediaSize.Preview, tempFile);
- }
- // Now scale the image
- success = MediaCacheUtils.downsample(bitmap, imageSize, tempFile);
- }
- return success;
- }
-
- @Override
- public Uri normalizeUri(Uri contentUri, MediaSize size) {
- return contentUri;
- }
-
- @Override
- public MediaSize normalizeMediaSize(Uri contentUri, MediaSize size) {
- return size;
- }
-
- private static boolean isVideo(Uri uri) {
- MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
- String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
- String mimeType = mimeTypeMap.getMimeTypeFromExtension(extension);
- return (mimeType != null && mimeType.startsWith("video/"));
- }
-}
diff --git a/src/com/android/photos/data/MediaCache.java b/src/com/android/photos/data/MediaCache.java
deleted file mode 100644
index 0952a4017..000000000
--- a/src/com/android/photos/data/MediaCache.java
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * 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.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Environment;
-import android.util.Log;
-
-import com.android.photos.data.MediaCacheDatabase.Action;
-import com.android.photos.data.MediaRetriever.MediaSize;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-
-/**
- * MediaCache keeps a cache of images, videos, thumbnails and previews. Calls to
- * retrieve a specific media item are executed asynchronously. The caller has an
- * option to receive a notification for lower resolution images that happen to
- * be available prior to the one requested.
- * <p>
- * When an media item has been retrieved, the notification for it is called on a
- * separate notifier thread. This thread should not be held for a long time so
- * that other notifications may happen.
- * </p>
- * <p>
- * Media items are uniquely identified by their content URIs. Each
- * scheme/authority can offer its own MediaRetriever, running in its own thread.
- * </p>
- * <p>
- * The MediaCache is an LRU cache, but does not allow the thumbnail cache to
- * drop below a minimum size. This prevents browsing through original images to
- * wipe out the thumbnails.
- * </p>
- */
-public class MediaCache {
- static final String TAG = MediaCache.class.getSimpleName();
- /** Subdirectory containing the image cache. */
- static final String IMAGE_CACHE_SUBDIR = "image_cache";
- /** File name extension to use for cached images. */
- static final String IMAGE_EXTENSION = ".cache";
- /** File name extension to use for temporary cached images while retrieving. */
- static final String TEMP_IMAGE_EXTENSION = ".temp";
-
- public static interface ImageReady {
- void imageReady(InputStream bitmapInputStream);
- }
-
- public static interface OriginalReady {
- void originalReady(File originalFile);
- }
-
- /** A Thread for each MediaRetriever */
- private class ProcessQueue extends Thread {
- private Queue<ProcessingJob> mQueue;
-
- public ProcessQueue(Queue<ProcessingJob> queue) {
- mQueue = queue;
- }
-
- @Override
- public void run() {
- while (mRunning) {
- ProcessingJob status;
- synchronized (mQueue) {
- while (mQueue.isEmpty()) {
- try {
- mQueue.wait();
- } catch (InterruptedException e) {
- if (!mRunning) {
- return;
- }
- Log.w(TAG, "Unexpected interruption", e);
- }
- }
- status = mQueue.remove();
- }
- processTask(status);
- }
- }
- };
-
- private interface NotifyReady {
- void notifyReady();
-
- void setFile(File file) throws FileNotFoundException;
-
- boolean isPrefetch();
- }
-
- private static class NotifyOriginalReady implements NotifyReady {
- private final OriginalReady mCallback;
- private File mFile;
-
- public NotifyOriginalReady(OriginalReady callback) {
- mCallback = callback;
- }
-
- @Override
- public void notifyReady() {
- if (mCallback != null) {
- mCallback.originalReady(mFile);
- }
- }
-
- @Override
- public void setFile(File file) {
- mFile = file;
- }
-
- @Override
- public boolean isPrefetch() {
- return mCallback == null;
- }
- }
-
- private static class NotifyImageReady implements NotifyReady {
- private final ImageReady mCallback;
- private InputStream mInputStream;
-
- public NotifyImageReady(ImageReady callback) {
- mCallback = callback;
- }
-
- @Override
- public void notifyReady() {
- if (mCallback != null) {
- mCallback.imageReady(mInputStream);
- }
- }
-
- @Override
- public void setFile(File file) throws FileNotFoundException {
- mInputStream = new FileInputStream(file);
- }
-
- public void setBytes(byte[] bytes) {
- mInputStream = new ByteArrayInputStream(bytes);
- }
-
- @Override
- public boolean isPrefetch() {
- return mCallback == null;
- }
- }
-
- /** A media item to be retrieved and its notifications. */
- private static class ProcessingJob {
- public ProcessingJob(Uri uri, MediaSize size, NotifyReady complete,
- NotifyImageReady lowResolution) {
- this.contentUri = uri;
- this.size = size;
- this.complete = complete;
- this.lowResolution = lowResolution;
- }
- public Uri contentUri;
- public MediaSize size;
- public NotifyImageReady lowResolution;
- public NotifyReady complete;
- }
-
- private boolean mRunning = true;
- private static MediaCache sInstance;
- private File mCacheDir;
- private Context mContext;
- private Queue<NotifyReady> mCallbacks = new LinkedList<NotifyReady>();
- private Map<String, MediaRetriever> mRetrievers = new HashMap<String, MediaRetriever>();
- private Map<String, List<ProcessingJob>> mTasks = new HashMap<String, List<ProcessingJob>>();
- private List<ProcessQueue> mProcessingThreads = new ArrayList<ProcessQueue>();
- private MediaCacheDatabase mDatabaseHelper;
- private long mTempImageNumber = 1;
- private Object mTempImageNumberLock = new Object();
-
- private long mMaxCacheSize = 40 * 1024 * 1024; // 40 MB
- private long mMinThumbCacheSize = 4 * 1024 * 1024; // 4 MB
- private long mCacheSize = -1;
- private long mThumbCacheSize = -1;
- private Object mCacheSizeLock = new Object();
-
- private Action mNotifyCachedLowResolution = new Action() {
- @Override
- public void execute(Uri uri, long id, MediaSize size, Object parameter) {
- ProcessingJob job = (ProcessingJob) parameter;
- File file = createCacheImagePath(id);
- addNotification(job.lowResolution, file);
- }
- };
-
- private Action mMoveTempToCache = new Action() {
- @Override
- public void execute(Uri uri, long id, MediaSize size, Object parameter) {
- File tempFile = (File) parameter;
- File cacheFile = createCacheImagePath(id);
- tempFile.renameTo(cacheFile);
- }
- };
-
- private Action mDeleteFile = new Action() {
- @Override
- public void execute(Uri uri, long id, MediaSize size, Object parameter) {
- File file = createCacheImagePath(id);
- file.delete();
- synchronized (mCacheSizeLock) {
- if (mCacheSize != -1) {
- long length = (Long) parameter;
- mCacheSize -= length;
- if (size == MediaSize.Thumbnail) {
- mThumbCacheSize -= length;
- }
- }
- }
- }
- };
-
- /** The thread used to make ImageReady and OriginalReady callbacks. */
- private Thread mProcessNotifications = new Thread() {
- @Override
- public void run() {
- while (mRunning) {
- NotifyReady notifyImage;
- synchronized (mCallbacks) {
- while (mCallbacks.isEmpty()) {
- try {
- mCallbacks.wait();
- } catch (InterruptedException e) {
- if (!mRunning) {
- return;
- }
- Log.w(TAG, "Unexpected Interruption, continuing");
- }
- }
- notifyImage = mCallbacks.remove();
- }
-
- notifyImage.notifyReady();
- }
- }
- };
-
- public static synchronized void initialize(Context context) {
- if (sInstance == null) {
- sInstance = new MediaCache(context);
- MediaCacheUtils.initialize(context);
- }
- }
-
- public static MediaCache getInstance() {
- return sInstance;
- }
-
- public static synchronized void shutdown() {
- sInstance.mRunning = false;
- sInstance.mProcessNotifications.interrupt();
- for (ProcessQueue processingThread : sInstance.mProcessingThreads) {
- processingThread.interrupt();
- }
- sInstance = null;
- }
-
- private MediaCache(Context context) {
- mDatabaseHelper = new MediaCacheDatabase(context);
- mProcessNotifications.start();
- mContext = context;
- }
-
- // This is used for testing.
- public void setCacheDir(File cacheDir) {
- cacheDir.mkdirs();
- mCacheDir = cacheDir;
- }
-
- public File getCacheDir() {
- synchronized (mContext) {
- if (mCacheDir == null) {
- String state = Environment.getExternalStorageState();
- File baseDir;
- if (Environment.MEDIA_MOUNTED.equals(state)) {
- baseDir = mContext.getExternalCacheDir();
- } else {
- // Stored in internal cache
- baseDir = mContext.getCacheDir();
- }
- mCacheDir = new File(baseDir, IMAGE_CACHE_SUBDIR);
- mCacheDir.mkdirs();
- }
- return mCacheDir;
- }
- }
-
- /**
- * Invalidates all cached images related to a given contentUri. This call
- * doesn't complete until the images have been removed from the cache.
- */
- public void invalidate(Uri contentUri) {
- mDatabaseHelper.delete(contentUri, mDeleteFile);
- }
-
- public void clearCacheDir() {
- File[] cachedFiles = getCacheDir().listFiles();
- if (cachedFiles != null) {
- for (File cachedFile : cachedFiles) {
- cachedFile.delete();
- }
- }
- }
-
- /**
- * Add a MediaRetriever for a Uri scheme and authority. This MediaRetriever
- * will be granted its own thread for retrieving images.
- */
- public void addRetriever(String scheme, String authority, MediaRetriever retriever) {
- String differentiator = getDifferentiator(scheme, authority);
- synchronized (mRetrievers) {
- mRetrievers.put(differentiator, retriever);
- }
- synchronized (mTasks) {
- LinkedList<ProcessingJob> queue = new LinkedList<ProcessingJob>();
- mTasks.put(differentiator, queue);
- new ProcessQueue(queue).start();
- }
- }
-
- /**
- * Retrieves a thumbnail. complete will be called when the thumbnail is
- * available. If lowResolution is not null and a lower resolution thumbnail
- * is available before the thumbnail, lowResolution will be called prior to
- * complete. All callbacks will be made on a thread other than the calling
- * thread.
- *
- * @param contentUri The URI for the full resolution image to search for.
- * @param complete Callback for when the image has been retrieved.
- * @param lowResolution If not null and a lower resolution image is
- * available prior to retrieving the thumbnail, this will be
- * called with the low resolution bitmap.
- */
- public void retrieveThumbnail(Uri contentUri, ImageReady complete, ImageReady lowResolution) {
- addTask(contentUri, complete, lowResolution, MediaSize.Thumbnail);
- }
-
- /**
- * Retrieves a preview. complete will be called when the preview is
- * available. If lowResolution is not null and a lower resolution preview is
- * available before the preview, lowResolution will be called prior to
- * complete. All callbacks will be made on a thread other than the calling
- * thread.
- *
- * @param contentUri The URI for the full resolution image to search for.
- * @param complete Callback for when the image has been retrieved.
- * @param lowResolution If not null and a lower resolution image is
- * available prior to retrieving the preview, this will be called
- * with the low resolution bitmap.
- */
- public void retrievePreview(Uri contentUri, ImageReady complete, ImageReady lowResolution) {
- addTask(contentUri, complete, lowResolution, MediaSize.Preview);
- }
-
- /**
- * Retrieves the original image or video. complete will be called when the
- * media is available on the local file system. If lowResolution is not null
- * and a lower resolution preview is available before the original,
- * lowResolution will be called prior to complete. All callbacks will be
- * made on a thread other than the calling thread.
- *
- * @param contentUri The URI for the full resolution image to search for.
- * @param complete Callback for when the image has been retrieved.
- * @param lowResolution If not null and a lower resolution image is
- * available prior to retrieving the preview, this will be called
- * with the low resolution bitmap.
- */
- public void retrieveOriginal(Uri contentUri, OriginalReady complete, ImageReady lowResolution) {
- File localFile = getLocalFile(contentUri);
- if (localFile != null) {
- addNotification(new NotifyOriginalReady(complete), localFile);
- } else {
- NotifyImageReady notifyLowResolution = (lowResolution == null) ? null
- : new NotifyImageReady(lowResolution);
- addTask(contentUri, new NotifyOriginalReady(complete), notifyLowResolution,
- MediaSize.Original);
- }
- }
-
- /**
- * Looks for an already cached media at a specific size.
- *
- * @param contentUri The original media item content URI
- * @param size The target size to search for in the cache
- * @return The cached file location or null if it is not cached.
- */
- public File getCachedFile(Uri contentUri, MediaSize size) {
- Long cachedId = mDatabaseHelper.getCached(contentUri, size);
- File file = null;
- if (cachedId != null) {
- file = createCacheImagePath(cachedId);
- if (!file.exists()) {
- mDatabaseHelper.delete(contentUri, size, mDeleteFile);
- file = null;
- }
- }
- return file;
- }
-
- /**
- * Inserts a media item into the cache.
- *
- * @param contentUri The original media item URI.
- * @param size The size of the media item to store in the cache.
- * @param tempFile The temporary file where the image is stored. This file
- * will no longer exist after executing this method.
- * @return The new location, in the cache, of the media item or null if it
- * wasn't possible to move into the cache.
- */
- public File insertIntoCache(Uri contentUri, MediaSize size, File tempFile) {
- long fileSize = tempFile.length();
- if (fileSize == 0) {
- return null;
- }
- File cacheFile = null;
- SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
- // Ensure that this step is atomic
- db.beginTransaction();
- try {
- Long id = mDatabaseHelper.getCached(contentUri, size);
- if (id != null) {
- cacheFile = createCacheImagePath(id);
- if (tempFile.renameTo(cacheFile)) {
- mDatabaseHelper.updateLength(id, fileSize);
- } else {
- Log.w(TAG, "Could not update cached file with " + tempFile);
- tempFile.delete();
- cacheFile = null;
- }
- } else {
- ensureFreeCacheSpace(tempFile.length(), size);
- id = mDatabaseHelper.insert(contentUri, size, mMoveTempToCache, tempFile);
- cacheFile = createCacheImagePath(id);
- }
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- return cacheFile;
- }
-
- /**
- * For testing purposes.
- */
- public void setMaxCacheSize(long maxCacheSize) {
- synchronized (mCacheSizeLock) {
- mMaxCacheSize = maxCacheSize;
- mMinThumbCacheSize = mMaxCacheSize / 10;
- mCacheSize = -1;
- mThumbCacheSize = -1;
- }
- }
-
- private File createCacheImagePath(long id) {
- return new File(getCacheDir(), String.valueOf(id) + IMAGE_EXTENSION);
- }
-
- private void addTask(Uri contentUri, ImageReady complete, ImageReady lowResolution,
- MediaSize size) {
- NotifyReady notifyComplete = new NotifyImageReady(complete);
- NotifyImageReady notifyLowResolution = null;
- if (lowResolution != null) {
- notifyLowResolution = new NotifyImageReady(lowResolution);
- }
- addTask(contentUri, notifyComplete, notifyLowResolution, size);
- }
-
- private void addTask(Uri contentUri, NotifyReady complete, NotifyImageReady lowResolution,
- MediaSize size) {
- MediaRetriever retriever = getMediaRetriever(contentUri);
- Uri uri = retriever.normalizeUri(contentUri, size);
- if (uri == null) {
- throw new IllegalArgumentException("No MediaRetriever for " + contentUri);
- }
- size = retriever.normalizeMediaSize(uri, size);
-
- File cachedFile = getCachedFile(uri, size);
- if (cachedFile != null) {
- addNotification(complete, cachedFile);
- return;
- }
- String differentiator = getDifferentiator(uri.getScheme(), uri.getAuthority());
- synchronized (mTasks) {
- List<ProcessingJob> tasks = mTasks.get(differentiator);
- if (tasks == null) {
- throw new IllegalArgumentException("Cannot find retriever for: " + uri);
- }
- synchronized (tasks) {
- ProcessingJob job = new ProcessingJob(uri, size, complete, lowResolution);
- if (complete.isPrefetch()) {
- tasks.add(job);
- } else {
- int index = tasks.size() - 1;
- while (index >= 0 && tasks.get(index).complete.isPrefetch()) {
- index--;
- }
- tasks.add(index + 1, job);
- }
- tasks.notifyAll();
- }
- }
- }
-
- private MediaRetriever getMediaRetriever(Uri uri) {
- String differentiator = getDifferentiator(uri.getScheme(), uri.getAuthority());
- MediaRetriever retriever;
- synchronized (mRetrievers) {
- retriever = mRetrievers.get(differentiator);
- }
- if (retriever == null) {
- throw new IllegalArgumentException("No MediaRetriever for " + uri);
- }
- return retriever;
- }
-
- private File getLocalFile(Uri uri) {
- MediaRetriever retriever = getMediaRetriever(uri);
- File localFile = null;
- if (retriever != null) {
- localFile = retriever.getLocalFile(uri);
- }
- return localFile;
- }
-
- private MediaSize getFastImageSize(Uri uri, MediaSize size) {
- MediaRetriever retriever = getMediaRetriever(uri);
- return retriever.getFastImageSize(uri, size);
- }
-
- private boolean isFastImageBetter(MediaSize fastImageType, MediaSize size) {
- if (fastImageType == null) {
- return false;
- }
- if (size == null) {
- return true;
- }
- return fastImageType.isBetterThan(size);
- }
-
- private byte[] getTemporaryImage(Uri uri, MediaSize fastImageType) {
- MediaRetriever retriever = getMediaRetriever(uri);
- return retriever.getTemporaryImage(uri, fastImageType);
- }
-
- private void processTask(ProcessingJob job) {
- File cachedFile = getCachedFile(job.contentUri, job.size);
- if (cachedFile != null) {
- addNotification(job.complete, cachedFile);
- return;
- }
-
- boolean hasLowResolution = job.lowResolution != null;
- if (hasLowResolution) {
- MediaSize cachedSize = mDatabaseHelper.executeOnBestCached(job.contentUri, job.size,
- mNotifyCachedLowResolution);
- MediaSize fastImageSize = getFastImageSize(job.contentUri, job.size);
- if (isFastImageBetter(fastImageSize, cachedSize)) {
- if (fastImageSize.isTemporary()) {
- byte[] bytes = getTemporaryImage(job.contentUri, fastImageSize);
- if (bytes != null) {
- addNotification(job.lowResolution, bytes);
- }
- } else {
- File lowFile = getMedia(job.contentUri, fastImageSize);
- if (lowFile != null) {
- addNotification(job.lowResolution, lowFile);
- }
- }
- }
- }
-
- // Now get the full size desired
- File fullSizeFile = getMedia(job.contentUri, job.size);
- if (fullSizeFile != null) {
- addNotification(job.complete, fullSizeFile);
- }
- }
-
- private void addNotification(NotifyReady callback, File file) {
- try {
- callback.setFile(file);
- synchronized (mCallbacks) {
- mCallbacks.add(callback);
- mCallbacks.notifyAll();
- }
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Unable to read file " + file, e);
- }
- }
-
- private void addNotification(NotifyImageReady callback, byte[] bytes) {
- callback.setBytes(bytes);
- synchronized (mCallbacks) {
- mCallbacks.add(callback);
- mCallbacks.notifyAll();
- }
- }
-
- private File getMedia(Uri uri, MediaSize size) {
- long imageNumber;
- synchronized (mTempImageNumberLock) {
- imageNumber = mTempImageNumber++;
- }
- File tempFile = new File(getCacheDir(), String.valueOf(imageNumber) + TEMP_IMAGE_EXTENSION);
- MediaRetriever retriever = getMediaRetriever(uri);
- boolean retrieved = retriever.getMedia(uri, size, tempFile);
- File cachedFile = null;
- if (retrieved) {
- ensureFreeCacheSpace(tempFile.length(), size);
- long id = mDatabaseHelper.insert(uri, size, mMoveTempToCache, tempFile);
- cachedFile = createCacheImagePath(id);
- }
- return cachedFile;
- }
-
- private static String getDifferentiator(String scheme, String authority) {
- if (authority == null) {
- return scheme;
- }
- StringBuilder differentiator = new StringBuilder(scheme);
- differentiator.append(':');
- differentiator.append(authority);
- return differentiator.toString();
- }
-
- private void ensureFreeCacheSpace(long size, MediaSize mediaSize) {
- synchronized (mCacheSizeLock) {
- if (mCacheSize == -1 || mThumbCacheSize == -1) {
- mCacheSize = mDatabaseHelper.getCacheSize();
- mThumbCacheSize = mDatabaseHelper.getThumbnailCacheSize();
- if (mCacheSize == -1 || mThumbCacheSize == -1) {
- Log.e(TAG, "Can't determine size of the image cache");
- return;
- }
- }
- mCacheSize += size;
- if (mediaSize == MediaSize.Thumbnail) {
- mThumbCacheSize += size;
- }
- if (mCacheSize > mMaxCacheSize) {
- shrinkCacheLocked();
- }
- }
- }
-
- private void shrinkCacheLocked() {
- long deleteSize = mMinThumbCacheSize;
- boolean includeThumbnails = (mThumbCacheSize - deleteSize) > mMinThumbCacheSize;
- mDatabaseHelper.deleteOldCached(includeThumbnails, deleteSize, mDeleteFile);
- }
-}
diff --git a/src/com/android/photos/data/MediaCacheDatabase.java b/src/com/android/photos/data/MediaCacheDatabase.java
deleted file mode 100644
index c92ac0fdf..000000000
--- a/src/com/android/photos/data/MediaCacheDatabase.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * 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.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-import com.android.photos.data.MediaRetriever.MediaSize;
-
-import java.io.File;
-
-class MediaCacheDatabase extends SQLiteOpenHelper {
- public static final int DB_VERSION = 1;
- public static final String DB_NAME = "mediacache.db";
-
- /** Internal database table used for the media cache */
- public static final String TABLE = "media_cache";
-
- private static interface Columns extends BaseColumns {
- /** The Content URI of the original image. */
- public static final String URI = "uri";
- /** MediaSize.getValue() values. */
- public static final String MEDIA_SIZE = "media_size";
- /** The last time this image was queried. */
- public static final String LAST_ACCESS = "last_access";
- /** The image size in bytes. */
- public static final String SIZE_IN_BYTES = "size";
- }
-
- static interface Action {
- void execute(Uri uri, long id, MediaSize size, Object parameter);
- }
-
- private static final String[] PROJECTION_ID = {
- Columns._ID,
- };
-
- private static final String[] PROJECTION_CACHED = {
- Columns._ID, Columns.MEDIA_SIZE, Columns.SIZE_IN_BYTES,
- };
-
- private static final String[] PROJECTION_CACHE_SIZE = {
- "SUM(" + Columns.SIZE_IN_BYTES + ")"
- };
-
- private static final String[] PROJECTION_DELETE_OLD = {
- Columns._ID, Columns.URI, Columns.MEDIA_SIZE, Columns.SIZE_IN_BYTES, Columns.LAST_ACCESS,
- };
-
- public static final String CREATE_TABLE = "CREATE TABLE " + TABLE + "("
- + Columns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + Columns.URI + " TEXT NOT NULL,"
- + Columns.MEDIA_SIZE + " INTEGER NOT NULL,"
- + Columns.LAST_ACCESS + " INTEGER NOT NULL,"
- + Columns.SIZE_IN_BYTES + " INTEGER NOT NULL,"
- + "UNIQUE(" + Columns.URI + ", " + Columns.MEDIA_SIZE + "))";
-
- public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE;
-
- public static final String WHERE_THUMBNAIL = Columns.MEDIA_SIZE + " = "
- + MediaSize.Thumbnail.getValue();
-
- public static final String WHERE_NOT_THUMBNAIL = Columns.MEDIA_SIZE + " <> "
- + MediaSize.Thumbnail.getValue();
-
- public static final String WHERE_CLEAR_CACHE = Columns.LAST_ACCESS + " <= ?";
-
- public static final String WHERE_CLEAR_CACHE_LARGE = WHERE_CLEAR_CACHE + " AND "
- + WHERE_NOT_THUMBNAIL;
-
- static class QueryCacheResults {
- public QueryCacheResults(long id, int sizeVal) {
- this.id = id;
- this.size = MediaSize.fromInteger(sizeVal);
- }
- public long id;
- public MediaSize size;
- }
-
- public MediaCacheDatabase(Context context) {
- super(context, DB_NAME, null, DB_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL(CREATE_TABLE);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL(DROP_TABLE);
- onCreate(db);
- MediaCache.getInstance().clearCacheDir();
- }
-
- public Long getCached(Uri uri, MediaSize size) {
- String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " = ?";
- SQLiteDatabase db = getWritableDatabase();
- String[] whereArgs = {
- uri.toString(), String.valueOf(size.getValue()),
- };
- Cursor cursor = db.query(TABLE, PROJECTION_ID, where, whereArgs, null, null, null);
- Long id = null;
- if (cursor.moveToNext()) {
- id = cursor.getLong(0);
- }
- cursor.close();
- if (id != null) {
- String[] updateArgs = {
- id.toString()
- };
- ContentValues values = new ContentValues();
- values.put(Columns.LAST_ACCESS, System.currentTimeMillis());
- db.beginTransaction();
- try {
- db.update(TABLE, values, Columns._ID + " = ?", updateArgs);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
- return id;
- }
-
- 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();
- String[] whereArgs = {
- uri.toString(), String.valueOf(size.getValue()),
- };
- Cursor cursor = db.query(TABLE, PROJECTION_CACHED, where, whereArgs, null, null, orderBy);
- MediaSize bestSize = null;
- if (cursor.moveToNext()) {
- long id = cursor.getLong(0);
- bestSize = MediaSize.fromInteger(cursor.getInt(1));
- long fileSize = cursor.getLong(2);
- action.execute(uri, id, bestSize, fileSize);
- }
- cursor.close();
- return bestSize;
- }
-
- public long insert(Uri uri, MediaSize size, Action action, File tempFile) {
- SQLiteDatabase db = getWritableDatabase();
- db.beginTransaction();
- try {
- ContentValues values = new ContentValues();
- values.put(Columns.LAST_ACCESS, System.currentTimeMillis());
- values.put(Columns.MEDIA_SIZE, size.getValue());
- values.put(Columns.URI, uri.toString());
- values.put(Columns.SIZE_IN_BYTES, tempFile.length());
- long id = db.insert(TABLE, null, values);
- if (id != -1) {
- action.execute(uri, id, size, tempFile);
- db.setTransactionSuccessful();
- }
- return id;
- } finally {
- db.endTransaction();
- }
- }
-
- public void updateLength(long id, long fileSize) {
- ContentValues values = new ContentValues();
- values.put(Columns.SIZE_IN_BYTES, fileSize);
- String[] whereArgs = {
- String.valueOf(id)
- };
- SQLiteDatabase db = getWritableDatabase();
- db.beginTransaction();
- try {
- db.update(TABLE, values, Columns._ID + " = ?", whereArgs);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- 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) {
- 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);
- MediaSize size = MediaSize.fromInteger(cursor.getInt(1));
- long length = cursor.getLong(2);
- action.execute(uri, id, size, length);
- }
- cursor.close();
- try {
- db.delete(TABLE, where, whereArgs);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- public void deleteOldCached(boolean includeThumbnails, long deleteSize, Action action) {
- String where = includeThumbnails ? null : WHERE_NOT_THUMBNAIL;
- long lastAccess = 0;
- SQLiteDatabase db = getWritableDatabase();
- db.beginTransaction();
- try {
- Cursor cursor = db.query(TABLE, PROJECTION_DELETE_OLD, where, null, null, null,
- Columns.LAST_ACCESS);
- while (cursor.moveToNext()) {
- long id = cursor.getLong(0);
- String uri = cursor.getString(1);
- MediaSize size = MediaSize.fromInteger(cursor.getInt(2));
- long length = cursor.getLong(3);
- long imageLastAccess = cursor.getLong(4);
-
- if (imageLastAccess != lastAccess && deleteSize < 0) {
- break; // We've deleted enough.
- }
- lastAccess = imageLastAccess;
- action.execute(Uri.parse(uri), id, size, length);
- deleteSize -= length;
- }
- cursor.close();
- String[] whereArgs = {
- String.valueOf(lastAccess),
- };
- String whereDelete = includeThumbnails ? WHERE_CLEAR_CACHE : WHERE_CLEAR_CACHE_LARGE;
- db.delete(TABLE, whereDelete, whereArgs);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- public long getCacheSize() {
- return getCacheSize(null);
- }
-
- public long getThumbnailCacheSize() {
- return getCacheSize(WHERE_THUMBNAIL);
- }
-
- private long getCacheSize(String where) {
- SQLiteDatabase db = getReadableDatabase();
- Cursor cursor = db.query(TABLE, PROJECTION_CACHE_SIZE, where, null, null, null, null);
- long size = -1;
- if (cursor.moveToNext()) {
- size = cursor.getLong(0);
- }
- cursor.close();
- return size;
- }
-}
diff --git a/src/com/android/photos/data/MediaCacheUtils.java b/src/com/android/photos/data/MediaCacheUtils.java
deleted file mode 100644
index e3ccd1402..000000000
--- a/src/com/android/photos/data/MediaCacheUtils.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-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;
-import com.android.gallery3d.util.ThreadPool.JobContext;
-import com.android.photos.data.MediaRetriever.MediaSize;
-
-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
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public void setCancelListener(CancelListener listener) {
- }
-
- @Override
- public boolean setMode(int mode) {
- return true;
- }
- };
-
- private static int mTargetThumbnailSize;
- private static int mTargetPreviewSize;
-
- public static void initialize(Context context) {
- Resources resources = context.getResources();
- mTargetThumbnailSize = resources.getDimensionPixelSize(R.dimen.size_thumbnail);
- mTargetPreviewSize = resources.getDimensionPixelSize(R.dimen.size_preview);
- }
-
- public static int getTargetSize(MediaSize size) {
- return (size == MediaSize.Thumbnail) ? mTargetThumbnailSize : mTargetPreviewSize;
- }
-
- public static boolean downsample(File inBitmap, MediaSize targetSize, File outBitmap) {
- if (MediaSize.Original == targetSize) {
- return false; // MediaCache should use the local path for this.
- }
- int size = getTargetSize(targetSize);
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- // TODO: remove unnecessary job context from DecodeUtils.
- Bitmap bitmap = DecodeUtils.decodeThumbnail(sJobStub, inBitmap.getPath(), options, size,
- MediaItem.TYPE_THUMBNAIL);
- boolean success = (bitmap != null);
- if (success) {
- success = writeAndRecycle(bitmap, outBitmap);
- }
- return success;
- }
-
- public static boolean downsample(Bitmap inBitmap, MediaSize size, File outBitmap) {
- if (MediaSize.Original == size) {
- return false; // MediaCache should use the local path for this.
- }
- int targetSize = getTargetSize(size);
- boolean success;
- if (!needsDownsample(inBitmap, size)) {
- success = writeAndRecycle(inBitmap, outBitmap);
- } else {
- float maxDimension = Math.max(inBitmap.getWidth(), inBitmap.getHeight());
- float scale = targetSize / maxDimension;
- int targetWidth = Math.round(scale * inBitmap.getWidth());
- int targetHeight = Math.round(scale * inBitmap.getHeight());
- Bitmap scaled = Bitmap.createScaledBitmap(inBitmap, targetWidth, targetHeight, false);
- success = writeAndRecycle(scaled, outBitmap);
- inBitmap.recycle();
- }
- return success;
- }
-
- public static boolean extractImageFromVideo(File inVideo, File outBitmap) {
- Bitmap bitmap = BitmapUtils.createVideoThumbnail(inVideo.getPath());
- return writeAndRecycle(bitmap, outBitmap);
- }
-
- public static boolean needsDownsample(Bitmap bitmap, MediaSize size) {
- if (size == MediaSize.Original) {
- return false;
- }
- int targetSize = getTargetSize(size);
- int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
- return maxDimension > (targetSize * 4 / 3);
- }
-
- public static boolean writeAndRecycle(Bitmap bitmap, File outBitmap) {
- boolean success = writeToFile(bitmap, outBitmap);
- bitmap.recycle();
- return success;
- }
-
- public static boolean writeToFile(Bitmap bitmap, File outBitmap) {
- boolean success = false;
- try {
- FileOutputStream out = new FileOutputStream(outBitmap);
- success = bitmap.compress(CompressFormat.JPEG, QUALITY, out);
- out.close();
- } catch (IOException e) {
- Log.w(TAG, "Couldn't write bitmap to cache", e);
- // success is already false
- }
- 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/MediaRetriever.java b/src/com/android/photos/data/MediaRetriever.java
deleted file mode 100644
index f383e5ffa..000000000
--- a/src/com/android/photos/data/MediaRetriever.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.net.Uri;
-
-import java.io.File;
-
-public interface MediaRetriever {
- public enum MediaSize {
- TemporaryThumbnail(5), Thumbnail(10), TemporaryPreview(15), Preview(20), Original(30);
-
- private final int mValue;
-
- private MediaSize(int value) {
- mValue = value;
- }
-
- public int getValue() {
- return mValue;
- }
-
- static MediaSize fromInteger(int value) {
- switch (value) {
- case 10:
- return MediaSize.Thumbnail;
- case 20:
- return MediaSize.Preview;
- case 30:
- return MediaSize.Original;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- public boolean isBetterThan(MediaSize that) {
- return mValue > that.mValue;
- }
-
- public boolean isTemporary() {
- return this == TemporaryThumbnail || this == TemporaryPreview;
- }
- }
-
- /**
- * Returns the local File for the given Uri. If the image is not stored
- * locally, null should be returned. The image should not be retrieved if it
- * isn't already available.
- *
- * @param contentUri The media URI to search for.
- * @return The local File of the image if it is available or null if it
- * isn't.
- */
- File getLocalFile(Uri contentUri);
-
- /**
- * Returns the fast access image type for a given image size, if supported.
- * This image should be smaller than size and should be quick to retrieve.
- * It does not have to obey the expected aspect ratio.
- *
- * @param contentUri The original media Uri.
- * @param size The target size to search for a fast-access image.
- * @return The fast image type supported for the given image size or null of
- * no fast image is supported.
- */
- MediaSize getFastImageSize(Uri contentUri, MediaSize size);
-
- /**
- * Returns a byte array containing the contents of the fast temporary image
- * for a given image size. For example, a thumbnail may be smaller or of a
- * different aspect ratio than the generated thumbnail.
- *
- * @param contentUri The original media Uri.
- * @param temporarySize The target media size. Guaranteed to be a MediaSize
- * for which isTemporary() returns true.
- * @return A byte array of contents for for the given contentUri and
- * fastImageType. null can be retrieved if the quick retrieval
- * fails.
- */
- byte[] getTemporaryImage(Uri contentUri, MediaSize temporarySize);
-
- /**
- * Retrieves an image and saves it to a file.
- *
- * @param contentUri The original media Uri.
- * @param size The target media size.
- * @param tempFile The file to write the bitmap to.
- * @return <code>true</code> on success.
- */
- boolean getMedia(Uri contentUri, MediaSize imageSize, File tempFile);
-
- /**
- * Normalizes a URI that may have additional parameters. It is fine to
- * return contentUri. This is executed on the calling thread, so it must be
- * a fast access operation and cannot depend, for example, on I/O.
- *
- * @param contentUri The URI to normalize
- * @param size The size of the image being requested
- * @return The normalized URI representation of contentUri.
- */
- Uri normalizeUri(Uri contentUri, MediaSize size);
-
- /**
- * Normalize the MediaSize for a given URI. Typically the size returned
- * would be the passed-in size. Some URIs may only have one size used and
- * should be treaded as Thumbnails, for example. This is executed on the
- * calling thread, so it must be a fast access operation and cannot depend,
- * for example, on I/O.
- *
- * @param contentUri The URI for the size being normalized.
- * @param size The size to be normalized.
- * @return The normalized size of the given URI.
- */
- MediaSize normalizeMediaSize(Uri contentUri, MediaSize size);
-}