blob: f45eebfd81acfa5831e0adace0e4b70dc0d3d28e [file] [log] [blame]
Owen Linf9a0a432011-08-17 22:07:43 +08001/*
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
17package com.android.gallery3d.data;
18
Owen Linf9a0a432011-08-17 22:07:43 +080019import android.content.ContentResolver;
Chih-Chung Chang6caf2212011-10-24 15:56:30 +080020import android.content.res.Resources;
Owen Linf9a0a432011-08-17 22:07:43 +080021import android.database.Cursor;
22import android.net.Uri;
Ray Chen84c220f2012-03-06 17:24:28 +080023import android.provider.MediaStore;
Owen Linf9a0a432011-08-17 22:07:43 +080024import android.provider.MediaStore.Images;
25import android.provider.MediaStore.Images.ImageColumns;
26import android.provider.MediaStore.Video;
27import android.provider.MediaStore.Video.VideoColumns;
28
Owen Lin2b3ee0e2012-03-14 17:27:24 +080029import com.android.gallery3d.R;
30import com.android.gallery3d.app.GalleryApp;
31import com.android.gallery3d.common.Utils;
32import com.android.gallery3d.util.GalleryUtils;
33import com.android.gallery3d.util.MediaSetUtils;
34
Owen Linf9a0a432011-08-17 22:07:43 +080035import 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.
39public 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;
Chih-Chung Chang6caf2212011-10-24 15:56:30 +080052 private final String mName;
Owen Linf9a0a432011-08-17 22:07:43 +080053 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;
Chih-Chung Chang6caf2212011-10-24 15:56:30 +080064 mName = getLocalizedName(application.getResources(), bucketId, name);
Owen Linf9a0a432011-08-17 22:07:43 +080065 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
Ray Chen84c220f2012-03-06 17:24:28 +080094 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
Owen Linf9a0a432011-08-17 22:07:43 +0800107 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() {
Chih-Chung Chang6caf2212011-10-24 15:56:30 +0800241 return mName;
Owen Linf9a0a432011-08-17 22:07:43 +0800242 }
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)});
Chih-Chung Changc7e89da2012-06-26 19:12:09 +0800263 mApplication.getDataManager().broadcastLocalDeletion();
Owen Linf9a0a432011-08-17 22:07:43 +0800264 }
265
266 @Override
267 public boolean isLeafAlbum() {
268 return true;
269 }
Chih-Chung Chang6caf2212011-10-24 15:56:30 +0800270
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 }
Owen Linf9a0a432011-08-17 22:07:43 +0800285}