Home | History | Annotate | Download | only in data
      1 /*
      2  * Copyright (C) 2010 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.gallery3d.data;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.res.Resources;
     21 import android.database.Cursor;
     22 import android.net.Uri;
     23 import android.provider.MediaStore;
     24 import android.provider.MediaStore.Images;
     25 import android.provider.MediaStore.Images.ImageColumns;
     26 import android.provider.MediaStore.Video;
     27 import android.provider.MediaStore.Video.VideoColumns;
     28 
     29 import com.android.gallery3d.R;
     30 import com.android.gallery3d.app.GalleryApp;
     31 import com.android.gallery3d.common.Utils;
     32 import com.android.gallery3d.util.GalleryUtils;
     33 import com.android.gallery3d.util.MediaSetUtils;
     34 
     35 import java.util.ArrayList;
     36 
     37 // LocalAlbumSet lists all media items in one bucket on local storage.
     38 // The media items need to be all images or all videos, but not both.
     39 public class LocalAlbum extends MediaSet {
     40     private static final String TAG = "LocalAlbum";
     41     private static final String[] COUNT_PROJECTION = { "count(*)" };
     42 
     43     private static final int INVALID_COUNT = -1;
     44     private final String mWhereClause;
     45     private final String mOrderClause;
     46     private final Uri mBaseUri;
     47     private final String[] mProjection;
     48 
     49     private final GalleryApp mApplication;
     50     private final ContentResolver mResolver;
     51     private final int mBucketId;
     52     private final String mName;
     53     private final boolean mIsImage;
     54     private final ChangeNotifier mNotifier;
     55     private final Path mItemPath;
     56     private int mCachedCount = INVALID_COUNT;
     57 
     58     public LocalAlbum(Path path, GalleryApp application, int bucketId,
     59             boolean isImage, String name) {
     60         super(path, nextVersionNumber());
     61         mApplication = application;
     62         mResolver = application.getContentResolver();
     63         mBucketId = bucketId;
     64         mName = getLocalizedName(application.getResources(), bucketId, name);
     65         mIsImage = isImage;
     66 
     67         if (isImage) {
     68             mWhereClause = ImageColumns.BUCKET_ID + " = ?";
     69             mOrderClause = ImageColumns.DATE_TAKEN + " DESC, "
     70                     + ImageColumns._ID + " DESC";
     71             mBaseUri = Images.Media.EXTERNAL_CONTENT_URI;
     72             mProjection = LocalImage.PROJECTION;
     73             mItemPath = LocalImage.ITEM_PATH;
     74         } else {
     75             mWhereClause = VideoColumns.BUCKET_ID + " = ?";
     76             mOrderClause = VideoColumns.DATE_TAKEN + " DESC, "
     77                     + VideoColumns._ID + " DESC";
     78             mBaseUri = Video.Media.EXTERNAL_CONTENT_URI;
     79             mProjection = LocalVideo.PROJECTION;
     80             mItemPath = LocalVideo.ITEM_PATH;
     81         }
     82 
     83         mNotifier = new ChangeNotifier(this, mBaseUri, application);
     84     }
     85 
     86     public LocalAlbum(Path path, GalleryApp application, int bucketId,
     87             boolean isImage) {
     88         this(path, application, bucketId, isImage,
     89                 LocalAlbumSet.getBucketName(application.getContentResolver(),
     90                 bucketId));
     91     }
     92 
     93     @Override
     94     public Uri getContentUri() {
     95         if (mIsImage) {
     96             return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
     97                     .appendQueryParameter(LocalSource.KEY_BUCKET_ID,
     98                             String.valueOf(mBucketId)).build();
     99         } else {
    100             return MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon()
    101                     .appendQueryParameter(LocalSource.KEY_BUCKET_ID,
    102                             String.valueOf(mBucketId)).build();
    103         }
    104     }
    105 
    106     @Override
    107     public ArrayList<MediaItem> getMediaItem(int start, int count) {
    108         DataManager dataManager = mApplication.getDataManager();
    109         Uri uri = mBaseUri.buildUpon()
    110                 .appendQueryParameter("limit", start + "," + count).build();
    111         ArrayList<MediaItem> list = new ArrayList<MediaItem>();
    112         GalleryUtils.assertNotInRenderThread();
    113         Cursor cursor = mResolver.query(
    114                 uri, mProjection, mWhereClause,
    115                 new String[]{String.valueOf(mBucketId)},
    116                 mOrderClause);
    117         if (cursor == null) {
    118             Log.w(TAG, "query fail: " + uri);
    119             return list;
    120         }
    121 
    122         try {
    123             while (cursor.moveToNext()) {
    124                 int id = cursor.getInt(0);  // _id must be in the first column
    125                 Path childPath = mItemPath.getChild(id);
    126                 MediaItem item = loadOrUpdateItem(childPath, cursor,
    127                         dataManager, mApplication, mIsImage);
    128                 list.add(item);
    129             }
    130         } finally {
    131             cursor.close();
    132         }
    133         return list;
    134     }
    135 
    136     private static MediaItem loadOrUpdateItem(Path path, Cursor cursor,
    137             DataManager dataManager, GalleryApp app, boolean isImage) {
    138         LocalMediaItem item = (LocalMediaItem) dataManager.peekMediaObject(path);
    139         if (item == null) {
    140             if (isImage) {
    141                 item = new LocalImage(path, app, cursor);
    142             } else {
    143                 item = new LocalVideo(path, app, cursor);
    144             }
    145         } else {
    146             item.updateContent(cursor);
    147         }
    148         return item;
    149     }
    150 
    151     // The pids array are sorted by the (path) id.
    152     public static MediaItem[] getMediaItemById(
    153             GalleryApp application, boolean isImage, ArrayList<Integer> ids) {
    154         // get the lower and upper bound of (path) id
    155         MediaItem[] result = new MediaItem[ids.size()];
    156         if (ids.isEmpty()) return result;
    157         int idLow = ids.get(0);
    158         int idHigh = ids.get(ids.size() - 1);
    159 
    160         // prepare the query parameters
    161         Uri baseUri;
    162         String[] projection;
    163         Path itemPath;
    164         if (isImage) {
    165             baseUri = Images.Media.EXTERNAL_CONTENT_URI;
    166             projection = LocalImage.PROJECTION;
    167             itemPath = LocalImage.ITEM_PATH;
    168         } else {
    169             baseUri = Video.Media.EXTERNAL_CONTENT_URI;
    170             projection = LocalVideo.PROJECTION;
    171             itemPath = LocalVideo.ITEM_PATH;
    172         }
    173 
    174         ContentResolver resolver = application.getContentResolver();
    175         DataManager dataManager = application.getDataManager();
    176         Cursor cursor = resolver.query(baseUri, projection, "_id BETWEEN ? AND ?",
    177                 new String[]{String.valueOf(idLow), String.valueOf(idHigh)},
    178                 "_id");
    179         if (cursor == null) {
    180             Log.w(TAG, "query fail" + baseUri);
    181             return result;
    182         }
    183         try {
    184             int n = ids.size();
    185             int i = 0;
    186 
    187             while (i < n && cursor.moveToNext()) {
    188                 int id = cursor.getInt(0);  // _id must be in the first column
    189 
    190                 // Match id with the one on the ids list.
    191                 if (ids.get(i) > id) {
    192                     continue;
    193                 }
    194 
    195                 while (ids.get(i) < id) {
    196                     if (++i >= n) {
    197                         return result;
    198                     }
    199                 }
    200 
    201                 Path childPath = itemPath.getChild(id);
    202                 MediaItem item = loadOrUpdateItem(childPath, cursor, dataManager,
    203                         application, isImage);
    204                 result[i] = item;
    205                 ++i;
    206             }
    207             return result;
    208         } finally {
    209             cursor.close();
    210         }
    211     }
    212 
    213     public static Cursor getItemCursor(ContentResolver resolver, Uri uri,
    214             String[] projection, int id) {
    215         return resolver.query(uri, projection, "_id=?",
    216                 new String[]{String.valueOf(id)}, null);
    217     }
    218 
    219     @Override
    220     public int getMediaItemCount() {
    221         if (mCachedCount == INVALID_COUNT) {
    222             Cursor cursor = mResolver.query(
    223                     mBaseUri, COUNT_PROJECTION, mWhereClause,
    224                     new String[]{String.valueOf(mBucketId)}, null);
    225             if (cursor == null) {
    226                 Log.w(TAG, "query fail");
    227                 return 0;
    228             }
    229             try {
    230                 Utils.assertTrue(cursor.moveToNext());
    231                 mCachedCount = cursor.getInt(0);
    232             } finally {
    233                 cursor.close();
    234             }
    235         }
    236         return mCachedCount;
    237     }
    238 
    239     @Override
    240     public String getName() {
    241         return mName;
    242     }
    243 
    244     @Override
    245     public long reload() {
    246         if (mNotifier.isDirty()) {
    247             mDataVersion = nextVersionNumber();
    248             mCachedCount = INVALID_COUNT;
    249         }
    250         return mDataVersion;
    251     }
    252 
    253     @Override
    254     public int getSupportedOperations() {
    255         return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_INFO;
    256     }
    257 
    258     @Override
    259     public void delete() {
    260         GalleryUtils.assertNotInRenderThread();
    261         mResolver.delete(mBaseUri, mWhereClause,
    262                 new String[]{String.valueOf(mBucketId)});
    263         mApplication.getDataManager().broadcastLocalDeletion();
    264     }
    265 
    266     @Override
    267     public boolean isLeafAlbum() {
    268         return true;
    269     }
    270 
    271     private static String getLocalizedName(Resources res, int bucketId,
    272             String name) {
    273         if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) {
    274             return res.getString(R.string.folder_camera);
    275         } else if (bucketId == MediaSetUtils.DOWNLOAD_BUCKET_ID) {
    276             return res.getString(R.string.folder_download);
    277         } else if (bucketId == MediaSetUtils.IMPORTED_BUCKET_ID) {
    278             return res.getString(R.string.folder_imported);
    279         } else if (bucketId == MediaSetUtils.SNAPSHOT_BUCKET_ID) {
    280             return res.getString(R.string.folder_screenshot);
    281         } else {
    282             return name;
    283         }
    284     }
    285 }
    286