AttachmentProvider.java revision 625451ed25747d2637bd45274b0ec1f7d1bbb93c
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; 361ec89e6290df7853990517ebe869bdb212d7e337Marc Blankimport android.os.Binder; 3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.os.ParcelFileDescriptor; 3880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadlerimport android.text.TextUtils; 39625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadlerimport android.util.Log; 4080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadlerimport android.webkit.MimeTypeMap; 4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 42c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.File; 43c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.FileNotFoundException; 44c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.FileOutputStream; 45c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.IOException; 46c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.InputStream; 47c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.util.List; 4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* 5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * A simple ContentProvider that allows file access to Email's attachments. 511d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 523d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * The URI scheme is as follows. For raw file access: 533d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * content://com.android.email.attachmentprovider/acct#/attach#/RAW 541d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 553d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * And for access to thumbnails: 563d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * content://com.android.email.attachmentprovider/acct#/attach#/THUMBNAIL/width#/height# 57a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * 58a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * The on-disk (storage) schema is as follows. 591d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 60a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Attachments are stored at: <database-path>/account#.db_att/item# 61a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Thumbnails are stored at: <cache-path>/thmb_account#_item# 621d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 63a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Using the standard application context, account #10 and attachment # 20, this would be: 64a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * /data/data/com.android.email/databases/10.db_att/20 65a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * /data/data/com.android.email/cache/thmb_10_20 6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class AttachmentProvider extends ContentProvider { 68301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler 69301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler public static final String AUTHORITY = "com.android.email.attachmentprovider"; 70301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY); 7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private static final String FORMAT_RAW = "RAW"; 7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private static final String FORMAT_THUMBNAIL = "THUMBNAIL"; 7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static class AttachmentProviderColumns { 7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String _ID = "_id"; 7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String DATA = "_data"; 7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String DISPLAY_NAME = "_display_name"; 7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public static final String SIZE = "_size"; 8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 8280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final String[] MIME_TYPE_PROJECTION = new String[] { 8380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler AttachmentColumns.MIME_TYPE, AttachmentColumns.FILENAME }; 8480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final int MIME_TYPE_COLUMN_MIME_TYPE = 0; 8580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final int MIME_TYPE_COLUMN_FILENAME = 1; 8680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler 8780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME, 883d25a519abf676f050b546d34401a277aea5de40Andrew Stadler AttachmentColumns.SIZE, AttachmentColumns.CONTENT_URI }; 893d25a519abf676f050b546d34401a277aea5de40Andrew Stadler 903d25a519abf676f050b546d34401a277aea5de40Andrew Stadler public static Uri getAttachmentUri(long accountId, long id) { 9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CONTENT_URI.buildUpon() 923d25a519abf676f050b546d34401a277aea5de40Andrew Stadler .appendPath(Long.toString(accountId)) 9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Long.toString(id)) 9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(FORMAT_RAW) 9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .build(); 9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 98a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler public static Uri getAttachmentThumbnailUri(long accountId, long id, 99c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadler int width, int height) { 10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CONTENT_URI.buildUpon() 101a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler .appendPath(Long.toString(accountId)) 10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Long.toString(id)) 10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(FORMAT_THUMBNAIL) 10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Integer.toString(width)) 10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .appendPath(Integer.toString(height)) 10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project .build(); 10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 109a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler /** 110a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Return the filename for a given attachment. This should be used by any code that is 111a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * going to *write* attachments. 112a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * 113a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * This does not create or write the file, or even the directories. It simply builds 114a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * the filename that should be used. 115a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler */ 116a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler public static File getAttachmentFilename(Context context, long accountId, long attachmentId) { 1173f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler return new File(getAttachmentDirectory(context, accountId), Long.toString(attachmentId)); 1183f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler } 1193f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler 1203f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler /** 1213f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * Return the directory for a given attachment. This should be used by any code that is 1223f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * going to *write* attachments. 1233f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * 1243f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * This does not create or write the directory. It simply builds the pathname that should be 1253f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler * used. 1263f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler */ 1273f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler public static File getAttachmentDirectory(Context context, long accountId) { 1283f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler return context.getDatabasePath(accountId + ".db_att"); 129a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler } 130a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler 13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean onCreate() { 13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * We use the cache dir as a temporary directory (since Android doesn't give us one) so 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * on startup we'll clean up any .tmp files from the last run. 13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project File[] files = getContext().getCacheDir().listFiles(); 13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project for (File file : files) { 139aeeefedb43c40e887850654ac88270755f5631c1Mihai Preda String filename = file.getName(); 140aeeefedb43c40e887850654ac88270755f5631c1Mihai Preda if (filename.endsWith(".tmp") || filename.startsWith("thmb_")) { 14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project file.delete(); 14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return true; 14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 147301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 148301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Returns the mime type for a given attachment. There are three possible results: 149301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - If thumbnail Uri, always returns "image/png" (even if there's no attachment) 150301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - If the attachment does not exist, returns null 151301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - Returns the mime type of the attachment 152301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 15496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public String getType(Uri uri) { 1551ec89e6290df7853990517ebe869bdb212d7e337Marc Blank long callingId = Binder.clearCallingIdentity(); 1561ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1571ec89e6290df7853990517ebe869bdb212d7e337Marc Blank List<String> segments = uri.getPathSegments(); 1581ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String id = segments.get(1); 1591ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String format = segments.get(2); 1601ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (FORMAT_THUMBNAIL.equals(format)) { 1611ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return "image/png"; 1621ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } else { 1631ec89e6290df7853990517ebe869bdb212d7e337Marc Blank uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); 1641ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Cursor c = getContext().getContentResolver().query(uri, MIME_TYPE_PROJECTION, 1651ec89e6290df7853990517ebe869bdb212d7e337Marc Blank null, null, null); 1661ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1671ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c.moveToFirst()) { 1681ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String mimeType = c.getString(MIME_TYPE_COLUMN_MIME_TYPE); 1691ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String fileName = c.getString(MIME_TYPE_COLUMN_FILENAME); 1701ec89e6290df7853990517ebe869bdb212d7e337Marc Blank mimeType = inferMimeType(fileName, mimeType); 1711ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return mimeType; 1721ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 1731ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 1741ec89e6290df7853990517ebe869bdb212d7e337Marc Blank c.close(); 17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1761ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 17796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1781ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 1791ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Binder.restoreCallingIdentity(callingId); 18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 183301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 18480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * Helper to convert unknown or unmapped attachments to something useful based on filename 18580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * extensions. Imperfect, but helps. 18680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * 1871d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * If the file extension is ".eml", return "message/rfc822", which is necessary for the email 1881d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * app to open it. 18980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * If the given mime type is non-empty and anything other than "application/octet-stream", 19080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * just return it. (This is the most common case.) 19180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * If the filename has a recognizable extension and it converts to a mime type, return that. 19280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * If the filename has an unrecognized extension, return "application/extension" 19380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * Otherwise return "application/octet-stream". 19480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * 19580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * @param fileName The given filename 19680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * @param mimeType The given mime type 19780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler * @return A likely mime type for the attachment 19880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler */ 19980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler public static String inferMimeType(String fileName, String mimeType) { 2001d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki if (fileName != null && fileName.toLowerCase().endsWith(".eml")) { 2011d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki return "message/rfc822"; 2021d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki } 20380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler // If the given mime type appears to be non-empty and non-generic - return it 20480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler if (!TextUtils.isEmpty(mimeType) && 20580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler !"application/octet-stream".equalsIgnoreCase(mimeType)) { 20680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler return mimeType; 20780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler } 20880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler 20980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler // Try to find an extension in the filename 21080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler if (!TextUtils.isEmpty(fileName)) { 21180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler int lastDot = fileName.lastIndexOf('.'); 21280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler String extension = null; 21380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 21480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler extension = fileName.substring(lastDot + 1).toLowerCase(); 21580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler } 21680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler if (!TextUtils.isEmpty(extension)) { 21780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler // Extension found. Look up mime type, or synthesize if none found. 21880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 21980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler if (mimeType == null) { 22080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler mimeType = "application/" + extension; 22180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler } 22280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler return mimeType; 22380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler } 22480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler } 22580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler 22680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler // Fallback case - no good guess could be made. 22780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler return "application/octet-stream"; 22880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler } 22980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler 23080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler /** 231301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Open an attachment file. There are two "modes" - "raw", which returns an actual file, 232301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * and "thumbnail", which attempts to generate a thumbnail image. 2331d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 234301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Thumbnails are cached for easy space recovery and cleanup. 2351d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 236301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * TODO: The thumbnail mode returns null for its failure cases, instead of throwing 237301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * FileNotFoundException, and should be fixed for consistency. 2381d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 239301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * @throws FileNotFoundException 240301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 2431ec89e6290df7853990517ebe869bdb212d7e337Marc Blank long callingId = Binder.clearCallingIdentity(); 2441ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 2451ec89e6290df7853990517ebe869bdb212d7e337Marc Blank List<String> segments = uri.getPathSegments(); 2461ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String accountId = segments.get(0); 2471ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String id = segments.get(1); 2481ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String format = segments.get(2); 2491ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (FORMAT_THUMBNAIL.equals(format)) { 2501ec89e6290df7853990517ebe869bdb212d7e337Marc Blank int width = Integer.parseInt(segments.get(3)); 2511ec89e6290df7853990517ebe869bdb212d7e337Marc Blank int height = Integer.parseInt(segments.get(4)); 2521ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String filename = "thmb_" + accountId + "_" + id; 2531ec89e6290df7853990517ebe869bdb212d7e337Marc Blank File dir = getContext().getCacheDir(); 2541ec89e6290df7853990517ebe869bdb212d7e337Marc Blank File file = new File(dir, filename); 2551ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (!file.exists()) { 2561ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Uri attachmentUri = 2571ec89e6290df7853990517ebe869bdb212d7e337Marc Blank getAttachmentUri(Long.parseLong(accountId), Long.parseLong(id)); 2581ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Cursor c = query(attachmentUri, 2591ec89e6290df7853990517ebe869bdb212d7e337Marc Blank new String[] { AttachmentProviderColumns.DATA }, null, null, null); 2601ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c != null) { 2611ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 2621ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c.moveToFirst()) { 2631ec89e6290df7853990517ebe869bdb212d7e337Marc Blank attachmentUri = Uri.parse(c.getString(0)); 2641ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } else { 2651ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 2661ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2671ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 2681ec89e6290df7853990517ebe869bdb212d7e337Marc Blank c.close(); 2695182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2701ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2711ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String type = getContext().getContentResolver().getType(attachmentUri); 2721ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 2731ec89e6290df7853990517ebe869bdb212d7e337Marc Blank InputStream in = 2741ec89e6290df7853990517ebe869bdb212d7e337Marc Blank getContext().getContentResolver().openInputStream(attachmentUri); 2751ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Bitmap thumbnail = createThumbnail(type, in); 276625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler if (thumbnail == null) { 277625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler return null; 278625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } 2791ec89e6290df7853990517ebe869bdb212d7e337Marc Blank thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true); 2801ec89e6290df7853990517ebe869bdb212d7e337Marc Blank FileOutputStream out = new FileOutputStream(file); 2811ec89e6290df7853990517ebe869bdb212d7e337Marc Blank thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out); 2821ec89e6290df7853990517ebe869bdb212d7e337Marc Blank out.close(); 2831ec89e6290df7853990517ebe869bdb212d7e337Marc Blank in.close(); 284625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (IOException ioe) { 285625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler Log.d(Email.LOG_TAG, "openFile/thumbnail failed with " + ioe.getMessage()); 286625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler return null; 287625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (OutOfMemoryError oome) { 288625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler Log.d(Email.LOG_TAG, "openFile/thumbnail failed with " + oome.getMessage()); 2891ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 2905182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2915182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2921ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2941ec89e6290df7853990517ebe869bdb212d7e337Marc Blank else { 2951ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return ParcelFileDescriptor.open( 2961ec89e6290df7853990517ebe869bdb212d7e337Marc Blank new File(getContext().getDatabasePath(accountId + ".db_att"), id), 2971ec89e6290df7853990517ebe869bdb212d7e337Marc Blank ParcelFileDescriptor.MODE_READ_ONLY); 2981ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2991ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 3001ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Binder.restoreCallingIdentity(callingId); 30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int delete(Uri uri, String arg1, String[] arg2) { 30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return 0; 30796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public Uri insert(Uri uri, ContentValues values) { 31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 314301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 315301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Returns a cursor based on the data in the attachments table, or null if the attachment 316301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * is not recorded in the table. 3171d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 318301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Supports REST Uri only, for a single row - selection, selection args, and sortOrder are 319301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * ignored (non-null values should probably throw an exception....) 320301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 32196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 32296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 32396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String sortOrder) { 3241ec89e6290df7853990517ebe869bdb212d7e337Marc Blank long callingId = Binder.clearCallingIdentity(); 32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 3261ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (projection == null) { 3271ec89e6290df7853990517ebe869bdb212d7e337Marc Blank projection = 3281ec89e6290df7853990517ebe869bdb212d7e337Marc Blank new String[] { 3291ec89e6290df7853990517ebe869bdb212d7e337Marc Blank AttachmentProviderColumns._ID, 3301ec89e6290df7853990517ebe869bdb212d7e337Marc Blank AttachmentProviderColumns.DATA, 3311ec89e6290df7853990517ebe869bdb212d7e337Marc Blank }; 33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 33396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 3341ec89e6290df7853990517ebe869bdb212d7e337Marc Blank List<String> segments = uri.getPathSegments(); 3351ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String accountId = segments.get(0); 3361ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String id = segments.get(1); 3371ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String format = segments.get(2); 3381ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String name = null; 3391ec89e6290df7853990517ebe869bdb212d7e337Marc Blank int size = -1; 3401ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String contentUri = null; 3411ec89e6290df7853990517ebe869bdb212d7e337Marc Blank 3421ec89e6290df7853990517ebe869bdb212d7e337Marc Blank uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); 3431ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Cursor c = getContext().getContentResolver().query(uri, PROJECTION_QUERY, 3441ec89e6290df7853990517ebe869bdb212d7e337Marc Blank null, null, null); 3451ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 3461ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c.moveToFirst()) { 3471ec89e6290df7853990517ebe869bdb212d7e337Marc Blank name = c.getString(0); 3481ec89e6290df7853990517ebe869bdb212d7e337Marc Blank size = c.getInt(1); 3491ec89e6290df7853990517ebe869bdb212d7e337Marc Blank contentUri = c.getString(2); 3501ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } else { 3511ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 3521ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 3531ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 3541ec89e6290df7853990517ebe869bdb212d7e337Marc Blank c.close(); 35596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 3561ec89e6290df7853990517ebe869bdb212d7e337Marc Blank 3571ec89e6290df7853990517ebe869bdb212d7e337Marc Blank MatrixCursor ret = new MatrixCursor(projection); 3581ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Object[] values = new Object[projection.length]; 3591ec89e6290df7853990517ebe869bdb212d7e337Marc Blank for (int i = 0, count = projection.length; i < count; i++) { 3601ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String column = projection[i]; 3611ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (AttachmentProviderColumns._ID.equals(column)) { 3621ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = id; 3631ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 3641ec89e6290df7853990517ebe869bdb212d7e337Marc Blank else if (AttachmentProviderColumns.DATA.equals(column)) { 3651ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = contentUri; 3661ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 3671ec89e6290df7853990517ebe869bdb212d7e337Marc Blank else if (AttachmentProviderColumns.DISPLAY_NAME.equals(column)) { 3681ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = name; 3691ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 3701ec89e6290df7853990517ebe869bdb212d7e337Marc Blank else if (AttachmentProviderColumns.SIZE.equals(column)) { 3711ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = size; 3721ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 37396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 3741ec89e6290df7853990517ebe869bdb212d7e337Marc Blank ret.addRow(values); 3751ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return ret; 3761ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 3771ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Binder.restoreCallingIdentity(callingId); 37896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 37996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 38096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 38196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 38296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 38396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return 0; 38496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 38596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 38696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private Bitmap createThumbnail(String type, InputStream data) { 38796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if(MimeUtility.mimeTypeMatches(type, "image/*")) { 38896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return createImageThumbnail(data); 38996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 39096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 39196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 39296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 39396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private Bitmap createImageThumbnail(InputStream data) { 39496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 39596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Bitmap bitmap = BitmapFactory.decodeStream(data); 39696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return bitmap; 397625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (OutOfMemoryError oome) { 398625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler Log.d(Email.LOG_TAG, "createImageThumbnail failed with " + oome.getMessage()); 39996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 400625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (Exception e) { 401625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler Log.d(Email.LOG_TAG, "createImageThumbnail failed with " + e.getMessage()); 40296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 40396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 40496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 405625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler 4067436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda /** 407301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Resolve attachment id to content URI. Returns the resolved content URI (from the attachment 408301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * DB) or, if not found, simply returns the incoming value. 4091d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 4107436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda * @param attachmentUri 4117436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda * @return resolved content URI 412301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * 413301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * TODO: Throws an SQLite exception on a missing DB file (e.g. unknown URI) instead of just 414301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * returning the incoming uri, as it should. 4157436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda */ 4167436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) { 4177436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda Cursor c = resolver.query(attachmentUri, 4187436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda new String[] { AttachmentProvider.AttachmentProviderColumns.DATA }, 4197436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda null, null, null); 4207436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda if (c != null) { 4217436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda try { 4227436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda if (c.moveToFirst()) { 4239ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda final String strUri = c.getString(0); 4249ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda if (strUri != null) { 4259ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda return Uri.parse(strUri); 4269ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda } else { 4279ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda Email.log("AttachmentProvider: attachment with null contentUri"); 4289ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda } 4297436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 4307436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } finally { 4317436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda c.close(); 4327436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 4337436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 4347436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda return attachmentUri; 4357436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda } 4364b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler 4374b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler /** 4384b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * In support of deleting a message, find all attachments and delete associated attachment 4394b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * files. 4404b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * @param context 4414b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * @param accountId the account for the message 4424b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler * @param messageId the message 4434b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler */ 4444b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler public static void deleteAllAttachmentFiles(Context context, long accountId, long messageId) { 4454b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId); 4464b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler Cursor c = context.getContentResolver().query(uri, Attachment.ID_PROJECTION, 4474b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler null, null, null); 4484b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler try { 4494b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler while (c.moveToNext()) { 4504b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler long attachmentId = c.getLong(Attachment.ID_PROJECTION_COLUMN); 4514b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler File attachmentFile = getAttachmentFilename(context, accountId, attachmentId); 4524b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler // Note, delete() throws no exceptions for basic FS errors (e.g. file not found) 4534b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler // it just returns false, which we ignore, and proceed to the next file. 4544b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler // This entire loop is best-effort only. 4554b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler attachmentFile.delete(); 4564b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } 4574b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } finally { 4584b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler c.close(); 4594b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } 4604b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler } 46171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler 46271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler /** 46371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * In support of deleting a mailbox, find all messages and delete their attachments. 46471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * 46571754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * @param context 46671754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * @param accountId the account for the mailbox 46771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler * @param mailboxId the mailbox for the messages 46871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler */ 46971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler public static void deleteAllMailboxAttachmentFiles(Context context, long accountId, 47071754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler long mailboxId) { 47171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler Cursor c = context.getContentResolver().query(Message.CONTENT_URI, 47271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler Message.ID_COLUMN_PROJECTION, MessageColumns.MAILBOX_KEY + "=?", 47371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler new String[] { Long.toString(mailboxId) }, null); 47471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler try { 47571754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler while (c.moveToNext()) { 47671754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler long messageId = c.getLong(Message.ID_PROJECTION_COLUMN); 47771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler deleteAllAttachmentFiles(context, accountId, messageId); 47871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } 47971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } finally { 48071754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler c.close(); 48171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } 48271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler } 4836c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki 4846c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki /** 4856c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki * Need this to suppress warning in unit tests. 4866c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki */ 4876c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki @Override 4886c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki public void shutdown() { 4896c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki // Don't call super.shutdown(), which emits a warning... 4906c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki } 49196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 492