Home | History | Annotate | Download | only in data
      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