diff options
Diffstat (limited to 'src/com/android/gallery3d/data/DownloadCache.java')
-rw-r--r-- | src/com/android/gallery3d/data/DownloadCache.java | 370 |
1 files changed, 0 insertions, 370 deletions
diff --git a/src/com/android/gallery3d/data/DownloadCache.java b/src/com/android/gallery3d/data/DownloadCache.java deleted file mode 100644 index be7820b01..000000000 --- a/src/com/android/gallery3d/data/DownloadCache.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.data; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.LruCache; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.data.DownloadEntry.Columns; -import com.android.gallery3d.util.Future; -import com.android.gallery3d.util.FutureListener; -import com.android.gallery3d.util.ThreadPool; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.io.File; -import java.net.URL; -import java.util.HashMap; -import java.util.HashSet; - -public class DownloadCache { - private static final String TAG = "DownloadCache"; - private static final int MAX_DELETE_COUNT = 16; - private static final int LRU_CAPACITY = 4; - - private static final String TABLE_NAME = DownloadEntry.SCHEMA.getTableName(); - - private static final String QUERY_PROJECTION[] = {Columns.ID, Columns.DATA}; - private static final String WHERE_HASH_AND_URL = String.format( - "%s = ? AND %s = ?", Columns.HASH_CODE, Columns.CONTENT_URL); - private static final int QUERY_INDEX_ID = 0; - private static final int QUERY_INDEX_DATA = 1; - - private static final String FREESPACE_PROJECTION[] = { - Columns.ID, Columns.DATA, Columns.CONTENT_URL, Columns.CONTENT_SIZE}; - private static final String FREESPACE_ORDER_BY = - String.format("%s ASC", Columns.LAST_ACCESS); - private static final int FREESPACE_IDNEX_ID = 0; - private static final int FREESPACE_IDNEX_DATA = 1; - private static final int FREESPACE_INDEX_CONTENT_URL = 2; - private static final int FREESPACE_INDEX_CONTENT_SIZE = 3; - - private static final String ID_WHERE = Columns.ID + " = ?"; - - private static final String SUM_PROJECTION[] = - {String.format("sum(%s)", Columns.CONTENT_SIZE)}; - private static final int SUM_INDEX_SUM = 0; - - private final LruCache<String, Entry> mEntryMap = - new LruCache<String, Entry>(LRU_CAPACITY); - private final HashMap<String, DownloadTask> mTaskMap = - new HashMap<String, DownloadTask>(); - private final File mRoot; - private final GalleryApp mApplication; - private final SQLiteDatabase mDatabase; - private final long mCapacity; - - private long mTotalBytes = 0; - private boolean mInitialized = false; - - public DownloadCache(GalleryApp application, File root, long capacity) { - mRoot = Utils.checkNotNull(root); - mApplication = Utils.checkNotNull(application); - mCapacity = capacity; - mDatabase = new DatabaseHelper(application.getAndroidContext()) - .getWritableDatabase(); - } - - private Entry findEntryInDatabase(String stringUrl) { - long hash = Utils.crc64Long(stringUrl); - String whereArgs[] = {String.valueOf(hash), stringUrl}; - Cursor cursor = mDatabase.query(TABLE_NAME, QUERY_PROJECTION, - WHERE_HASH_AND_URL, whereArgs, null, null, null); - try { - if (cursor.moveToNext()) { - File file = new File(cursor.getString(QUERY_INDEX_DATA)); - long id = cursor.getInt(QUERY_INDEX_ID); - Entry entry = null; - synchronized (mEntryMap) { - entry = mEntryMap.get(stringUrl); - if (entry == null) { - entry = new Entry(id, file); - mEntryMap.put(stringUrl, entry); - } - } - return entry; - } - } finally { - cursor.close(); - } - return null; - } - - public Entry download(JobContext jc, URL url) { - if (!mInitialized) initialize(); - - String stringUrl = url.toString(); - - // First find in the entry-pool - synchronized (mEntryMap) { - Entry entry = mEntryMap.get(stringUrl); - if (entry != null) { - updateLastAccess(entry.mId); - return entry; - } - } - - // Then, find it in database - TaskProxy proxy = new TaskProxy(); - synchronized (mTaskMap) { - Entry entry = findEntryInDatabase(stringUrl); - if (entry != null) { - updateLastAccess(entry.mId); - return entry; - } - - // Finally, we need to download the file .... - // First check if we are downloading it now ... - DownloadTask task = mTaskMap.get(stringUrl); - if (task == null) { // if not, start the download task now - task = new DownloadTask(stringUrl); - mTaskMap.put(stringUrl, task); - task.mFuture = mApplication.getThreadPool().submit(task, task); - } - task.addProxy(proxy); - } - - return proxy.get(jc); - } - - private void updateLastAccess(long id) { - ContentValues values = new ContentValues(); - values.put(Columns.LAST_ACCESS, System.currentTimeMillis()); - mDatabase.update(TABLE_NAME, values, - ID_WHERE, new String[] {String.valueOf(id)}); - } - - private synchronized void freeSomeSpaceIfNeed(int maxDeleteFileCount) { - if (mTotalBytes <= mCapacity) return; - Cursor cursor = mDatabase.query(TABLE_NAME, - FREESPACE_PROJECTION, null, null, null, null, FREESPACE_ORDER_BY); - try { - while (maxDeleteFileCount > 0 - && mTotalBytes > mCapacity && cursor.moveToNext()) { - long id = cursor.getLong(FREESPACE_IDNEX_ID); - String url = cursor.getString(FREESPACE_INDEX_CONTENT_URL); - long size = cursor.getLong(FREESPACE_INDEX_CONTENT_SIZE); - String path = cursor.getString(FREESPACE_IDNEX_DATA); - boolean containsKey; - synchronized (mEntryMap) { - containsKey = mEntryMap.containsKey(url); - } - if (!containsKey) { - --maxDeleteFileCount; - mTotalBytes -= size; - new File(path).delete(); - mDatabase.delete(TABLE_NAME, - ID_WHERE, new String[]{String.valueOf(id)}); - } else { - // skip delete, since it is being used - } - } - } finally { - cursor.close(); - } - } - - private synchronized long insertEntry(String url, File file) { - long size = file.length(); - mTotalBytes += size; - - ContentValues values = new ContentValues(); - String hashCode = String.valueOf(Utils.crc64Long(url)); - values.put(Columns.DATA, file.getAbsolutePath()); - values.put(Columns.HASH_CODE, hashCode); - values.put(Columns.CONTENT_URL, url); - values.put(Columns.CONTENT_SIZE, size); - values.put(Columns.LAST_UPDATED, System.currentTimeMillis()); - return mDatabase.insert(TABLE_NAME, "", values); - } - - private synchronized void initialize() { - if (mInitialized) return; - mInitialized = true; - if (!mRoot.isDirectory()) mRoot.mkdirs(); - if (!mRoot.isDirectory()) { - throw new RuntimeException("cannot create " + mRoot.getAbsolutePath()); - } - - Cursor cursor = mDatabase.query( - TABLE_NAME, SUM_PROJECTION, null, null, null, null, null); - mTotalBytes = 0; - try { - if (cursor.moveToNext()) { - mTotalBytes = cursor.getLong(SUM_INDEX_SUM); - } - } finally { - cursor.close(); - } - if (mTotalBytes > mCapacity) freeSomeSpaceIfNeed(MAX_DELETE_COUNT); - } - - private final class DatabaseHelper extends SQLiteOpenHelper { - public static final String DATABASE_NAME = "download.db"; - public static final int DATABASE_VERSION = 2; - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - DownloadEntry.SCHEMA.createTables(db); - // Delete old files - for (File file : mRoot.listFiles()) { - if (!file.delete()) { - Log.w(TAG, "fail to remove: " + file.getAbsolutePath()); - } - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - //reset everything - DownloadEntry.SCHEMA.dropTables(db); - onCreate(db); - } - } - - public class Entry { - public File cacheFile; - protected long mId; - - Entry(long id, File cacheFile) { - mId = id; - this.cacheFile = Utils.checkNotNull(cacheFile); - } - } - - private class DownloadTask implements Job<File>, FutureListener<File> { - private HashSet<TaskProxy> mProxySet = new HashSet<TaskProxy>(); - private Future<File> mFuture; - private final String mUrl; - - public DownloadTask(String url) { - mUrl = Utils.checkNotNull(url); - } - - public void removeProxy(TaskProxy proxy) { - synchronized (mTaskMap) { - Utils.assertTrue(mProxySet.remove(proxy)); - if (mProxySet.isEmpty()) { - mFuture.cancel(); - mTaskMap.remove(mUrl); - } - } - } - - // should be used in synchronized block of mDatabase - public void addProxy(TaskProxy proxy) { - proxy.mTask = this; - mProxySet.add(proxy); - } - - @Override - public void onFutureDone(Future<File> future) { - File file = future.get(); - long id = 0; - if (file != null) { // insert to database - id = insertEntry(mUrl, file); - } - - if (future.isCancelled()) { - Utils.assertTrue(mProxySet.isEmpty()); - return; - } - - synchronized (mTaskMap) { - Entry entry = null; - synchronized (mEntryMap) { - if (file != null) { - entry = new Entry(id, file); - Utils.assertTrue(mEntryMap.put(mUrl, entry) == null); - } - } - for (TaskProxy proxy : mProxySet) { - proxy.setResult(entry); - } - mTaskMap.remove(mUrl); - freeSomeSpaceIfNeed(MAX_DELETE_COUNT); - } - } - - @Override - public File run(JobContext jc) { - // TODO: utilize etag - jc.setMode(ThreadPool.MODE_NETWORK); - File tempFile = null; - try { - URL url = new URL(mUrl); - tempFile = File.createTempFile("cache", ".tmp", mRoot); - // download from url to tempFile - jc.setMode(ThreadPool.MODE_NETWORK); - boolean downloaded = DownloadUtils.requestDownload(jc, url, tempFile); - jc.setMode(ThreadPool.MODE_NONE); - if (downloaded) return tempFile; - } catch (Exception e) { - Log.e(TAG, String.format("fail to download %s", mUrl), e); - } finally { - jc.setMode(ThreadPool.MODE_NONE); - } - if (tempFile != null) tempFile.delete(); - return null; - } - } - - public static class TaskProxy { - private DownloadTask mTask; - private boolean mIsCancelled = false; - private Entry mEntry; - - synchronized void setResult(Entry entry) { - if (mIsCancelled) return; - mEntry = entry; - notifyAll(); - } - - public synchronized Entry get(JobContext jc) { - jc.setCancelListener(new CancelListener() { - @Override - public void onCancel() { - mTask.removeProxy(TaskProxy.this); - synchronized (TaskProxy.this) { - mIsCancelled = true; - TaskProxy.this.notifyAll(); - } - } - }); - while (!mIsCancelled && mEntry == null) { - try { - wait(); - } catch (InterruptedException e) { - Log.w(TAG, "ignore interrupt", e); - } - } - jc.setCancelListener(null); - return mEntry; - } - } -} |