AttachmentProvider.java revision 9ef6f645f57d869a600113f555389b5d5e368c21
196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* 296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License. 696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at 796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and 1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under the License. 1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email.provider; 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 199ef6f645f57d869a600113f555389b5d5e368c21Mihai Predaimport com.android.email.Email; 20c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport com.android.email.mail.internet.MimeUtility; 213d25a519abf676f050b546d34401a277aea5de40Andrew Stadlerimport com.android.email.provider.EmailContent.Attachment; 223d25a519abf676f050b546d34401a277aea5de40Andrew Stadlerimport com.android.email.provider.EmailContent.AttachmentColumns; 2371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadlerimport com.android.email.provider.EmailContent.Message; 2471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadlerimport com.android.email.provider.EmailContent.MessageColumns; 2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.content.ContentProvider; 277436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Predaimport android.content.ContentResolver; 283d25a519abf676f050b546d34401a277aea5de40Andrew Stadlerimport android.content.ContentUris; 2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.content.ContentValues; 30a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadlerimport android.content.Context; 3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.database.Cursor; 3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.database.MatrixCursor; 3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.graphics.Bitmap; 3496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.graphics.BitmapFactory; 3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.net.Uri; 3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.os.ParcelFileDescriptor; 3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 38c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.File; 39c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.FileNotFoundException; 40c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.FileOutputStream; 41c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.IOException; 42c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.InputStream; 43c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.util.List; 4496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* 4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * A simple ContentProvider that allows file access to Email's attachments. 473d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * 483d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * The URI scheme is as follows. For raw file access: 493d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * content://com.android.email.attachmentprovider/acct#/attach#/RAW 503d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * 513d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * And for access to thumbnails: 523d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * content://com.android.email.attachmentprovider/acct#/attach#/THUMBNAIL/width#/height# 53a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * 54a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * The on-disk (storage) schema is as follows. 55a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * 56a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Attachments are stored at: <database-path>/account#.db_att/item# 57a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Thumbnails are stored at: <cache-path>/thmb_account#_item# 58a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * 59a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Using the standard application context, account #10 and attachment # 20, this would be: 60a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * /data/data/com.android.email/databases/10.db_att/20 61a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * /data/data/com.android.email/cache/thmb_10_20 6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class AttachmentProvider extends ContentProvider { 64301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler 65301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler public static final String AUTHORITY = "com.android.email.attachmentprovider"; 66301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY); 6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private static final String FORMAT_RAW = "RAW"; 6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private static final String FORMAT_THUMBNAIL = "THUMBNAIL"; 7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static class AttachmentProviderColumns { 7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String _ID = "_id"; 7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String DATA = "_data"; 7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String DISPLAY_NAME = "_display_name"; 7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String SIZE = "_size"; 7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 783d25a519abf676f050b546d34401a277aea5de40Andrew Stadler private String[] PROJECTION_MIME_TYPE = new String[] { AttachmentColumns.MIME_TYPE }; 793d25a519abf676f050b546d34401a277aea5de40Andrew Stadler private String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME, 803d25a519abf676f050b546d34401a277aea5de40Andrew Stadler AttachmentColumns.SIZE, AttachmentColumns.CONTENT_URI }; 813d25a519abf676f050b546d34401a277aea5de40Andrew Stadler 823d25a519abf676f050b546d34401a277aea5de40Andrew Stadler public static Uri getAttachmentUri(long accountId, long id) { 8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CONTENT_URI.buildUpon() 843d25a519abf676f050b546d34401a277aea5de40Andrew Stadler .appendPath(Long.toString(accountId)) 8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Long.toString(id)) 8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(FORMAT_RAW) 8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .build(); 8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 90a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler public static Uri getAttachmentThumbnailUri(long accountId, long id, 91c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadler int width, int height) { 9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CONTENT_URI.buildUpon() 93a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler .appendPath(Long.toString(accountId)) 9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Long.toString(id)) 9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(FORMAT_THUMBNAIL) 9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Integer.toString(width)) 9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Integer.toString(height)) 9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .build(); 9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 101a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler /** 102a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Return the filename for a given attachment. This should be used by any code that is 103a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * going to *write* attachments. 104a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * 105a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * This does not create or write the file, or even the directories. It simply builds 106a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * the filename that should be used. 107a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler */ 108a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler public static File getAttachmentFilename(Context context, long accountId, long attachmentId) { 1093f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler return new File(getAttachmentDirectory(context, accountId), Long.toString(attachmentId)); 1103f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler } 1113f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler 1123f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler /** 1133f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * Return the directory for a given attachment. This should be used by any code that is 1143f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * going to *write* attachments. 1153f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * 1163f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * This does not create or write the directory. It simply builds the pathname that should be 1173f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * used. 1183f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler */ 1193f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler public static File getAttachmentDirectory(Context context, long accountId) { 1203f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler return context.getDatabasePath(accountId + ".db_att"); 121a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler } 122a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler 12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean onCreate() { 12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * We use the cache dir as a temporary directory (since Android doesn't give us one) so 12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * on startup we'll clean up any .tmp files from the last run. 12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project File[] files = getContext().getCacheDir().listFiles(); 13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (File file : files) { 131aeeefedb43c40e887850654ac88270755f5631c1Mihai Preda String filename = file.getName(); 132aeeefedb43c40e887850654ac88270755f5631c1Mihai Preda if (filename.endsWith(".tmp") || filename.startsWith("thmb_")) { 13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project file.delete(); 13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return true; 13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 139301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 140301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Returns the mime type for a given attachment. There are three possible results: 141301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - If thumbnail Uri, always returns "image/png" (even if there's no attachment) 142301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - If the attachment does not exist, returns null 143301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - Returns the mime type of the attachment 144301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public String getType(Uri uri) { 14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project List<String> segments = uri.getPathSegments(); 1483d25a519abf676f050b546d34401a277aea5de40Andrew Stadler String accountId = segments.get(0); 14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String id = segments.get(1); 15096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String format = segments.get(2); 15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (FORMAT_THUMBNAIL.equals(format)) { 15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return "image/png"; 1533d25a519abf676f050b546d34401a277aea5de40Andrew Stadler } else { 1543d25a519abf676f050b546d34401a277aea5de40Andrew Stadler uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); 1553d25a519abf676f050b546d34401a277aea5de40Andrew Stadler Cursor c = getContext().getContentResolver().query(uri, PROJECTION_MIME_TYPE, 1563d25a519abf676f050b546d34401a277aea5de40Andrew Stadler null, null, null); 15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 1583d25a519abf676f050b546d34401a277aea5de40Andrew Stadler if (c.moveToFirst()) { 1593d25a519abf676f050b546d34401a277aea5de40Andrew Stadler return c.getString(0); 16096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1613d25a519abf676f050b546d34401a277aea5de40Andrew Stadler } finally { 1623d25a519abf676f050b546d34401a277aea5de40Andrew Stadler c.close(); 16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1643d25a519abf676f050b546d34401a277aea5de40Andrew Stadler return null; 16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 168301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 169301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Open an attachment file. There are two "modes" - "raw", which returns an actual file, 170301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * and "thumbnail", which attempts to generate a thumbnail image. 171301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * 172301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Thumbnails are cached for easy space recovery and cleanup. 173301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * 174301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * TODO: The thumbnail mode returns null for its failure cases, instead of throwing 175301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * FileNotFoundException, and should be fixed for consistency. 176301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * 177301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * @throws FileNotFoundException 178301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project List<String> segments = uri.getPathSegments(); 1823d25a519abf676f050b546d34401a277aea5de40Andrew Stadler String accountId = segments.get(0); 18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String id = segments.get(1); 18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String format = segments.get(2); 18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (FORMAT_THUMBNAIL.equals(format)) { 18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int width = Integer.parseInt(segments.get(3)); 18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int height = Integer.parseInt(segments.get(4)); 1883d25a519abf676f050b546d34401a277aea5de40Andrew Stadler String filename = "thmb_" + accountId + "_" + id; 18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project File dir = getContext().getCacheDir(); 19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project File file = new File(dir, filename); 19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (!file.exists()) { 1923d25a519abf676f050b546d34401a277aea5de40Andrew Stadler Uri attachmentUri = getAttachmentUri(Long.parseLong(accountId), Long.parseLong(id)); 1935182d80674a1ef90586f52355a21a82e8265c10aMihai Preda Cursor c = query(attachmentUri, 1945182d80674a1ef90586f52355a21a82e8265c10aMihai Preda new String[] { AttachmentProviderColumns.DATA }, null, null, null); 1955182d80674a1ef90586f52355a21a82e8265c10aMihai Preda if (c != null) { 1965182d80674a1ef90586f52355a21a82e8265c10aMihai Preda try { 1975182d80674a1ef90586f52355a21a82e8265c10aMihai Preda if (c.moveToFirst()) { 1985182d80674a1ef90586f52355a21a82e8265c10aMihai Preda attachmentUri = Uri.parse(c.getString(0)); 1993d25a519abf676f050b546d34401a277aea5de40Andrew Stadler } else { 2003d25a519abf676f050b546d34401a277aea5de40Andrew Stadler return null; 2015182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2025182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } finally { 2035182d80674a1ef90586f52355a21a82e8265c10aMihai Preda c.close(); 2045182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2055182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2065182d80674a1ef90586f52355a21a82e8265c10aMihai Preda String type = getContext().getContentResolver().getType(attachmentUri); 20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 2085182d80674a1ef90586f52355a21a82e8265c10aMihai Preda InputStream in = 2095182d80674a1ef90586f52355a21a82e8265c10aMihai Preda getContext().getContentResolver().openInputStream(attachmentUri); 21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Bitmap thumbnail = createThumbnail(type, in); 2115182d80674a1ef90586f52355a21a82e8265c10aMihai Preda thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true); 21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project FileOutputStream out = new FileOutputStream(file); 21396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out); 21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.close(); 21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project in.close(); 21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project catch (IOException ioe) { 21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else { 22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return ParcelFileDescriptor.open( 2253d25a519abf676f050b546d34401a277aea5de40Andrew Stadler new File(getContext().getDatabasePath(accountId + ".db_att"), id), 22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project ParcelFileDescriptor.MODE_READ_ONLY); 22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int delete(Uri uri, String arg1, String[] arg2) { 23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return 0; 23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public Uri insert(Uri uri, ContentValues values) { 23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 240301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 241301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Returns a cursor based on the data in the attachments table, or null if the attachment 242301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * is not recorded in the table. 243301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * 244301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Supports REST Uri only, for a single row - selection, selection args, and sortOrder are 245301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * ignored (non-null values should probably throw an exception....) 246301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String sortOrder) { 25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (projection == null) { 25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project projection = 25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project new String[] { 25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project AttachmentProviderColumns._ID, 25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project AttachmentProviderColumns.DATA, 25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project }; 25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project List<String> segments = uri.getPathSegments(); 2593d25a519abf676f050b546d34401a277aea5de40Andrew Stadler String accountId = segments.get(0); 26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String id = segments.get(1); 26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String format = segments.get(2); 26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String name = null; 26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int size = -1; 2645182d80674a1ef90586f52355a21a82e8265c10aMihai Preda String contentUri = null; 2653d25a519abf676f050b546d34401a277aea5de40Andrew Stadler 2663d25a519abf676f050b546d34401a277aea5de40Andrew Stadler uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); 2673d25a519abf676f050b546d34401a277aea5de40Andrew Stadler Cursor c = getContext().getContentResolver().query(uri, PROJECTION_QUERY, 2683d25a519abf676f050b546d34401a277aea5de40Andrew Stadler null, null, null); 26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 2703d25a519abf676f050b546d34401a277aea5de40Andrew Stadler if (c.moveToFirst()) { 2713d25a519abf676f050b546d34401a277aea5de40Andrew Stadler name = c.getString(0); 2723d25a519abf676f050b546d34401a277aea5de40Andrew Stadler size = c.getInt(1); 2733d25a519abf676f050b546d34401a277aea5de40Andrew Stadler contentUri = c.getString(2); 2743d25a519abf676f050b546d34401a277aea5de40Andrew Stadler } else { 27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2773d25a519abf676f050b546d34401a277aea5de40Andrew Stadler } finally { 2783d25a519abf676f050b546d34401a277aea5de40Andrew Stadler c.close(); 27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project MatrixCursor ret = new MatrixCursor(projection); 28296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Object[] values = new Object[projection.length]; 28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (int i = 0, count = projection.length; i < count; i++) { 28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String column = projection[i]; 28596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (AttachmentProviderColumns._ID.equals(column)) { 28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project values[i] = id; 28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else if (AttachmentProviderColumns.DATA.equals(column)) { 2895182d80674a1ef90586f52355a21a82e8265c10aMihai Preda values[i] = contentUri; 29096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 29196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else if (AttachmentProviderColumns.DISPLAY_NAME.equals(column)) { 29296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project values[i] = name; 29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 29496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project else if (AttachmentProviderColumns.SIZE.equals(column)) { 29596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project values[i] = size; 29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 29796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project ret.addRow(values); 29996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return ret; 30096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return 0; 30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 30796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private Bitmap createThumbnail(String type, InputStream data) { 30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if(MimeUtility.mimeTypeMatches(type, "image/*")) { 30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return createImageThumbnail(data); 31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private Bitmap createImageThumbnail(InputStream data) { 31596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 31696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Bitmap bitmap = BitmapFactory.decodeStream(data); 31796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return bitmap; 31896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project catch (OutOfMemoryError oome) { 32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 32196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Improperly downloaded images, corrupt bitmaps and the like can commonly 32296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * cause OOME due to invalid allocation sizes. We're happy with a null bitmap in 32396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * that case. If the system is really out of memory we'll know about it soon 32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * enough. 32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 32696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 32896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project catch (Exception e) { 32996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 33196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 3327436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda /** 333301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Resolve attachment id to content URI. Returns the resolved content URI (from the attachment 334301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * DB) or, if not found, simply returns the incoming value. 3357436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda * 3367436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda * @param attachmentUri 3377436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda * @return resolved content URI 338301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * 339301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * TODO: Throws an SQLite exception on a missing DB file (e.g. unknown URI) instead of just 340301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * returning the incoming uri, as it should. 3417436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda */ 3427436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) { 3437436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda Cursor c = resolver.query(attachmentUri, 3447436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda new String[] { AttachmentProvider.AttachmentProviderColumns.DATA }, 3457436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda null, null, null); 3467436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda if (c != null) { 3477436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda try { 3487436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda if (c.moveToFirst()) { 3499ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda final String strUri = c.getString(0); 3509ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda if (strUri != null) { 3519ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda return Uri.parse(strUri); 3529ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda } else { 3539ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda Email.log("AttachmentProvider: attachment with null contentUri"); 3549ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda } 3557436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 3567436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } finally { 3577436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda c.close(); 3587436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 3597436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 3607436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda return attachmentUri; 3617436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 3624b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler 3634b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler /** 3644b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * In support of deleting a message, find all attachments and delete associated attachment 3654b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * files. 3664b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * @param context 3674b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * @param accountId the account for the message 3684b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * @param messageId the message 3694b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler */ 3704b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler public static void deleteAllAttachmentFiles(Context context, long accountId, long messageId) { 3714b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId); 3724b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler Cursor c = context.getContentResolver().query(uri, Attachment.ID_PROJECTION, 3734b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler null, null, null); 3744b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler try { 3754b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler while (c.moveToNext()) { 3764b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler long attachmentId = c.getLong(Attachment.ID_PROJECTION_COLUMN); 3774b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler File attachmentFile = getAttachmentFilename(context, accountId, attachmentId); 3784b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler // Note, delete() throws no exceptions for basic FS errors (e.g. file not found) 3794b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler // it just returns false, which we ignore, and proceed to the next file. 3804b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler // This entire loop is best-effort only. 3814b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler attachmentFile.delete(); 3824b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } 3834b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } finally { 3844b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler c.close(); 3854b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } 3864b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } 38771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler 38871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler /** 38971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * In support of deleting a mailbox, find all messages and delete their attachments. 39071754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * 39171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * @param context 39271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * @param accountId the account for the mailbox 39371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * @param mailboxId the mailbox for the messages 39471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler */ 39571754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler public static void deleteAllMailboxAttachmentFiles(Context context, long accountId, 39671754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler long mailboxId) { 39771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler Cursor c = context.getContentResolver().query(Message.CONTENT_URI, 39871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler Message.ID_COLUMN_PROJECTION, MessageColumns.MAILBOX_KEY + "=?", 39971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler new String[] { Long.toString(mailboxId) }, null); 40071754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler try { 40171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler while (c.moveToNext()) { 40271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler long messageId = c.getLong(Message.ID_PROJECTION_COLUMN); 40371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler deleteAllAttachmentFiles(context, accountId, messageId); 40471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } 40571754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } finally { 40671754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler c.close(); 40771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } 40871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } 40996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 410