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