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