Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2009 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.provider;
     18 
     19 import com.android.gallery3d.app.GalleryApp;
     20 import com.android.gallery3d.common.Utils;
     21 import com.android.gallery3d.data.DataManager;
     22 import com.android.gallery3d.data.DownloadCache;
     23 import com.android.gallery3d.data.MediaItem;
     24 import com.android.gallery3d.data.MediaObject;
     25 import com.android.gallery3d.data.MtpImage;
     26 import com.android.gallery3d.data.Path;
     27 import com.android.gallery3d.picasasource.PicasaSource;
     28 import com.android.gallery3d.util.GalleryUtils;
     29 
     30 import android.content.ContentProvider;
     31 import android.content.ContentValues;
     32 import android.content.Context;
     33 import android.database.Cursor;
     34 import android.database.MatrixCursor;
     35 import android.net.Uri;
     36 import android.os.Binder;
     37 import android.os.Bundle;
     38 import android.os.ParcelFileDescriptor;
     39 import android.provider.MediaStore.Images.ImageColumns;
     40 import android.util.Log;
     41 
     42 import java.io.FileNotFoundException;
     43 import java.io.IOException;
     44 import java.io.OutputStream;
     45 
     46 public class GalleryProvider extends ContentProvider {
     47     private static final String TAG = "GalleryProvider";
     48 
     49     public static final String AUTHORITY = "com.android.gallery3d.provider";
     50     public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
     51     private static final String[] SUPPORTED_PICASA_COLUMNS = {
     52             ImageColumns.DISPLAY_NAME,
     53             ImageColumns.SIZE,
     54             ImageColumns.MIME_TYPE,
     55             ImageColumns.DATE_TAKEN,
     56             ImageColumns.LATITUDE,
     57             ImageColumns.LONGITUDE,
     58             ImageColumns.ORIENTATION};
     59 
     60     private DataManager mDataManager;
     61     private DownloadCache mDownloadCache;
     62     private static Uri sBaseUri;
     63 
     64     public static String getAuthority(Context context) {
     65         return context.getPackageName() + ".provider";
     66     }
     67 
     68     public static Uri getUriFor(Context context, Path path) {
     69         if (sBaseUri == null) {
     70             sBaseUri = Uri.parse("content://" + context.getPackageName() + ".provider");
     71         }
     72         return sBaseUri.buildUpon()
     73                 .appendEncodedPath(path.toString().substring(1)) // ignore the leading '/'
     74                 .build();
     75     }
     76 
     77     @Override
     78     public int delete(Uri uri, String selection, String[] selectionArgs) {
     79         throw new UnsupportedOperationException();
     80     }
     81 
     82     // TODO: consider concurrent access
     83     @Override
     84     public String getType(Uri uri) {
     85         long token = Binder.clearCallingIdentity();
     86         try {
     87             Path path = Path.fromString(uri.getPath());
     88             MediaItem item = (MediaItem) mDataManager.getMediaObject(path);
     89             return item != null ? item.getMimeType() : null;
     90         } finally {
     91             Binder.restoreCallingIdentity(token);
     92         }
     93     }
     94 
     95     @Override
     96     public Uri insert(Uri uri, ContentValues values) {
     97         throw new UnsupportedOperationException();
     98     }
     99 
    100     @Override
    101     public boolean onCreate() {
    102         GalleryApp app = (GalleryApp) getContext().getApplicationContext();
    103         mDataManager = app.getDataManager();
    104         return true;
    105     }
    106 
    107     private DownloadCache getDownloadCache() {
    108         if (mDownloadCache == null) {
    109             GalleryApp app = (GalleryApp) getContext().getApplicationContext();
    110             mDownloadCache = app.getDownloadCache();
    111         }
    112         return mDownloadCache;
    113     }
    114 
    115     // TODO: consider concurrent access
    116     @Override
    117     public Cursor query(Uri uri, String[] projection,
    118             String selection, String[] selectionArgs, String sortOrder) {
    119         long token = Binder.clearCallingIdentity();
    120         try {
    121             Path path = Path.fromString(uri.getPath());
    122             MediaObject object = mDataManager.getMediaObject(path);
    123             if (object == null) {
    124                 Log.w(TAG, "cannot find: " + uri);
    125                 return null;
    126             }
    127             if (PicasaSource.isPicasaImage(object)) {
    128                 return queryPicasaItem(object,
    129                         projection, selection, selectionArgs, sortOrder);
    130             } else if (object instanceof MtpImage) {
    131                 return queryMtpItem((MtpImage) object,
    132                         projection, selection, selectionArgs, sortOrder);
    133             } else {
    134                     return null;
    135             }
    136         } finally {
    137             Binder.restoreCallingIdentity(token);
    138         }
    139     }
    140 
    141     private Cursor queryMtpItem(MtpImage image, String[] projection,
    142             String selection, String[] selectionArgs, String sortOrder) {
    143         Object[] columnValues = new Object[projection.length];
    144         for (int i = 0, n = projection.length; i < n; ++i) {
    145             String column = projection[i];
    146             if (ImageColumns.DISPLAY_NAME.equals(column)) {
    147                 columnValues[i] = image.getName();
    148             } else if (ImageColumns.SIZE.equals(column)){
    149                 columnValues[i] = image.getSize();
    150             } else if (ImageColumns.MIME_TYPE.equals(column)) {
    151                 columnValues[i] = image.getMimeType();
    152             } else if (ImageColumns.DATE_TAKEN.equals(column)) {
    153                 columnValues[i] = image.getDateInMs();
    154             } else {
    155                 Log.w(TAG, "unsupported column: " + column);
    156             }
    157         }
    158         MatrixCursor cursor = new MatrixCursor(projection);
    159         cursor.addRow(columnValues);
    160         return cursor;
    161     }
    162 
    163     private Cursor queryPicasaItem(MediaObject image, String[] projection,
    164             String selection, String[] selectionArgs, String sortOrder) {
    165         if (projection == null) projection = SUPPORTED_PICASA_COLUMNS;
    166         Object[] columnValues = new Object[projection.length];
    167         double latitude = PicasaSource.getLatitude(image);
    168         double longitude = PicasaSource.getLongitude(image);
    169         boolean isValidLatlong = GalleryUtils.isValidLocation(latitude, longitude);
    170 
    171         for (int i = 0, n = projection.length; i < n; ++i) {
    172             String column = projection[i];
    173             if (ImageColumns.DISPLAY_NAME.equals(column)) {
    174                 columnValues[i] = PicasaSource.getImageTitle(image);
    175             } else if (ImageColumns.SIZE.equals(column)){
    176                 columnValues[i] = PicasaSource.getImageSize(image);
    177             } else if (ImageColumns.MIME_TYPE.equals(column)) {
    178                 columnValues[i] = PicasaSource.getContentType(image);
    179             } else if (ImageColumns.DATE_TAKEN.equals(column)) {
    180                 columnValues[i] = PicasaSource.getDateTaken(image);
    181             } else if (ImageColumns.LATITUDE.equals(column)) {
    182                 columnValues[i] = isValidLatlong ? latitude : null;
    183             } else if (ImageColumns.LONGITUDE.equals(column)) {
    184                 columnValues[i] = isValidLatlong ? longitude : null;
    185             } else if (ImageColumns.ORIENTATION.equals(column)) {
    186                 columnValues[i] = PicasaSource.getRotation(image);
    187             } else {
    188                 Log.w(TAG, "unsupported column: " + column);
    189             }
    190         }
    191         MatrixCursor cursor = new MatrixCursor(projection);
    192         cursor.addRow(columnValues);
    193         return cursor;
    194     }
    195 
    196     @Override
    197     public ParcelFileDescriptor openFile(Uri uri, String mode)
    198             throws FileNotFoundException {
    199         long token = Binder.clearCallingIdentity();
    200         try {
    201             if (mode.contains("w")) {
    202                 throw new FileNotFoundException("cannot open file for write");
    203             }
    204             Path path = Path.fromString(uri.getPath());
    205             MediaObject object = mDataManager.getMediaObject(path);
    206             if (object == null) {
    207                 throw new FileNotFoundException(uri.toString());
    208             }
    209             if (PicasaSource.isPicasaImage(object)) {
    210                 return PicasaSource.openFile(getContext(), object, mode);
    211             } else if (object instanceof MtpImage) {
    212                 return openPipeHelper(uri, null, null, null,
    213                         new MtpPipeDataWriter((MtpImage) object));
    214             } else {
    215                 throw new FileNotFoundException("unspported type: " + object);
    216             }
    217         } finally {
    218             Binder.restoreCallingIdentity(token);
    219         }
    220     }
    221 
    222     @Override
    223     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    224         throw new UnsupportedOperationException();
    225     }
    226 
    227     private final class MtpPipeDataWriter implements PipeDataWriter<Object> {
    228         private final MtpImage mImage;
    229 
    230         private MtpPipeDataWriter(MtpImage image) {
    231             mImage = image;
    232         }
    233 
    234         @Override
    235         public void writeDataToPipe(ParcelFileDescriptor output,
    236                 Uri uri, String mimeType, Bundle opts, Object args) {
    237             OutputStream os = null;
    238             try {
    239                 os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
    240                 os.write(mImage.getImageData());
    241             } catch (IOException e) {
    242                 Log.w(TAG, "fail to download: " + uri, e);
    243             } finally {
    244                 Utils.closeSilently(os);
    245             }
    246         }
    247     }
    248 }
    249