• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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