• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LocalAlbum(Path path, GalleryApp application, int bucketId, boolean isImage, String name)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 
LocalAlbum(Path path, GalleryApp application, int bucketId, boolean isImage)86     public LocalAlbum(Path path, GalleryApp application, int bucketId,
87             boolean isImage) {
88         this(path, application, bucketId, isImage,
89                 BucketHelper.getBucketName(
90                 application.getContentResolver(), bucketId));
91     }
92 
93     @Override
isCameraRoll()94     public boolean isCameraRoll() {
95         return mBucketId == MediaSetUtils.CAMERA_BUCKET_ID;
96     }
97 
98     @Override
getContentUri()99     public Uri getContentUri() {
100         if (mIsImage) {
101             return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
102                     .appendQueryParameter(LocalSource.KEY_BUCKET_ID,
103                             String.valueOf(mBucketId)).build();
104         } else {
105             return MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon()
106                     .appendQueryParameter(LocalSource.KEY_BUCKET_ID,
107                             String.valueOf(mBucketId)).build();
108         }
109     }
110 
111     @Override
getMediaItem(int start, int count)112     public ArrayList<MediaItem> getMediaItem(int start, int count) {
113         DataManager dataManager = mApplication.getDataManager();
114         Uri uri = mBaseUri.buildUpon()
115                 .appendQueryParameter("limit", start + "," + count).build();
116         ArrayList<MediaItem> list = new ArrayList<MediaItem>();
117         GalleryUtils.assertNotInRenderThread();
118         Cursor cursor = mResolver.query(
119                 uri, mProjection, mWhereClause,
120                 new String[]{String.valueOf(mBucketId)},
121                 mOrderClause);
122         if (cursor == null) {
123             Log.w(TAG, "query fail: " + uri);
124             return list;
125         }
126 
127         try {
128             while (cursor.moveToNext()) {
129                 int id = cursor.getInt(0);  // _id must be in the first column
130                 Path childPath = mItemPath.getChild(id);
131                 MediaItem item = loadOrUpdateItem(childPath, cursor,
132                         dataManager, mApplication, mIsImage);
133                 list.add(item);
134             }
135         } finally {
136             cursor.close();
137         }
138         return list;
139     }
140 
loadOrUpdateItem(Path path, Cursor cursor, DataManager dataManager, GalleryApp app, boolean isImage)141     private static MediaItem loadOrUpdateItem(Path path, Cursor cursor,
142             DataManager dataManager, GalleryApp app, boolean isImage) {
143         synchronized (DataManager.LOCK) {
144             LocalMediaItem item = (LocalMediaItem) dataManager.peekMediaObject(path);
145             if (item == null) {
146                 if (isImage) {
147                     item = new LocalImage(path, app, cursor);
148                 } else {
149                     item = new LocalVideo(path, app, cursor);
150                 }
151             } else {
152                 item.updateContent(cursor);
153             }
154             return item;
155         }
156     }
157 
158     // The pids array are sorted by the (path) id.
getMediaItemById( GalleryApp application, boolean isImage, ArrayList<Integer> ids)159     public static MediaItem[] getMediaItemById(
160             GalleryApp application, boolean isImage, ArrayList<Integer> ids) {
161         // get the lower and upper bound of (path) id
162         MediaItem[] result = new MediaItem[ids.size()];
163         if (ids.isEmpty()) return result;
164         int idLow = ids.get(0);
165         int idHigh = ids.get(ids.size() - 1);
166 
167         // prepare the query parameters
168         Uri baseUri;
169         String[] projection;
170         Path itemPath;
171         if (isImage) {
172             baseUri = Images.Media.EXTERNAL_CONTENT_URI;
173             projection = LocalImage.PROJECTION;
174             itemPath = LocalImage.ITEM_PATH;
175         } else {
176             baseUri = Video.Media.EXTERNAL_CONTENT_URI;
177             projection = LocalVideo.PROJECTION;
178             itemPath = LocalVideo.ITEM_PATH;
179         }
180 
181         ContentResolver resolver = application.getContentResolver();
182         DataManager dataManager = application.getDataManager();
183         Cursor cursor = resolver.query(baseUri, projection, "_id BETWEEN ? AND ?",
184                 new String[]{String.valueOf(idLow), String.valueOf(idHigh)},
185                 "_id");
186         if (cursor == null) {
187             Log.w(TAG, "query fail" + baseUri);
188             return result;
189         }
190         try {
191             int n = ids.size();
192             int i = 0;
193 
194             while (i < n && cursor.moveToNext()) {
195                 int id = cursor.getInt(0);  // _id must be in the first column
196 
197                 // Match id with the one on the ids list.
198                 if (ids.get(i) > id) {
199                     continue;
200                 }
201 
202                 while (ids.get(i) < id) {
203                     if (++i >= n) {
204                         return result;
205                     }
206                 }
207 
208                 Path childPath = itemPath.getChild(id);
209                 MediaItem item = loadOrUpdateItem(childPath, cursor, dataManager,
210                         application, isImage);
211                 result[i] = item;
212                 ++i;
213             }
214             return result;
215         } finally {
216             cursor.close();
217         }
218     }
219 
getItemCursor(ContentResolver resolver, Uri uri, String[] projection, int id)220     public static Cursor getItemCursor(ContentResolver resolver, Uri uri,
221             String[] projection, int id) {
222         return resolver.query(uri, projection, "_id=?",
223                 new String[]{String.valueOf(id)}, null);
224     }
225 
226     @Override
getMediaItemCount()227     public int getMediaItemCount() {
228         if (mCachedCount == INVALID_COUNT) {
229             Cursor cursor = mResolver.query(
230                     mBaseUri, COUNT_PROJECTION, mWhereClause,
231                     new String[]{String.valueOf(mBucketId)}, null);
232             if (cursor == null) {
233                 Log.w(TAG, "query fail");
234                 return 0;
235             }
236             try {
237                 Utils.assertTrue(cursor.moveToNext());
238                 mCachedCount = cursor.getInt(0);
239             } finally {
240                 cursor.close();
241             }
242         }
243         return mCachedCount;
244     }
245 
246     @Override
getName()247     public String getName() {
248         return mName;
249     }
250 
251     @Override
reload()252     public long reload() {
253         if (mNotifier.isDirty()) {
254             mDataVersion = nextVersionNumber();
255             mCachedCount = INVALID_COUNT;
256         }
257         return mDataVersion;
258     }
259 
260     @Override
getSupportedOperations()261     public int getSupportedOperations() {
262         return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_INFO;
263     }
264 
265     @Override
delete()266     public void delete() {
267         GalleryUtils.assertNotInRenderThread();
268         mResolver.delete(mBaseUri, mWhereClause,
269                 new String[]{String.valueOf(mBucketId)});
270     }
271 
272     @Override
isLeafAlbum()273     public boolean isLeafAlbum() {
274         return true;
275     }
276 
getLocalizedName(Resources res, int bucketId, String name)277     public static String getLocalizedName(Resources res, int bucketId,
278             String name) {
279         if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) {
280             return res.getString(R.string.folder_camera);
281         } else if (bucketId == MediaSetUtils.DOWNLOAD_BUCKET_ID) {
282             return res.getString(R.string.folder_download);
283         } else if (bucketId == MediaSetUtils.IMPORTED_BUCKET_ID) {
284             return res.getString(R.string.folder_imported);
285         } else if (bucketId == MediaSetUtils.SNAPSHOT_BUCKET_ID) {
286             return res.getString(R.string.folder_screenshot);
287         } else if (bucketId == MediaSetUtils.EDITED_ONLINE_PHOTOS_BUCKET_ID) {
288             return res.getString(R.string.folder_edited_online_photos);
289         } else {
290             return name;
291         }
292     }
293 }
294