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