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