• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.photos.data;
17 
18 import android.content.ContentValues;
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.database.sqlite.SQLiteDatabase;
22 import android.database.sqlite.SQLiteOpenHelper;
23 import android.net.Uri;
24 import android.provider.BaseColumns;
25 
26 import com.android.photos.data.MediaRetriever.MediaSize;
27 
28 import java.io.File;
29 
30 class MediaCacheDatabase extends SQLiteOpenHelper {
31     public static final int DB_VERSION = 1;
32     public static final String DB_NAME = "mediacache.db";
33 
34     /** Internal database table used for the media cache */
35     public static final String TABLE = "media_cache";
36 
37     private static interface Columns extends BaseColumns {
38         /** The Content URI of the original image. */
39         public static final String URI = "uri";
40         /** MediaSize.getValue() values. */
41         public static final String MEDIA_SIZE = "media_size";
42         /** The last time this image was queried. */
43         public static final String LAST_ACCESS = "last_access";
44         /** The image size in bytes. */
45         public static final String SIZE_IN_BYTES = "size";
46     }
47 
48     static interface Action {
execute(Uri uri, long id, MediaSize size, Object parameter)49         void execute(Uri uri, long id, MediaSize size, Object parameter);
50     }
51 
52     private static final String[] PROJECTION_ID = {
53         Columns._ID,
54     };
55 
56     private static final String[] PROJECTION_CACHED = {
57         Columns._ID, Columns.MEDIA_SIZE, Columns.SIZE_IN_BYTES,
58     };
59 
60     private static final String[] PROJECTION_CACHE_SIZE = {
61         "SUM(" + Columns.SIZE_IN_BYTES + ")"
62     };
63 
64     private static final String[] PROJECTION_DELETE_OLD = {
65         Columns._ID, Columns.URI, Columns.MEDIA_SIZE, Columns.SIZE_IN_BYTES, Columns.LAST_ACCESS,
66     };
67 
68     public static final String CREATE_TABLE = "CREATE TABLE " + TABLE + "("
69             + Columns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
70             + Columns.URI + " TEXT NOT NULL,"
71             + Columns.MEDIA_SIZE + " INTEGER NOT NULL,"
72             + Columns.LAST_ACCESS + " INTEGER NOT NULL,"
73             + Columns.SIZE_IN_BYTES + " INTEGER NOT NULL,"
74             + "UNIQUE(" + Columns.URI + ", " + Columns.MEDIA_SIZE + "))";
75 
76     public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE;
77 
78     public static final String WHERE_THUMBNAIL = Columns.MEDIA_SIZE + " = "
79             + MediaSize.Thumbnail.getValue();
80 
81     public static final String WHERE_NOT_THUMBNAIL = Columns.MEDIA_SIZE + " <> "
82             + MediaSize.Thumbnail.getValue();
83 
84     public static final String WHERE_CLEAR_CACHE = Columns.LAST_ACCESS + " <= ?";
85 
86     public static final String WHERE_CLEAR_CACHE_LARGE = WHERE_CLEAR_CACHE + " AND "
87             + WHERE_NOT_THUMBNAIL;
88 
89     static class QueryCacheResults {
QueryCacheResults(long id, int sizeVal)90         public QueryCacheResults(long id, int sizeVal) {
91             this.id = id;
92             this.size = MediaSize.fromInteger(sizeVal);
93         }
94         public long id;
95         public MediaSize size;
96     }
97 
MediaCacheDatabase(Context context)98     public MediaCacheDatabase(Context context) {
99         super(context, DB_NAME, null, DB_VERSION);
100     }
101 
102     @Override
onCreate(SQLiteDatabase db)103     public void onCreate(SQLiteDatabase db) {
104         db.execSQL(CREATE_TABLE);
105     }
106 
107     @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)108     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
109         db.execSQL(DROP_TABLE);
110         onCreate(db);
111         MediaCache.getInstance().clearCacheDir();
112     }
113 
getCached(Uri uri, MediaSize size)114     public Long getCached(Uri uri, MediaSize size) {
115         String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " = ?";
116         SQLiteDatabase db = getWritableDatabase();
117         String[] whereArgs = {
118                 uri.toString(), String.valueOf(size.getValue()),
119         };
120         Cursor cursor = db.query(TABLE, PROJECTION_ID, where, whereArgs, null, null, null);
121         Long id = null;
122         if (cursor.moveToNext()) {
123             id = cursor.getLong(0);
124         }
125         cursor.close();
126         if (id != null) {
127             String[] updateArgs = {
128                 id.toString()
129             };
130             ContentValues values = new ContentValues();
131             values.put(Columns.LAST_ACCESS, System.currentTimeMillis());
132             db.beginTransaction();
133             try {
134                 db.update(TABLE, values, Columns._ID + " = ?", updateArgs);
135                 db.setTransactionSuccessful();
136             } finally {
137                 db.endTransaction();
138             }
139         }
140         return id;
141     }
142 
executeOnBestCached(Uri uri, MediaSize size, Action action)143     public MediaSize executeOnBestCached(Uri uri, MediaSize size, Action action) {
144         String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " < ?";
145         String orderBy = Columns.MEDIA_SIZE + " DESC";
146         SQLiteDatabase db = getReadableDatabase();
147         String[] whereArgs = {
148                 uri.toString(), String.valueOf(size.getValue()),
149         };
150         Cursor cursor = db.query(TABLE, PROJECTION_CACHED, where, whereArgs, null, null, orderBy);
151         MediaSize bestSize = null;
152         if (cursor.moveToNext()) {
153             long id = cursor.getLong(0);
154             bestSize = MediaSize.fromInteger(cursor.getInt(1));
155             long fileSize = cursor.getLong(2);
156             action.execute(uri, id, bestSize, fileSize);
157         }
158         cursor.close();
159         return bestSize;
160     }
161 
insert(Uri uri, MediaSize size, Action action, File tempFile)162     public long insert(Uri uri, MediaSize size, Action action, File tempFile) {
163         SQLiteDatabase db = getWritableDatabase();
164         db.beginTransaction();
165         try {
166             ContentValues values = new ContentValues();
167             values.put(Columns.LAST_ACCESS, System.currentTimeMillis());
168             values.put(Columns.MEDIA_SIZE, size.getValue());
169             values.put(Columns.URI, uri.toString());
170             values.put(Columns.SIZE_IN_BYTES, tempFile.length());
171             long id = db.insert(TABLE, null, values);
172             if (id != -1) {
173                 action.execute(uri, id, size, tempFile);
174                 db.setTransactionSuccessful();
175             }
176             return id;
177         } finally {
178             db.endTransaction();
179         }
180     }
181 
updateLength(long id, long fileSize)182     public void updateLength(long id, long fileSize) {
183         ContentValues values = new ContentValues();
184         values.put(Columns.SIZE_IN_BYTES, fileSize);
185         String[] whereArgs = {
186             String.valueOf(id)
187         };
188         SQLiteDatabase db = getWritableDatabase();
189         db.beginTransaction();
190         try {
191             db.update(TABLE, values, Columns._ID + " = ?", whereArgs);
192             db.setTransactionSuccessful();
193         } finally {
194             db.endTransaction();
195         }
196     }
197 
delete(Uri uri, MediaSize size, Action action)198     public void delete(Uri uri, MediaSize size, Action action) {
199         String where = Columns.URI + " = ? AND " + Columns.MEDIA_SIZE + " = ?";
200         String[] whereArgs = {
201                 uri.toString(), String.valueOf(size.getValue()),
202         };
203         deleteRows(uri, where, whereArgs, action);
204     }
205 
delete(Uri uri, Action action)206     public void delete(Uri uri, Action action) {
207         String where = Columns.URI + " = ?";
208         String[] whereArgs = {
209             uri.toString()
210         };
211         deleteRows(uri, where, whereArgs, action);
212     }
213 
deleteRows(Uri uri, String where, String[] whereArgs, Action action)214     private void deleteRows(Uri uri, String where, String[] whereArgs, Action action) {
215         SQLiteDatabase db = getWritableDatabase();
216         // Make this an atomic operation
217         db.beginTransaction();
218         Cursor cursor = db.query(TABLE, PROJECTION_CACHED, where, whereArgs, null, null, null);
219         while (cursor.moveToNext()) {
220             long id = cursor.getLong(0);
221             MediaSize size = MediaSize.fromInteger(cursor.getInt(1));
222             long length = cursor.getLong(2);
223             action.execute(uri, id, size, length);
224         }
225         cursor.close();
226         try {
227             db.delete(TABLE, where, whereArgs);
228             db.setTransactionSuccessful();
229         } finally {
230             db.endTransaction();
231         }
232     }
233 
deleteOldCached(boolean includeThumbnails, long deleteSize, Action action)234     public void deleteOldCached(boolean includeThumbnails, long deleteSize, Action action) {
235         String where = includeThumbnails ? null : WHERE_NOT_THUMBNAIL;
236         long lastAccess = 0;
237         SQLiteDatabase db = getWritableDatabase();
238         db.beginTransaction();
239         try {
240             Cursor cursor = db.query(TABLE, PROJECTION_DELETE_OLD, where, null, null, null,
241                     Columns.LAST_ACCESS);
242             while (cursor.moveToNext()) {
243                 long id = cursor.getLong(0);
244                 String uri = cursor.getString(1);
245                 MediaSize size = MediaSize.fromInteger(cursor.getInt(2));
246                 long length = cursor.getLong(3);
247                 long imageLastAccess = cursor.getLong(4);
248 
249                 if (imageLastAccess != lastAccess && deleteSize < 0) {
250                     break; // We've deleted enough.
251                 }
252                 lastAccess = imageLastAccess;
253                 action.execute(Uri.parse(uri), id, size, length);
254                 deleteSize -= length;
255             }
256             cursor.close();
257             String[] whereArgs = {
258                 String.valueOf(lastAccess),
259             };
260             String whereDelete = includeThumbnails ? WHERE_CLEAR_CACHE : WHERE_CLEAR_CACHE_LARGE;
261             db.delete(TABLE, whereDelete, whereArgs);
262             db.setTransactionSuccessful();
263         } finally {
264             db.endTransaction();
265         }
266     }
267 
getCacheSize()268     public long getCacheSize() {
269         return getCacheSize(null);
270     }
271 
getThumbnailCacheSize()272     public long getThumbnailCacheSize() {
273         return getCacheSize(WHERE_THUMBNAIL);
274     }
275 
getCacheSize(String where)276     private long getCacheSize(String where) {
277         SQLiteDatabase db = getReadableDatabase();
278         Cursor cursor = db.query(TABLE, PROJECTION_CACHE_SIZE, where, null, null, null, null);
279         long size = -1;
280         if (cursor.moveToNext()) {
281             size = cursor.getLong(0);
282         }
283         cursor.close();
284         return size;
285     }
286 }
287