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