AttachmentProvider.java revision a290f503f14432163f74548a5e5d1dc5003ad049
1/* 2 * Copyright (C) 2008 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 17package com.android.email.provider; 18 19import com.android.email.mail.internet.MimeUtility; 20 21import android.content.ContentProvider; 22import android.content.ContentResolver; 23import android.content.ContentValues; 24import android.database.Cursor; 25import android.database.MatrixCursor; 26import android.database.sqlite.SQLiteDatabase; 27import android.graphics.Bitmap; 28import android.graphics.BitmapFactory; 29import android.net.Uri; 30import android.os.ParcelFileDescriptor; 31 32import java.io.File; 33import java.io.FileNotFoundException; 34import java.io.FileOutputStream; 35import java.io.IOException; 36import java.io.InputStream; 37import java.util.List; 38 39/* 40 * A simple ContentProvider that allows file access to Email's attachments. 41 */ 42public class AttachmentProvider extends ContentProvider { 43 public static final Uri CONTENT_URI = Uri.parse( "content://com.android.email.attachmentprovider"); 44 45 private static final String FORMAT_RAW = "RAW"; 46 private static final String FORMAT_THUMBNAIL = "THUMBNAIL"; 47 48 public static class AttachmentProviderColumns { 49 public static final String _ID = "_id"; 50 public static final String DATA = "_data"; 51 public static final String DISPLAY_NAME = "_display_name"; 52 public static final String SIZE = "_size"; 53 } 54 55 public static Uri getAttachmentUri(EmailContent.Account account, long id) { 56 return CONTENT_URI.buildUpon() 57 .appendPath(account.getUuid() + ".db") 58 .appendPath(Long.toString(id)) 59 .appendPath(FORMAT_RAW) 60 .build(); 61 } 62 63 public static Uri getAttachmentThumbnailUri(EmailContent.Account account, long id, 64 int width, int height) { 65 return CONTENT_URI.buildUpon() 66 .appendPath(account.getUuid() + ".db") 67 .appendPath(Long.toString(id)) 68 .appendPath(FORMAT_THUMBNAIL) 69 .appendPath(Integer.toString(width)) 70 .appendPath(Integer.toString(height)) 71 .build(); 72 } 73 74 public static Uri getAttachmentUri(String db, long id) { 75 return CONTENT_URI.buildUpon() 76 .appendPath(db) 77 .appendPath(Long.toString(id)) 78 .appendPath(FORMAT_RAW) 79 .build(); 80 } 81 82 @Override 83 public boolean onCreate() { 84 /* 85 * We use the cache dir as a temporary directory (since Android doesn't give us one) so 86 * on startup we'll clean up any .tmp files from the last run. 87 */ 88 File[] files = getContext().getCacheDir().listFiles(); 89 for (File file : files) { 90 if (file.getName().endsWith(".tmp")) { 91 file.delete(); 92 } 93 } 94 return true; 95 } 96 97 @Override 98 public String getType(Uri uri) { 99 List<String> segments = uri.getPathSegments(); 100 String dbName = segments.get(0); 101 String id = segments.get(1); 102 String format = segments.get(2); 103 if (FORMAT_THUMBNAIL.equals(format)) { 104 return "image/png"; 105 } 106 else { 107 String path = getContext().getDatabasePath(dbName).getAbsolutePath(); 108 SQLiteDatabase db = null; 109 Cursor cursor = null; 110 try { 111 db = SQLiteDatabase.openDatabase(path, null, 0); 112 cursor = db.query( 113 "attachments", 114 new String[] { "mime_type" }, 115 "id = ?", 116 new String[] { id }, 117 null, 118 null, 119 null); 120 cursor.moveToFirst(); 121 String type = cursor.getString(0); 122 cursor.close(); 123 db.close(); 124 return type; 125 126 } 127 finally { 128 if (cursor != null) { 129 cursor.close(); 130 } 131 if (db != null) { 132 db.close(); 133 } 134 135 } 136 } 137 } 138 139 @Override 140 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 141 List<String> segments = uri.getPathSegments(); 142 String dbName = segments.get(0); 143 String id = segments.get(1); 144 String format = segments.get(2); 145 if (FORMAT_THUMBNAIL.equals(format)) { 146 int width = Integer.parseInt(segments.get(3)); 147 int height = Integer.parseInt(segments.get(4)); 148 String filename = "thmb_" + dbName + "_" + id; 149 File dir = getContext().getCacheDir(); 150 File file = new File(dir, filename); 151 if (!file.exists()) { 152 Uri attachmentUri = getAttachmentUri(dbName, Long.parseLong(id)); 153 Cursor c = query(attachmentUri, 154 new String[] { AttachmentProviderColumns.DATA }, null, null, null); 155 if (c != null) { 156 try { 157 if (c.moveToFirst()) { 158 attachmentUri = Uri.parse(c.getString(0)); 159 } 160 } finally { 161 c.close(); 162 } 163 } 164 String type = getContext().getContentResolver().getType(attachmentUri); 165 try { 166 InputStream in = 167 getContext().getContentResolver().openInputStream(attachmentUri); 168 Bitmap thumbnail = createThumbnail(type, in); 169 thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true); 170 FileOutputStream out = new FileOutputStream(file); 171 thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out); 172 out.close(); 173 in.close(); 174 } 175 catch (IOException ioe) { 176 return null; 177 } 178 } 179 return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 180 } 181 else { 182 return ParcelFileDescriptor.open( 183 new File(getContext().getDatabasePath(dbName + "_att"), id), 184 ParcelFileDescriptor.MODE_READ_ONLY); 185 } 186 } 187 188 @Override 189 public int delete(Uri uri, String arg1, String[] arg2) { 190 return 0; 191 } 192 193 @Override 194 public Uri insert(Uri uri, ContentValues values) { 195 return null; 196 } 197 198 @Override 199 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 200 String sortOrder) { 201 if (projection == null) { 202 projection = 203 new String[] { 204 AttachmentProviderColumns._ID, 205 AttachmentProviderColumns.DATA, 206 }; 207 } 208 209 List<String> segments = uri.getPathSegments(); 210 String dbName = segments.get(0); 211 String id = segments.get(1); 212 String format = segments.get(2); 213 String path = getContext().getDatabasePath(dbName).getAbsolutePath(); 214 String name = null; 215 int size = -1; 216 String contentUri = null; 217 SQLiteDatabase db = null; 218 Cursor cursor = null; 219 try { 220 db = SQLiteDatabase.openDatabase(path, null, 0); 221 cursor = db.query( 222 "attachments", 223 new String[] { "name", "size", "content_uri" }, 224 "id = ?", 225 new String[] { id }, 226 null, 227 null, 228 null); 229 if (!cursor.moveToFirst()) { 230 return null; 231 } 232 name = cursor.getString(0); 233 size = cursor.getInt(1); 234 contentUri = cursor.getString(2); 235 } 236 finally { 237 if (cursor != null) { 238 cursor.close(); 239 } 240 if (db != null) { 241 db.close(); 242 } 243 } 244 245 MatrixCursor ret = new MatrixCursor(projection); 246 Object[] values = new Object[projection.length]; 247 for (int i = 0, count = projection.length; i < count; i++) { 248 String column = projection[i]; 249 if (AttachmentProviderColumns._ID.equals(column)) { 250 values[i] = id; 251 } 252 else if (AttachmentProviderColumns.DATA.equals(column)) { 253 values[i] = contentUri; 254 } 255 else if (AttachmentProviderColumns.DISPLAY_NAME.equals(column)) { 256 values[i] = name; 257 } 258 else if (AttachmentProviderColumns.SIZE.equals(column)) { 259 values[i] = size; 260 } 261 } 262 ret.addRow(values); 263 return ret; 264 } 265 266 @Override 267 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 268 return 0; 269 } 270 271 private Bitmap createThumbnail(String type, InputStream data) { 272 if(MimeUtility.mimeTypeMatches(type, "image/*")) { 273 return createImageThumbnail(data); 274 } 275 return null; 276 } 277 278 private Bitmap createImageThumbnail(InputStream data) { 279 try { 280 Bitmap bitmap = BitmapFactory.decodeStream(data); 281 return bitmap; 282 } 283 catch (OutOfMemoryError oome) { 284 /* 285 * Improperly downloaded images, corrupt bitmaps and the like can commonly 286 * cause OOME due to invalid allocation sizes. We're happy with a null bitmap in 287 * that case. If the system is really out of memory we'll know about it soon 288 * enough. 289 */ 290 return null; 291 } 292 catch (Exception e) { 293 return null; 294 } 295 } 296 /** 297 * Resolve attachment id to content URI. 298 * 299 * @param attachmentUri 300 * @return resolved content URI 301 */ 302 public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) { 303 Cursor c = resolver.query(attachmentUri, 304 new String[] { AttachmentProvider.AttachmentProviderColumns.DATA }, 305 null, null, null); 306 if (c != null) { 307 try { 308 if (c.moveToFirst()) { 309 return Uri.parse(c.getString(0)); 310 } 311 } finally { 312 c.close(); 313 } 314 } 315 return attachmentUri; 316 } 317} 318