1666ea1b28a76aeba74744148b15099254d918671Owen Lin/* 2666ea1b28a76aeba74744148b15099254d918671Owen Lin * Copyright (C) 2009 The Android Open Source Project 3666ea1b28a76aeba74744148b15099254d918671Owen Lin * 4666ea1b28a76aeba74744148b15099254d918671Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5666ea1b28a76aeba74744148b15099254d918671Owen Lin * you may not use this file except in compliance with the License. 6666ea1b28a76aeba74744148b15099254d918671Owen Lin * You may obtain a copy of the License at 7666ea1b28a76aeba74744148b15099254d918671Owen Lin * 8666ea1b28a76aeba74744148b15099254d918671Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9666ea1b28a76aeba74744148b15099254d918671Owen Lin * 10666ea1b28a76aeba74744148b15099254d918671Owen Lin * Unless required by applicable law or agreed to in writing, software 11666ea1b28a76aeba74744148b15099254d918671Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12666ea1b28a76aeba74744148b15099254d918671Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13666ea1b28a76aeba74744148b15099254d918671Owen Lin * See the License for the specific language governing permissions and 14666ea1b28a76aeba74744148b15099254d918671Owen Lin * limitations under the License. 15666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 16666ea1b28a76aeba74744148b15099254d918671Owen Lin 17666ea1b28a76aeba74744148b15099254d918671Owen Linpackage com.android.camera.gallery; 18666ea1b28a76aeba74744148b15099254d918671Owen Lin 19666ea1b28a76aeba74744148b15099254d918671Owen Linimport com.android.camera.ImageManager; 20666ea1b28a76aeba74744148b15099254d918671Owen Linimport com.android.camera.Util; 21666ea1b28a76aeba74744148b15099254d918671Owen Lin 22666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.content.ContentResolver; 23666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.content.ContentUris; 24666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.database.Cursor; 25666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.net.Uri; 26666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.Log; 27666ea1b28a76aeba74744148b15099254d918671Owen Lin 28666ea1b28a76aeba74744148b15099254d918671Owen Linimport java.util.regex.Matcher; 29666ea1b28a76aeba74744148b15099254d918671Owen Linimport java.util.regex.Pattern; 30666ea1b28a76aeba74744148b15099254d918671Owen Lin 31666ea1b28a76aeba74744148b15099254d918671Owen Lin/** 32666ea1b28a76aeba74744148b15099254d918671Owen Lin * A collection of <code>BaseImage</code>s. 33666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 34666ea1b28a76aeba74744148b15099254d918671Owen Linpublic abstract class BaseImageList implements IImageList { 35666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final String TAG = "BaseImageList"; 36666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int CACHE_CAPACITY = 512; 37666ea1b28a76aeba74744148b15099254d918671Owen Lin private final LruCache<Integer, BaseImage> mCache = 38666ea1b28a76aeba74744148b15099254d918671Owen Lin new LruCache<Integer, BaseImage>(CACHE_CAPACITY); 39666ea1b28a76aeba74744148b15099254d918671Owen Lin 40666ea1b28a76aeba74744148b15099254d918671Owen Lin protected ContentResolver mContentResolver; 41666ea1b28a76aeba74744148b15099254d918671Owen Lin protected int mSort; 42666ea1b28a76aeba74744148b15099254d918671Owen Lin 43666ea1b28a76aeba74744148b15099254d918671Owen Lin protected Uri mBaseUri; 44666ea1b28a76aeba74744148b15099254d918671Owen Lin protected Cursor mCursor; 45666ea1b28a76aeba74744148b15099254d918671Owen Lin protected String mBucketId; 46666ea1b28a76aeba74744148b15099254d918671Owen Lin protected boolean mCursorDeactivated = false; 47666ea1b28a76aeba74744148b15099254d918671Owen Lin 48666ea1b28a76aeba74744148b15099254d918671Owen Lin public BaseImageList(ContentResolver resolver, Uri uri, int sort, 49666ea1b28a76aeba74744148b15099254d918671Owen Lin String bucketId) { 50666ea1b28a76aeba74744148b15099254d918671Owen Lin mSort = sort; 51666ea1b28a76aeba74744148b15099254d918671Owen Lin mBaseUri = uri; 52666ea1b28a76aeba74744148b15099254d918671Owen Lin mBucketId = bucketId; 53666ea1b28a76aeba74744148b15099254d918671Owen Lin mContentResolver = resolver; 54666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursor = createCursor(); 55666ea1b28a76aeba74744148b15099254d918671Owen Lin 56666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCursor == null) { 57666ea1b28a76aeba74744148b15099254d918671Owen Lin Log.w(TAG, "createCursor returns null."); 58666ea1b28a76aeba74744148b15099254d918671Owen Lin } 59666ea1b28a76aeba74744148b15099254d918671Owen Lin 60666ea1b28a76aeba74744148b15099254d918671Owen Lin // TODO: We need to clear the cache because we may "reopen" the image 61666ea1b28a76aeba74744148b15099254d918671Owen Lin // list. After we implement the image list state, we can remove this 62666ea1b28a76aeba74744148b15099254d918671Owen Lin // kind of usage. 63666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.clear(); 64666ea1b28a76aeba74744148b15099254d918671Owen Lin } 65666ea1b28a76aeba74744148b15099254d918671Owen Lin 66666ea1b28a76aeba74744148b15099254d918671Owen Lin public void close() { 67666ea1b28a76aeba74744148b15099254d918671Owen Lin try { 68666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidateCursor(); 69666ea1b28a76aeba74744148b15099254d918671Owen Lin } catch (IllegalStateException e) { 70666ea1b28a76aeba74744148b15099254d918671Owen Lin // IllegalStateException may be thrown if the cursor is stale. 71666ea1b28a76aeba74744148b15099254d918671Owen Lin Log.e(TAG, "Caught exception while deactivating cursor.", e); 72666ea1b28a76aeba74744148b15099254d918671Owen Lin } 73666ea1b28a76aeba74744148b15099254d918671Owen Lin mContentResolver = null; 74666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCursor != null) { 75666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursor.close(); 76666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursor = null; 77666ea1b28a76aeba74744148b15099254d918671Owen Lin } 78666ea1b28a76aeba74744148b15099254d918671Owen Lin } 79666ea1b28a76aeba74744148b15099254d918671Owen Lin 80666ea1b28a76aeba74744148b15099254d918671Owen Lin // TODO: Change public to protected 81666ea1b28a76aeba74744148b15099254d918671Owen Lin public Uri contentUri(long id) { 82666ea1b28a76aeba74744148b15099254d918671Owen Lin // TODO: avoid using exception for most cases 83666ea1b28a76aeba74744148b15099254d918671Owen Lin try { 84666ea1b28a76aeba74744148b15099254d918671Owen Lin // does our uri already have an id (single image query)? 85666ea1b28a76aeba74744148b15099254d918671Owen Lin // if so just return it 86666ea1b28a76aeba74744148b15099254d918671Owen Lin long existingId = ContentUris.parseId(mBaseUri); 87666ea1b28a76aeba74744148b15099254d918671Owen Lin if (existingId != id) Log.e(TAG, "id mismatch"); 88666ea1b28a76aeba74744148b15099254d918671Owen Lin return mBaseUri; 89666ea1b28a76aeba74744148b15099254d918671Owen Lin } catch (NumberFormatException ex) { 90666ea1b28a76aeba74744148b15099254d918671Owen Lin // otherwise tack on the id 91666ea1b28a76aeba74744148b15099254d918671Owen Lin return ContentUris.withAppendedId(mBaseUri, id); 92666ea1b28a76aeba74744148b15099254d918671Owen Lin } 93666ea1b28a76aeba74744148b15099254d918671Owen Lin } 94666ea1b28a76aeba74744148b15099254d918671Owen Lin 95666ea1b28a76aeba74744148b15099254d918671Owen Lin public int getCount() { 96666ea1b28a76aeba74744148b15099254d918671Owen Lin Cursor cursor = getCursor(); 97666ea1b28a76aeba74744148b15099254d918671Owen Lin if (cursor == null) return 0; 98666ea1b28a76aeba74744148b15099254d918671Owen Lin synchronized (this) { 99666ea1b28a76aeba74744148b15099254d918671Owen Lin return cursor.getCount(); 100666ea1b28a76aeba74744148b15099254d918671Owen Lin } 101666ea1b28a76aeba74744148b15099254d918671Owen Lin } 102666ea1b28a76aeba74744148b15099254d918671Owen Lin 103666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean isEmpty() { 104666ea1b28a76aeba74744148b15099254d918671Owen Lin return getCount() == 0; 105666ea1b28a76aeba74744148b15099254d918671Owen Lin } 106666ea1b28a76aeba74744148b15099254d918671Owen Lin 107666ea1b28a76aeba74744148b15099254d918671Owen Lin private Cursor getCursor() { 108666ea1b28a76aeba74744148b15099254d918671Owen Lin synchronized (this) { 109666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCursor == null) return null; 110666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCursorDeactivated) { 111666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursor.requery(); 112666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursorDeactivated = false; 113666ea1b28a76aeba74744148b15099254d918671Owen Lin } 114666ea1b28a76aeba74744148b15099254d918671Owen Lin return mCursor; 115666ea1b28a76aeba74744148b15099254d918671Owen Lin } 116666ea1b28a76aeba74744148b15099254d918671Owen Lin } 117666ea1b28a76aeba74744148b15099254d918671Owen Lin 118666ea1b28a76aeba74744148b15099254d918671Owen Lin public IImage getImageAt(int i) { 119666ea1b28a76aeba74744148b15099254d918671Owen Lin BaseImage result = mCache.get(i); 120666ea1b28a76aeba74744148b15099254d918671Owen Lin if (result == null) { 121666ea1b28a76aeba74744148b15099254d918671Owen Lin Cursor cursor = getCursor(); 122666ea1b28a76aeba74744148b15099254d918671Owen Lin if (cursor == null) return null; 123666ea1b28a76aeba74744148b15099254d918671Owen Lin synchronized (this) { 124666ea1b28a76aeba74744148b15099254d918671Owen Lin result = cursor.moveToPosition(i) 125666ea1b28a76aeba74744148b15099254d918671Owen Lin ? loadImageFromCursor(cursor) 126666ea1b28a76aeba74744148b15099254d918671Owen Lin : null; 127666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.put(i, result); 128666ea1b28a76aeba74744148b15099254d918671Owen Lin } 129666ea1b28a76aeba74744148b15099254d918671Owen Lin } 130666ea1b28a76aeba74744148b15099254d918671Owen Lin return result; 131666ea1b28a76aeba74744148b15099254d918671Owen Lin } 132666ea1b28a76aeba74744148b15099254d918671Owen Lin 133666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean removeImage(IImage image) { 134666ea1b28a76aeba74744148b15099254d918671Owen Lin // TODO: need to delete the thumbnails as well 135666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mContentResolver.delete(image.fullSizeImageUri(), null, null) > 0) { 136666ea1b28a76aeba74744148b15099254d918671Owen Lin ((BaseImage) image).onRemove(); 137666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidateCursor(); 138666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidateCache(); 139666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 140666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 141666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 142666ea1b28a76aeba74744148b15099254d918671Owen Lin } 143666ea1b28a76aeba74744148b15099254d918671Owen Lin } 144666ea1b28a76aeba74744148b15099254d918671Owen Lin 145666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean removeImageAt(int i) { 146666ea1b28a76aeba74744148b15099254d918671Owen Lin // TODO: need to delete the thumbnails as well 147666ea1b28a76aeba74744148b15099254d918671Owen Lin return removeImage(getImageAt(i)); 148666ea1b28a76aeba74744148b15099254d918671Owen Lin } 149666ea1b28a76aeba74744148b15099254d918671Owen Lin 150666ea1b28a76aeba74744148b15099254d918671Owen Lin protected abstract Cursor createCursor(); 151666ea1b28a76aeba74744148b15099254d918671Owen Lin 152666ea1b28a76aeba74744148b15099254d918671Owen Lin protected abstract BaseImage loadImageFromCursor(Cursor cursor); 153666ea1b28a76aeba74744148b15099254d918671Owen Lin 154666ea1b28a76aeba74744148b15099254d918671Owen Lin protected abstract long getImageId(Cursor cursor); 155666ea1b28a76aeba74744148b15099254d918671Owen Lin 156666ea1b28a76aeba74744148b15099254d918671Owen Lin protected void invalidateCursor() { 157666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCursor == null) return; 158666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursor.deactivate(); 159666ea1b28a76aeba74744148b15099254d918671Owen Lin mCursorDeactivated = true; 160666ea1b28a76aeba74744148b15099254d918671Owen Lin } 161666ea1b28a76aeba74744148b15099254d918671Owen Lin 162666ea1b28a76aeba74744148b15099254d918671Owen Lin protected void invalidateCache() { 163666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.clear(); 164666ea1b28a76aeba74744148b15099254d918671Owen Lin } 165666ea1b28a76aeba74744148b15099254d918671Owen Lin 166666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final Pattern sPathWithId = Pattern.compile("(.*)/\\d+"); 167666ea1b28a76aeba74744148b15099254d918671Owen Lin 168666ea1b28a76aeba74744148b15099254d918671Owen Lin private static String getPathWithoutId(Uri uri) { 169666ea1b28a76aeba74744148b15099254d918671Owen Lin String path = uri.getPath(); 170666ea1b28a76aeba74744148b15099254d918671Owen Lin Matcher matcher = sPathWithId.matcher(path); 171666ea1b28a76aeba74744148b15099254d918671Owen Lin return matcher.matches() ? matcher.group(1) : path; 172666ea1b28a76aeba74744148b15099254d918671Owen Lin } 173666ea1b28a76aeba74744148b15099254d918671Owen Lin 174666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean isChildImageUri(Uri uri) { 175666ea1b28a76aeba74744148b15099254d918671Owen Lin // Sometimes, the URI of an image contains a query string with key 176666ea1b28a76aeba74744148b15099254d918671Owen Lin // "bucketId" inorder to restore the image list. However, the query 177666ea1b28a76aeba74744148b15099254d918671Owen Lin // string is not part of the mBaseUri. So, we check only other parts 178666ea1b28a76aeba74744148b15099254d918671Owen Lin // of the two Uri to see if they are the same. 179666ea1b28a76aeba74744148b15099254d918671Owen Lin Uri base = mBaseUri; 180666ea1b28a76aeba74744148b15099254d918671Owen Lin return Util.equals(base.getScheme(), uri.getScheme()) 181666ea1b28a76aeba74744148b15099254d918671Owen Lin && Util.equals(base.getHost(), uri.getHost()) 182666ea1b28a76aeba74744148b15099254d918671Owen Lin && Util.equals(base.getAuthority(), uri.getAuthority()) 183666ea1b28a76aeba74744148b15099254d918671Owen Lin && Util.equals(base.getPath(), getPathWithoutId(uri)); 184666ea1b28a76aeba74744148b15099254d918671Owen Lin } 185666ea1b28a76aeba74744148b15099254d918671Owen Lin 186666ea1b28a76aeba74744148b15099254d918671Owen Lin public IImage getImageForUri(Uri uri) { 187666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!isChildImageUri(uri)) return null; 188666ea1b28a76aeba74744148b15099254d918671Owen Lin // Find the id of the input URI. 189666ea1b28a76aeba74744148b15099254d918671Owen Lin long matchId; 190666ea1b28a76aeba74744148b15099254d918671Owen Lin try { 191666ea1b28a76aeba74744148b15099254d918671Owen Lin matchId = ContentUris.parseId(uri); 192666ea1b28a76aeba74744148b15099254d918671Owen Lin } catch (NumberFormatException ex) { 193666ea1b28a76aeba74744148b15099254d918671Owen Lin Log.i(TAG, "fail to get id in: " + uri, ex); 194666ea1b28a76aeba74744148b15099254d918671Owen Lin return null; 195666ea1b28a76aeba74744148b15099254d918671Owen Lin } 196666ea1b28a76aeba74744148b15099254d918671Owen Lin // TODO: design a better method to get URI of specified ID 197666ea1b28a76aeba74744148b15099254d918671Owen Lin Cursor cursor = getCursor(); 198666ea1b28a76aeba74744148b15099254d918671Owen Lin if (cursor == null) return null; 199666ea1b28a76aeba74744148b15099254d918671Owen Lin synchronized (this) { 200666ea1b28a76aeba74744148b15099254d918671Owen Lin cursor.moveToPosition(-1); // before first 201666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; cursor.moveToNext(); ++i) { 202666ea1b28a76aeba74744148b15099254d918671Owen Lin if (getImageId(cursor) == matchId) { 203666ea1b28a76aeba74744148b15099254d918671Owen Lin BaseImage image = mCache.get(i); 204666ea1b28a76aeba74744148b15099254d918671Owen Lin if (image == null) { 205666ea1b28a76aeba74744148b15099254d918671Owen Lin image = loadImageFromCursor(cursor); 206666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.put(i, image); 207666ea1b28a76aeba74744148b15099254d918671Owen Lin } 208666ea1b28a76aeba74744148b15099254d918671Owen Lin return image; 209666ea1b28a76aeba74744148b15099254d918671Owen Lin } 210666ea1b28a76aeba74744148b15099254d918671Owen Lin } 211666ea1b28a76aeba74744148b15099254d918671Owen Lin return null; 212666ea1b28a76aeba74744148b15099254d918671Owen Lin } 213666ea1b28a76aeba74744148b15099254d918671Owen Lin } 214666ea1b28a76aeba74744148b15099254d918671Owen Lin 215666ea1b28a76aeba74744148b15099254d918671Owen Lin public int getImageIndex(IImage image) { 216666ea1b28a76aeba74744148b15099254d918671Owen Lin return ((BaseImage) image).mIndex; 217666ea1b28a76aeba74744148b15099254d918671Owen Lin } 218666ea1b28a76aeba74744148b15099254d918671Owen Lin 219666ea1b28a76aeba74744148b15099254d918671Owen Lin // This provides a default sorting order string for subclasses. 220666ea1b28a76aeba74744148b15099254d918671Owen Lin // The list is first sorted by date, then by id. The order can be ascending 221666ea1b28a76aeba74744148b15099254d918671Owen Lin // or descending, depending on the mSort variable. 222666ea1b28a76aeba74744148b15099254d918671Owen Lin // The date is obtained from the "datetaken" column. But if it is null, 223666ea1b28a76aeba74744148b15099254d918671Owen Lin // the "date_modified" column is used instead. 224666ea1b28a76aeba74744148b15099254d918671Owen Lin protected String sortOrder() { 225666ea1b28a76aeba74744148b15099254d918671Owen Lin String ascending = 226666ea1b28a76aeba74744148b15099254d918671Owen Lin (mSort == ImageManager.SORT_ASCENDING) 227666ea1b28a76aeba74744148b15099254d918671Owen Lin ? " ASC" 228666ea1b28a76aeba74744148b15099254d918671Owen Lin : " DESC"; 229666ea1b28a76aeba74744148b15099254d918671Owen Lin 230666ea1b28a76aeba74744148b15099254d918671Owen Lin // Use DATE_TAKEN if it's non-null, otherwise use DATE_MODIFIED. 231666ea1b28a76aeba74744148b15099254d918671Owen Lin // DATE_TAKEN is in milliseconds, but DATE_MODIFIED is in seconds. 232666ea1b28a76aeba74744148b15099254d918671Owen Lin String dateExpr = 233666ea1b28a76aeba74744148b15099254d918671Owen Lin "case ifnull(datetaken,0)" + 234666ea1b28a76aeba74744148b15099254d918671Owen Lin " when 0 then date_modified*1000" + 235666ea1b28a76aeba74744148b15099254d918671Owen Lin " else datetaken" + 236666ea1b28a76aeba74744148b15099254d918671Owen Lin " end"; 237666ea1b28a76aeba74744148b15099254d918671Owen Lin 238666ea1b28a76aeba74744148b15099254d918671Owen Lin // Add id to the end so that we don't ever get random sorting 239666ea1b28a76aeba74744148b15099254d918671Owen Lin // which could happen, I suppose, if the date values are the same. 240666ea1b28a76aeba74744148b15099254d918671Owen Lin return dateExpr + ascending + ", _id" + ascending; 241666ea1b28a76aeba74744148b15099254d918671Owen Lin } 242666ea1b28a76aeba74744148b15099254d918671Owen Lin} 243