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.ContentProviderClient; 20 import android.content.ContentUris; 21 import android.content.UriMatcher; 22 import android.net.Uri; 23 import android.provider.MediaStore; 24 25 import com.android.gallery3d.app.Gallery; 26 import com.android.gallery3d.app.GalleryApp; 27 import com.android.gallery3d.data.MediaSet.ItemConsumer; 28 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.Comparator; 32 33 class LocalSource extends MediaSource { 34 35 public static final String KEY_BUCKET_ID = "bucketId"; 36 37 private GalleryApp mApplication; 38 private PathMatcher mMatcher; 39 private static final int NO_MATCH = -1; 40 private final UriMatcher mUriMatcher = new UriMatcher(NO_MATCH); 41 public static final Comparator<PathId> sIdComparator = new IdComparator(); 42 43 private static final int LOCAL_IMAGE_ALBUMSET = 0; 44 private static final int LOCAL_VIDEO_ALBUMSET = 1; 45 private static final int LOCAL_IMAGE_ALBUM = 2; 46 private static final int LOCAL_VIDEO_ALBUM = 3; 47 private static final int LOCAL_IMAGE_ITEM = 4; 48 private static final int LOCAL_VIDEO_ITEM = 5; 49 private static final int LOCAL_ALL_ALBUMSET = 6; 50 private static final int LOCAL_ALL_ALBUM = 7; 51 52 private static final String TAG = "LocalSource"; 53 54 private ContentProviderClient mClient; 55 56 public LocalSource(GalleryApp context) { 57 super("local"); 58 mApplication = context; 59 mMatcher = new PathMatcher(); 60 mMatcher.add("/local/image", LOCAL_IMAGE_ALBUMSET); 61 mMatcher.add("/local/video", LOCAL_VIDEO_ALBUMSET); 62 mMatcher.add("/local/all", LOCAL_ALL_ALBUMSET); 63 64 mMatcher.add("/local/image/*", LOCAL_IMAGE_ALBUM); 65 mMatcher.add("/local/video/*", LOCAL_VIDEO_ALBUM); 66 mMatcher.add("/local/all/*", LOCAL_ALL_ALBUM); 67 mMatcher.add("/local/image/item/*", LOCAL_IMAGE_ITEM); 68 mMatcher.add("/local/video/item/*", LOCAL_VIDEO_ITEM); 69 70 mUriMatcher.addURI(MediaStore.AUTHORITY, 71 "external/images/media/#", LOCAL_IMAGE_ITEM); 72 mUriMatcher.addURI(MediaStore.AUTHORITY, 73 "external/video/media/#", LOCAL_VIDEO_ITEM); 74 mUriMatcher.addURI(MediaStore.AUTHORITY, 75 "external/images/media", LOCAL_IMAGE_ALBUM); 76 mUriMatcher.addURI(MediaStore.AUTHORITY, 77 "external/video/media", LOCAL_VIDEO_ALBUM); 78 mUriMatcher.addURI(MediaStore.AUTHORITY, 79 "external/file", LOCAL_ALL_ALBUM); 80 } 81 82 @Override 83 public MediaObject createMediaObject(Path path) { 84 GalleryApp app = mApplication; 85 switch (mMatcher.match(path)) { 86 case LOCAL_ALL_ALBUMSET: 87 case LOCAL_IMAGE_ALBUMSET: 88 case LOCAL_VIDEO_ALBUMSET: 89 return new LocalAlbumSet(path, mApplication); 90 case LOCAL_IMAGE_ALBUM: 91 return new LocalAlbum(path, app, mMatcher.getIntVar(0), true); 92 case LOCAL_VIDEO_ALBUM: 93 return new LocalAlbum(path, app, mMatcher.getIntVar(0), false); 94 case LOCAL_ALL_ALBUM: { 95 int bucketId = mMatcher.getIntVar(0); 96 DataManager dataManager = app.getDataManager(); 97 MediaSet imageSet = (MediaSet) dataManager.getMediaObject( 98 LocalAlbumSet.PATH_IMAGE.getChild(bucketId)); 99 MediaSet videoSet = (MediaSet) dataManager.getMediaObject( 100 LocalAlbumSet.PATH_VIDEO.getChild(bucketId)); 101 Comparator<MediaItem> comp = DataManager.sDateTakenComparator; 102 return new LocalMergeAlbum( 103 path, comp, new MediaSet[] {imageSet, videoSet}, bucketId); 104 } 105 case LOCAL_IMAGE_ITEM: 106 return new LocalImage(path, mApplication, mMatcher.getIntVar(0)); 107 case LOCAL_VIDEO_ITEM: 108 return new LocalVideo(path, mApplication, mMatcher.getIntVar(0)); 109 default: 110 throw new RuntimeException("bad path: " + path); 111 } 112 } 113 114 private static int getMediaType(String type, int defaultType) { 115 if (type == null) return defaultType; 116 try { 117 int value = Integer.parseInt(type); 118 if ((value & (MEDIA_TYPE_IMAGE 119 | MEDIA_TYPE_VIDEO)) != 0) return value; 120 } catch (NumberFormatException e) { 121 Log.w(TAG, "invalid type: " + type, e); 122 } 123 return defaultType; 124 } 125 126 // The media type bit passed by the intent 127 private static final int MEDIA_TYPE_ALL = 0; 128 private static final int MEDIA_TYPE_IMAGE = 1; 129 private static final int MEDIA_TYPE_VIDEO = 4; 130 131 private Path getAlbumPath(Uri uri, int defaultType) { 132 int mediaType = getMediaType( 133 uri.getQueryParameter(Gallery.KEY_MEDIA_TYPES), 134 defaultType); 135 String bucketId = uri.getQueryParameter(KEY_BUCKET_ID); 136 int id = 0; 137 try { 138 id = Integer.parseInt(bucketId); 139 } catch (NumberFormatException e) { 140 Log.w(TAG, "invalid bucket id: " + bucketId, e); 141 return null; 142 } 143 switch (mediaType) { 144 case MEDIA_TYPE_IMAGE: 145 return Path.fromString("/local/image").getChild(id); 146 case MEDIA_TYPE_VIDEO: 147 return Path.fromString("/local/video").getChild(id); 148 default: 149 return Path.fromString("/local/all").getChild(id); 150 } 151 } 152 153 @Override 154 public Path findPathByUri(Uri uri, String type) { 155 try { 156 switch (mUriMatcher.match(uri)) { 157 case LOCAL_IMAGE_ITEM: { 158 long id = ContentUris.parseId(uri); 159 return id >= 0 ? LocalImage.ITEM_PATH.getChild(id) : null; 160 } 161 case LOCAL_VIDEO_ITEM: { 162 long id = ContentUris.parseId(uri); 163 return id >= 0 ? LocalVideo.ITEM_PATH.getChild(id) : null; 164 } 165 case LOCAL_IMAGE_ALBUM: { 166 return getAlbumPath(uri, MEDIA_TYPE_IMAGE); 167 } 168 case LOCAL_VIDEO_ALBUM: { 169 return getAlbumPath(uri, MEDIA_TYPE_VIDEO); 170 } 171 case LOCAL_ALL_ALBUM: { 172 return getAlbumPath(uri, MEDIA_TYPE_ALL); 173 } 174 } 175 } catch (NumberFormatException e) { 176 Log.w(TAG, "uri: " + uri.toString(), e); 177 } 178 return null; 179 } 180 181 @Override 182 public Path getDefaultSetOf(Path item) { 183 MediaObject object = mApplication.getDataManager().getMediaObject(item); 184 if (object instanceof LocalMediaItem) { 185 return Path.fromString("/local/all").getChild( 186 String.valueOf(((LocalMediaItem) object).getBucketId())); 187 } 188 return null; 189 } 190 191 @Override 192 public void mapMediaItems(ArrayList<PathId> list, ItemConsumer consumer) { 193 ArrayList<PathId> imageList = new ArrayList<PathId>(); 194 ArrayList<PathId> videoList = new ArrayList<PathId>(); 195 int n = list.size(); 196 for (int i = 0; i < n; i++) { 197 PathId pid = list.get(i); 198 // We assume the form is: "/local/{image,video}/item/#" 199 // We don't use mMatcher for efficiency's reason. 200 Path parent = pid.path.getParent(); 201 if (parent == LocalImage.ITEM_PATH) { 202 imageList.add(pid); 203 } else if (parent == LocalVideo.ITEM_PATH) { 204 videoList.add(pid); 205 } 206 } 207 // TODO: use "files" table so we can merge the two cases. 208 processMapMediaItems(imageList, consumer, true); 209 processMapMediaItems(videoList, consumer, false); 210 } 211 212 private void processMapMediaItems(ArrayList<PathId> list, 213 ItemConsumer consumer, boolean isImage) { 214 // Sort path by path id 215 Collections.sort(list, sIdComparator); 216 int n = list.size(); 217 for (int i = 0; i < n; ) { 218 PathId pid = list.get(i); 219 220 // Find a range of items. 221 ArrayList<Integer> ids = new ArrayList<Integer>(); 222 int startId = Integer.parseInt(pid.path.getSuffix()); 223 ids.add(startId); 224 225 int j; 226 for (j = i + 1; j < n; j++) { 227 PathId pid2 = list.get(j); 228 int curId = Integer.parseInt(pid2.path.getSuffix()); 229 if (curId - startId >= MediaSet.MEDIAITEM_BATCH_FETCH_COUNT) { 230 break; 231 } 232 ids.add(curId); 233 } 234 235 MediaItem[] items = LocalAlbum.getMediaItemById( 236 mApplication, isImage, ids); 237 for(int k = i ; k < j; k++) { 238 PathId pid2 = list.get(k); 239 consumer.consume(pid2.id, items[k - i]); 240 } 241 242 i = j; 243 } 244 } 245 246 // This is a comparator which compares the suffix number in two Paths. 247 private static class IdComparator implements Comparator<PathId> { 248 @Override 249 public int compare(PathId p1, PathId p2) { 250 String s1 = p1.path.getSuffix(); 251 String s2 = p2.path.getSuffix(); 252 int len1 = s1.length(); 253 int len2 = s2.length(); 254 if (len1 < len2) { 255 return -1; 256 } else if (len1 > len2) { 257 return 1; 258 } else { 259 return s1.compareTo(s2); 260 } 261 } 262 } 263 264 @Override 265 public void resume() { 266 mClient = mApplication.getContentResolver() 267 .acquireContentProviderClient(MediaStore.AUTHORITY); 268 } 269 270 @Override 271 public void pause() { 272 mClient.release(); 273 mClient = null; 274 } 275 } 276