AttachmentProvider.java revision 908fac0b247deff27fee44297f892afdfbb086a5
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 1996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.content.ContentProvider; 203d25a519abf676f050b546d34401a277aea5de40Andrew Stadlerimport android.content.ContentUris; 2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.content.ContentValues; 22dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blankimport android.content.Context; 23dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blankimport android.content.pm.PackageManager; 2496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.database.Cursor; 2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.database.MatrixCursor; 2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.graphics.Bitmap; 2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.graphics.BitmapFactory; 2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.net.Uri; 291ec89e6290df7853990517ebe869bdb212d7e337Marc Blankimport android.os.Binder; 3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.os.ParcelFileDescriptor; 3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 327fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.Logging; 337fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.internet.MimeUtility; 347fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.provider.EmailContent; 357fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.provider.EmailContent.Attachment; 367fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.provider.EmailContent.AttachmentColumns; 377fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.utility.AttachmentUtilities; 387fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.emailcommon.utility.AttachmentUtilities.Columns; 39560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils; 407fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.mail.utils.MatrixCursorWithCachedColumns; 417fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy 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: 53e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank * content://com.android.mail.attachmentprovider/acct#/attach#/RAW 541d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 553d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * And for access to thumbnails: 56e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank * content://com.android.mail.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 6980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final String[] MIME_TYPE_PROJECTION = new String[] { 7080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler AttachmentColumns.MIME_TYPE, AttachmentColumns.FILENAME }; 7180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final int MIME_TYPE_COLUMN_MIME_TYPE = 0; 7280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final int MIME_TYPE_COLUMN_FILENAME = 1; 7380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler 7480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler private static final String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME, 753d25a519abf676f050b546d34401a277aea5de40Andrew Stadler AttachmentColumns.SIZE, AttachmentColumns.CONTENT_URI }; 763d25a519abf676f050b546d34401a277aea5de40Andrew Stadler 7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean onCreate() { 7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /* 8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * We use the cache dir as a temporary directory (since Android doesn't give us one) so 8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * on startup we'll clean up any .tmp files from the last run. 8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 83908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook 84908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook final File[] files = getContext().getCacheDir().listFiles(); 85908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook if (files != null) { 86908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook for (File file : files) { 87908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook final String filename = file.getName(); 88908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook if (filename.endsWith(".tmp") || filename.startsWith("thmb_")) { 89908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook file.delete(); 90908fac0b247deff27fee44297f892afdfbb086a5Paul Westbrook } 9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return true; 9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 96301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 97301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Returns the mime type for a given attachment. There are three possible results: 98301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - If thumbnail Uri, always returns "image/png" (even if there's no attachment) 99301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - If the attachment does not exist, returns null 100301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * - Returns the mime type of the attachment 101301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public String getType(Uri uri) { 1041ec89e6290df7853990517ebe869bdb212d7e337Marc Blank long callingId = Binder.clearCallingIdentity(); 1051ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1061ec89e6290df7853990517ebe869bdb212d7e337Marc Blank List<String> segments = uri.getPathSegments(); 1071ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String id = segments.get(1); 1081ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String format = segments.get(2); 1098a574694606f0e5d781334d0d426fc379c51f3edMarc Blank if (AttachmentUtilities.FORMAT_THUMBNAIL.equals(format)) { 1101ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return "image/png"; 1111ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } else { 1121ec89e6290df7853990517ebe869bdb212d7e337Marc Blank uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); 1138a574694606f0e5d781334d0d426fc379c51f3edMarc Blank Cursor c = getContext().getContentResolver().query(uri, MIME_TYPE_PROJECTION, null, 1148a574694606f0e5d781334d0d426fc379c51f3edMarc Blank null, null); 1151ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1161ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c.moveToFirst()) { 1171ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String mimeType = c.getString(MIME_TYPE_COLUMN_MIME_TYPE); 1181ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String fileName = c.getString(MIME_TYPE_COLUMN_FILENAME); 1198a574694606f0e5d781334d0d426fc379c51f3edMarc Blank mimeType = AttachmentUtilities.inferMimeType(fileName, mimeType); 1201ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return mimeType; 1211ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 1221ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 1231ec89e6290df7853990517ebe869bdb212d7e337Marc Blank c.close(); 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1251ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 1271ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 1281ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Binder.restoreCallingIdentity(callingId); 12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 132301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 133dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank * Open an attachment file. There are two "formats" - "raw", which returns an actual file, 134301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * and "thumbnail", which attempts to generate a thumbnail image. 1351d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 136301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Thumbnails are cached for easy space recovery and cleanup. 1371d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 138dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank * TODO: The thumbnail format returns null for its failure cases, instead of throwing 139301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * FileNotFoundException, and should be fixed for consistency. 1401d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 141301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * @throws FileNotFoundException 142301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 145dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank // If this is a write, the caller must have the EmailProvider permission, which is 146dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank // based on signature only 147dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank if (mode.equals("w")) { 148dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank Context context = getContext(); 149f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (context.checkCallingOrSelfPermission(EmailContent.PROVIDER_PERMISSION) 150dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank != PackageManager.PERMISSION_GRANTED) { 151dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank throw new FileNotFoundException(); 152dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank } 153dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank List<String> segments = uri.getPathSegments(); 154dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank String accountId = segments.get(0); 155dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank String id = segments.get(1); 156dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank File saveIn = 157dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank AttachmentUtilities.getAttachmentDirectory(context, Long.parseLong(accountId)); 158dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank if (!saveIn.exists()) { 159dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank saveIn.mkdirs(); 160dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank } 161dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank File newFile = new File(saveIn, id); 162dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank return ParcelFileDescriptor.open( 163dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank newFile, ParcelFileDescriptor.MODE_READ_WRITE | 164dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE); 165dc78a769fce18d259eccc602c4623fa74cdf5319Marc Blank } 1661ec89e6290df7853990517ebe869bdb212d7e337Marc Blank long callingId = Binder.clearCallingIdentity(); 1671ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1681ec89e6290df7853990517ebe869bdb212d7e337Marc Blank List<String> segments = uri.getPathSegments(); 1691ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String accountId = segments.get(0); 1701ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String id = segments.get(1); 1711ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String format = segments.get(2); 1728a574694606f0e5d781334d0d426fc379c51f3edMarc Blank if (AttachmentUtilities.FORMAT_THUMBNAIL.equals(format)) { 1731ec89e6290df7853990517ebe869bdb212d7e337Marc Blank int width = Integer.parseInt(segments.get(3)); 1741ec89e6290df7853990517ebe869bdb212d7e337Marc Blank int height = Integer.parseInt(segments.get(4)); 1751ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String filename = "thmb_" + accountId + "_" + id; 1761ec89e6290df7853990517ebe869bdb212d7e337Marc Blank File dir = getContext().getCacheDir(); 1771ec89e6290df7853990517ebe869bdb212d7e337Marc Blank File file = new File(dir, filename); 1781ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (!file.exists()) { 1798a574694606f0e5d781334d0d426fc379c51f3edMarc Blank Uri attachmentUri = AttachmentUtilities. 1801ec89e6290df7853990517ebe869bdb212d7e337Marc Blank getAttachmentUri(Long.parseLong(accountId), Long.parseLong(id)); 1811ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Cursor c = query(attachmentUri, 1828a574694606f0e5d781334d0d426fc379c51f3edMarc Blank new String[] { Columns.DATA }, null, null, null); 1831ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c != null) { 1841ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1851ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c.moveToFirst()) { 1861ec89e6290df7853990517ebe869bdb212d7e337Marc Blank attachmentUri = Uri.parse(c.getString(0)); 1871ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } else { 1881ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 1891ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 1901ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 1911ec89e6290df7853990517ebe869bdb212d7e337Marc Blank c.close(); 1925182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 1931ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 1941ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String type = getContext().getContentResolver().getType(attachmentUri); 1951ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 1961ec89e6290df7853990517ebe869bdb212d7e337Marc Blank InputStream in = 1971ec89e6290df7853990517ebe869bdb212d7e337Marc Blank getContext().getContentResolver().openInputStream(attachmentUri); 1981ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Bitmap thumbnail = createThumbnail(type, in); 199625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler if (thumbnail == null) { 200625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler return null; 201625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } 2021ec89e6290df7853990517ebe869bdb212d7e337Marc Blank thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true); 2031ec89e6290df7853990517ebe869bdb212d7e337Marc Blank FileOutputStream out = new FileOutputStream(file); 2041ec89e6290df7853990517ebe869bdb212d7e337Marc Blank thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out); 2051ec89e6290df7853990517ebe869bdb212d7e337Marc Blank out.close(); 2061ec89e6290df7853990517ebe869bdb212d7e337Marc Blank in.close(); 207625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (IOException ioe) { 208560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "openFile/thumbnail failed with " + 20931d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank ioe.getMessage()); 210625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler return null; 211625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (OutOfMemoryError oome) { 212560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "openFile/thumbnail failed with " + 21331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank oome.getMessage()); 2141ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 2155182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2165182d80674a1ef90586f52355a21a82e8265c10aMihai Preda } 2171ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2191ec89e6290df7853990517ebe869bdb212d7e337Marc Blank else { 2201ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return ParcelFileDescriptor.open( 2211ec89e6290df7853990517ebe869bdb212d7e337Marc Blank new File(getContext().getDatabasePath(accountId + ".db_att"), id), 2221ec89e6290df7853990517ebe869bdb212d7e337Marc Blank ParcelFileDescriptor.MODE_READ_ONLY); 2231ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2241ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 2251ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Binder.restoreCallingIdentity(callingId); 22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int delete(Uri uri, String arg1, String[] arg2) { 23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return 0; 23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public Uri insert(Uri uri, ContentValues values) { 23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 239301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler /** 240301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Returns a cursor based on the data in the attachments table, or null if the attachment 241301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * is not recorded in the table. 2421d0be30871745ef0e623d8bb5e2e433567541623Makoto Onuki * 243301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * Supports REST Uri only, for a single row - selection, selection args, and sortOrder are 244301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler * ignored (non-null values should probably throw an exception....) 245301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler */ 24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String sortOrder) { 2491ec89e6290df7853990517ebe869bdb212d7e337Marc Blank long callingId = Binder.clearCallingIdentity(); 25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 2511ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (projection == null) { 2521ec89e6290df7853990517ebe869bdb212d7e337Marc Blank projection = 2531ec89e6290df7853990517ebe869bdb212d7e337Marc Blank new String[] { 2548a574694606f0e5d781334d0d426fc379c51f3edMarc Blank Columns._ID, 2558a574694606f0e5d781334d0d426fc379c51f3edMarc Blank Columns.DATA, 2561ec89e6290df7853990517ebe869bdb212d7e337Marc Blank }; 25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 2591ec89e6290df7853990517ebe869bdb212d7e337Marc Blank List<String> segments = uri.getPathSegments(); 2601ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String accountId = segments.get(0); 2611ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String id = segments.get(1); 2621ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String format = segments.get(2); 2631ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String name = null; 2641ec89e6290df7853990517ebe869bdb212d7e337Marc Blank int size = -1; 2651ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String contentUri = null; 2661ec89e6290df7853990517ebe869bdb212d7e337Marc Blank 2671ec89e6290df7853990517ebe869bdb212d7e337Marc Blank uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); 2681ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Cursor c = getContext().getContentResolver().query(uri, PROJECTION_QUERY, 2691ec89e6290df7853990517ebe869bdb212d7e337Marc Blank null, null, null); 2701ec89e6290df7853990517ebe869bdb212d7e337Marc Blank try { 2711ec89e6290df7853990517ebe869bdb212d7e337Marc Blank if (c.moveToFirst()) { 2721ec89e6290df7853990517ebe869bdb212d7e337Marc Blank name = c.getString(0); 2731ec89e6290df7853990517ebe869bdb212d7e337Marc Blank size = c.getInt(1); 2741ec89e6290df7853990517ebe869bdb212d7e337Marc Blank contentUri = c.getString(2); 2751ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } else { 2761ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return null; 2771ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2781ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 2791ec89e6290df7853990517ebe869bdb212d7e337Marc Blank c.close(); 28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2811ec89e6290df7853990517ebe869bdb212d7e337Marc Blank 2827fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy MatrixCursor ret = new MatrixCursorWithCachedColumns(projection); 2831ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Object[] values = new Object[projection.length]; 2841ec89e6290df7853990517ebe869bdb212d7e337Marc Blank for (int i = 0, count = projection.length; i < count; i++) { 2851ec89e6290df7853990517ebe869bdb212d7e337Marc Blank String column = projection[i]; 2868a574694606f0e5d781334d0d426fc379c51f3edMarc Blank if (Columns._ID.equals(column)) { 2871ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = id; 2881ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2898a574694606f0e5d781334d0d426fc379c51f3edMarc Blank else if (Columns.DATA.equals(column)) { 2901ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = contentUri; 2911ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2928a574694606f0e5d781334d0d426fc379c51f3edMarc Blank else if (Columns.DISPLAY_NAME.equals(column)) { 2931ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = name; 2941ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 2958a574694606f0e5d781334d0d426fc379c51f3edMarc Blank else if (Columns.SIZE.equals(column)) { 2961ec89e6290df7853990517ebe869bdb212d7e337Marc Blank values[i] = size; 2971ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } 29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2991ec89e6290df7853990517ebe869bdb212d7e337Marc Blank ret.addRow(values); 3001ec89e6290df7853990517ebe869bdb212d7e337Marc Blank return ret; 3011ec89e6290df7853990517ebe869bdb212d7e337Marc Blank } finally { 3021ec89e6290df7853990517ebe869bdb212d7e337Marc Blank Binder.restoreCallingIdentity(callingId); 30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project @Override 30796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return 0; 30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 311560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy private static Bitmap createThumbnail(String type, InputStream data) { 31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if(MimeUtility.mimeTypeMatches(type, "image/*")) { 31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return createImageThumbnail(data); 31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 31696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 318560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy private static Bitmap createImageThumbnail(InputStream data) { 31996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Bitmap bitmap = BitmapFactory.decodeStream(data); 32196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return bitmap; 322625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (OutOfMemoryError oome) { 323560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "createImageThumbnail failed with " + oome.getMessage()); 32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 325625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler } catch (Exception e) { 326560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "createImageThumbnail failed with " + e.getMessage()); 32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return null; 32896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 32996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 330625451ed25747d2637bd45274b0ec1f7d1bbb93cAndrew Stadler 3317436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda /** 3326c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki * Need this to suppress warning in unit tests. 3336c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki */ 3346c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki @Override 3356c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki public void shutdown() { 3366c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki // Don't call super.shutdown(), which emits a warning... 3376c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki } 33896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 339