2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.data;
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;
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;
38 import java.util.ArrayList;
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(*)" };
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;
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;
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();
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;
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;
86 mNotifier = new ChangeNotifier(this, mBaseUri, application);
89 public LocalAlbum(Path path, GalleryApp application, int bucketId,
91 this(path, application, bucketId, isImage,
92 BucketHelper.getBucketName(
93 application.getContentResolver(), bucketId));
97 public boolean isCameraRoll() {
98 return mBucketId == MediaSetUtils.CAMERA_BUCKET_ID;
102 public Uri getContentUri() {
104 return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
105 .appendQueryParameter(LocalSource.KEY_BUCKET_ID,
106 String.valueOf(mBucketId)).build();
108 return MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon()
109 .appendQueryParameter(LocalSource.KEY_BUCKET_ID,
110 String.valueOf(mBucketId)).build();
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)},
125 if (cursor == null) {
126 Log.w(TAG, "query fail: " + uri);
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);
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);
150 item = new LocalImage(path, app, cursor);
152 item = new LocalVideo(path, app, cursor);
155 item.updateContent(cursor);
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);
170 // prepare the query parameters
175 baseUri = Images.Media.EXTERNAL_CONTENT_URI;
176 projection = LocalImage.PROJECTION;
177 itemPath = LocalImage.ITEM_PATH;
179 baseUri = Video.Media.EXTERNAL_CONTENT_URI;
180 projection = LocalVideo.PROJECTION;
181 itemPath = LocalVideo.ITEM_PATH;
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)},
189 if (cursor == null) {
190 Log.w(TAG, "query fail" + baseUri);
197 while (i < n && cursor.moveToNext()) {
198 int id = cursor.getInt(0); // _id must be in the first column
200 // Match id with the one on the ids list.
201 if (ids.get(i) > id) {
205 while (ids.get(i) < id) {
211 Path childPath = itemPath.getChild(id);
212 MediaItem item = loadOrUpdateItem(childPath, cursor, dataManager,
213 application, isImage);
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);
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");
240 Utils.assertTrue(cursor.moveToNext());
241 mCachedCount = cursor.getInt(0);
250 public String getName() {
251 return getLocalizedName(mApplication.getResources(), mBucketId, mName);
255 public long reload() {
256 if (mNotifier.isDirty()) {
257 mDataVersion = nextVersionNumber();
258 mCachedCount = INVALID_COUNT;
264 public int getSupportedOperations() {
265 return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_INFO;
269 public void delete() {
270 GalleryUtils.assertNotInRenderThread();
271 mResolver.delete(mBaseUri, mWhereClause,
272 new String[]{String.valueOf(mBucketId)});
276 public boolean isLeafAlbum() {
280 public static String getLocalizedName(Resources res, int bucketId,
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);
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;
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);
316 Log.w(TAG, "Relative path for bucket id: " + bucketId + " is not found.");
319 relativePath = path.substring(extStorage.getAbsolutePath().length());