1 /* 2 * Copyright (C) 2009 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 17 package com.android.camera.gallery; 18 19 import com.android.camera.ImageManager; 20 21 import android.content.ContentResolver; 22 import android.content.ContentUris; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.util.Log; 26 27 /** 28 * A collection of <code>BaseImage</code>s. 29 */ 30 public abstract class BaseImageList implements IImageList { 31 private static final String TAG = "BaseImageList"; 32 private static final int CACHE_CAPACITY = 512; 33 private final LruCache<Integer, BaseImage> mCache = 34 new LruCache<Integer, BaseImage>(CACHE_CAPACITY); 35 36 protected ContentResolver mContentResolver; 37 protected int mSort; 38 39 protected Uri mBaseUri; 40 protected Cursor mCursor; 41 protected String mBucketId; 42 protected boolean mCursorDeactivated = false; 43 BaseImageList(ContentResolver resolver, Uri uri, int sort, String bucketId)44 public BaseImageList(ContentResolver resolver, Uri uri, int sort, 45 String bucketId) { 46 mSort = sort; 47 mBaseUri = uri; 48 mBucketId = bucketId; 49 mContentResolver = resolver; 50 mCursor = createCursor(); 51 52 if (mCursor == null) { 53 Log.w(TAG, "createCursor returns null."); 54 } 55 56 // TODO: We need to clear the cache because we may "reopen" the image 57 // list. After we implement the image list state, we can remove this 58 // kind of usage. 59 mCache.clear(); 60 } 61 close()62 public void close() { 63 try { 64 invalidateCursor(); 65 } catch (IllegalStateException e) { 66 // IllegalStateException may be thrown if the cursor is stale. 67 Log.e(TAG, "Caught exception while deactivating cursor.", e); 68 } 69 mContentResolver = null; 70 if (mCursor != null) { 71 mCursor.close(); 72 mCursor = null; 73 } 74 } 75 76 // TODO: Change public to protected contentUri(long id)77 public Uri contentUri(long id) { 78 // TODO: avoid using exception for most cases 79 try { 80 // does our uri already have an id (single image query)? 81 // if so just return it 82 long existingId = ContentUris.parseId(mBaseUri); 83 if (existingId != id) Log.e(TAG, "id mismatch"); 84 return mBaseUri; 85 } catch (NumberFormatException ex) { 86 // otherwise tack on the id 87 return ContentUris.withAppendedId(mBaseUri, id); 88 } 89 } 90 getCount()91 public int getCount() { 92 Cursor cursor = getCursor(); 93 if (cursor == null) return 0; 94 synchronized (this) { 95 return cursor.getCount(); 96 } 97 } 98 isEmpty()99 public boolean isEmpty() { 100 return getCount() == 0; 101 } 102 getCursor()103 private Cursor getCursor() { 104 synchronized (this) { 105 if (mCursor == null) return null; 106 if (mCursorDeactivated) { 107 mCursor.requery(); 108 mCursorDeactivated = false; 109 } 110 return mCursor; 111 } 112 } 113 getImageAt(int i)114 public IImage getImageAt(int i) { 115 BaseImage result = mCache.get(i); 116 if (result == null) { 117 Cursor cursor = getCursor(); 118 if (cursor == null) return null; 119 synchronized (this) { 120 result = cursor.moveToPosition(i) 121 ? loadImageFromCursor(cursor) 122 : null; 123 mCache.put(i, result); 124 } 125 } 126 return result; 127 } 128 createCursor()129 protected abstract Cursor createCursor(); 130 loadImageFromCursor(Cursor cursor)131 protected abstract BaseImage loadImageFromCursor(Cursor cursor); 132 invalidateCursor()133 protected void invalidateCursor() { 134 if (mCursor == null) return; 135 mCursor.deactivate(); 136 mCursorDeactivated = true; 137 } 138 139 // This provides a default sorting order string for subclasses. 140 // The list is first sorted by date, then by id. The order can be ascending 141 // or descending, depending on the mSort variable. 142 // The date is obtained from the "datetaken" column. But if it is null, 143 // the "date_modified" column is used instead. sortOrder()144 protected String sortOrder() { 145 String ascending = 146 (mSort == ImageManager.SORT_ASCENDING) 147 ? " ASC" 148 : " DESC"; 149 150 // Use DATE_TAKEN if it's non-null, otherwise use DATE_MODIFIED. 151 // DATE_TAKEN is in milliseconds, but DATE_MODIFIED is in seconds. 152 String dateExpr = 153 "case ifnull(datetaken,0)" + 154 " when 0 then date_modified*1000" + 155 " else datetaken" + 156 " end"; 157 158 // Add id to the end so that we don't ever get random sorting 159 // which could happen, I suppose, if the date values are the same. 160 return dateExpr + ascending + ", _id" + ascending; 161 } 162 } 163