Home | History | Annotate | Download | only in gallery
      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 
     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 
     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
     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 
     91     public int getCount() {
     92         Cursor cursor = getCursor();
     93         if (cursor == null) return 0;
     94         synchronized (this) {
     95             return cursor.getCount();
     96         }
     97     }
     98 
     99     public boolean isEmpty() {
    100         return getCount() == 0;
    101     }
    102 
    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 
    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 
    129     protected abstract Cursor createCursor();
    130 
    131     protected abstract BaseImage loadImageFromCursor(Cursor cursor);
    132 
    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.
    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