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;
3780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadlerimport android.text.TextUtils;
3880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadlerimport android.webkit.MimeTypeMap;
3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
40c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.File;
41c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.FileNotFoundException;
42c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.FileOutputStream;
43c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.IOException;
44c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.io.InputStream;
45c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadlerimport java.util.List;
4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
4796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/*
4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * A simple ContentProvider that allows file access to Email's attachments.
493d25a519abf676f050b546d34401a277aea5de40Andrew Stadler *
503d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * The URI scheme is as follows.  For raw file access:
513d25a519abf676f050b546d34401a277aea5de40Andrew Stadler *   content://com.android.email.attachmentprovider/acct#/attach#/RAW
523d25a519abf676f050b546d34401a277aea5de40Andrew Stadler *
533d25a519abf676f050b546d34401a277aea5de40Andrew Stadler * And for access to thumbnails:
543d25a519abf676f050b546d34401a277aea5de40Andrew Stadler *   content://com.android.email.attachmentprovider/acct#/attach#/THUMBNAIL/width#/height#
55a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler *
56a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * The on-disk (storage) schema is as follows.
57a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler *
58a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Attachments are stored at:  <database-path>/account#.db_att/item#
59a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Thumbnails are stored at:   <cache-path>/thmb_account#_item#
60a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler *
61a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler * Using the standard application context, account #10 and attachment # 20, this would be:
62a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler *      /data/data/com.android.email/databases/10.db_att/20
63a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler *      /data/data/com.android.email/cache/thmb_10_20
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class AttachmentProvider extends ContentProvider {
66301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler
67301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler    public static final String AUTHORITY = "com.android.email.attachmentprovider";
68301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler    public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY);
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static final String FORMAT_RAW = "RAW";
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static final String FORMAT_THUMBNAIL = "THUMBNAIL";
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public static class AttachmentProviderColumns {
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final String _ID = "_id";
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final String DATA = "_data";
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final String DISPLAY_NAME = "_display_name";
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final String SIZE = "_size";
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
8080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    private static final String[] MIME_TYPE_PROJECTION = new String[] {
8180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            AttachmentColumns.MIME_TYPE, AttachmentColumns.FILENAME };
8280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    private static final int MIME_TYPE_COLUMN_MIME_TYPE = 0;
8380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    private static final int MIME_TYPE_COLUMN_FILENAME = 1;
8480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler
8580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    private static final String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME,
863d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            AttachmentColumns.SIZE, AttachmentColumns.CONTENT_URI };
873d25a519abf676f050b546d34401a277aea5de40Andrew Stadler
883d25a519abf676f050b546d34401a277aea5de40Andrew Stadler    public static Uri getAttachmentUri(long accountId, long id) {
8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return CONTENT_URI.buildUpon()
903d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                .appendPath(Long.toString(accountId))
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .appendPath(Long.toString(id))
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .appendPath(FORMAT_RAW)
9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .build();
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
96a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler    public static Uri getAttachmentThumbnailUri(long accountId, long id,
97c0033f24a26a08c47aa38d957f42cf63cfa3c345Andrew Stadler            int width, int height) {
9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return CONTENT_URI.buildUpon()
99a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler                .appendPath(Long.toString(accountId))
10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .appendPath(Long.toString(id))
10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .appendPath(FORMAT_THUMBNAIL)
10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .appendPath(Integer.toString(width))
10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .appendPath(Integer.toString(height))
10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                .build();
10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
107a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler    /**
108a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler     * Return the filename for a given attachment.  This should be used by any code that is
109a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler     * going to *write* attachments.
110a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler     *
111a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler     * This does not create or write the file, or even the directories.  It simply builds
112a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler     * the filename that should be used.
113a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler     */
114a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler    public static File getAttachmentFilename(Context context, long accountId, long attachmentId) {
1153f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler        return new File(getAttachmentDirectory(context, accountId), Long.toString(attachmentId));
1163f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler    }
1173f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler
1183f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler    /**
1193f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler     * Return the directory for a given attachment.  This should be used by any code that is
1203f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler     * going to *write* attachments.
1213f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler     *
1223f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler     * This does not create or write the directory.  It simply builds the pathname that should be
1233f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler     * used.
1243f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler     */
1253f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler    public static File getAttachmentDirectory(Context context, long accountId) {
1263f1ac4da947f426775c9546f2e37206f58ce1a6eAndrew Stadler        return context.getDatabasePath(accountId + ".db_att");
127a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler    }
128a98de7e55e91229c35352b496fad3fbb108a9609Andrew Stadler
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean onCreate() {
13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /*
13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * We use the cache dir as a temporary directory (since Android doesn't give us one) so
13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * on startup we'll clean up any .tmp files from the last run.
13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        File[] files = getContext().getCacheDir().listFiles();
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (File file : files) {
137aeeefedb43c40e887850654ac88270755f5631c1Mihai Preda            String filename = file.getName();
138aeeefedb43c40e887850654ac88270755f5631c1Mihai Preda            if (filename.endsWith(".tmp") || filename.startsWith("thmb_")) {
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                file.delete();
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return true;
14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
145301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler    /**
146301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * Returns the mime type for a given attachment.  There are three possible results:
147301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *  - If thumbnail Uri, always returns "image/png" (even if there's no attachment)
148301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *  - If the attachment does not exist, returns null
149301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *  - Returns the mime type of the attachment
150301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     */
15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String getType(Uri uri) {
15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        List<String> segments = uri.getPathSegments();
1543d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        String accountId = segments.get(0);
15596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String id = segments.get(1);
15696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String format = segments.get(2);
15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (FORMAT_THUMBNAIL.equals(format)) {
15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return "image/png";
1593d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        } else {
1603d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id));
16180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            Cursor c = getContext().getContentResolver().query(uri, MIME_TYPE_PROJECTION,
1623d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                    null, null, null);
16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
1643d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                if (c.moveToFirst()) {
16580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                    String mimeType = c.getString(MIME_TYPE_COLUMN_MIME_TYPE);
16680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                    String fileName = c.getString(MIME_TYPE_COLUMN_FILENAME);
16780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                    mimeType = inferMimeType(fileName, mimeType);
16880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                    return mimeType;
16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
1703d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            } finally {
1713d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                c.close();
17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
1733d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            return null;
17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
17596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
177301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler    /**
17880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * Helper to convert unknown or unmapped attachments to something useful based on filename
17980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * extensions.  Imperfect, but helps.
18080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     *
18180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * If the given mime type is non-empty and anything other than "application/octet-stream",
18280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * just return it.  (This is the most common case.)
18380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * If the filename has a recognizable extension and it converts to a mime type, return that.
18480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * If the filename has an unrecognized extension, return "application/extension"
18580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * Otherwise return "application/octet-stream".
18680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     *
18780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * @param fileName The given filename
18880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * @param mimeType The given mime type
18980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     * @return A likely mime type for the attachment
19080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler     */
19180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    public static String inferMimeType(String fileName, String mimeType) {
19280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        // If the given mime type appears to be non-empty and non-generic - return it
19380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        if (!TextUtils.isEmpty(mimeType) &&
19480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                !"application/octet-stream".equalsIgnoreCase(mimeType)) {
19580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            return mimeType;
19680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        }
19780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler
19880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        // Try to find an extension in the filename
19980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        if (!TextUtils.isEmpty(fileName)) {
20080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            int lastDot = fileName.lastIndexOf('.');
20180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            String extension = null;
20280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            if ((lastDot > 0) && (lastDot < fileName.length() - 1)) {
20380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                extension = fileName.substring(lastDot + 1).toLowerCase();
20480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            }
20580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            if (!TextUtils.isEmpty(extension)) {
20680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                // Extension found.  Look up mime type, or synthesize if none found.
20780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
20880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                if (mimeType == null) {
20980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                    mimeType = "application/" + extension;
21080ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                }
21180ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler                return mimeType;
21280ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler            }
21380ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        }
21480ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler
21580ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        // Fallback case - no good guess could be made.
21680ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler        return "application/octet-stream";
21780ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    }
21880ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler
21980ebde2897dced46a0f24efb7c15a997b660a8feAndrew Stadler    /**
220301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * Open an attachment file.  There are two "modes" - "raw", which returns an actual file,
221301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * and "thumbnail", which attempts to generate a thumbnail image.
222301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *
223301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * Thumbnails are cached for easy space recovery and cleanup.
224301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *
225301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * TODO:  The thumbnail mode returns null for its failure cases, instead of throwing
226301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * FileNotFoundException, and should be fixed for consistency.
227301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *
228301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *  @throws FileNotFoundException
229301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     */
23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        List<String> segments = uri.getPathSegments();
2333d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        String accountId = segments.get(0);
23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String id = segments.get(1);
23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String format = segments.get(2);
23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (FORMAT_THUMBNAIL.equals(format)) {
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int width = Integer.parseInt(segments.get(3));
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int height = Integer.parseInt(segments.get(4));
2393d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            String filename = "thmb_" + accountId + "_" + id;
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            File dir = getContext().getCacheDir();
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            File file = new File(dir, filename);
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (!file.exists()) {
2433d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                Uri attachmentUri = getAttachmentUri(Long.parseLong(accountId), Long.parseLong(id));
2445182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                Cursor c = query(attachmentUri,
2455182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                        new String[] { AttachmentProviderColumns.DATA }, null, null, null);
2465182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                if (c != null) {
2475182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                    try {
2485182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                        if (c.moveToFirst()) {
2495182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                            attachmentUri = Uri.parse(c.getString(0));
2503d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                        } else {
2513d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                            return null;
2525182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                        }
2535182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                    } finally {
2545182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                        c.close();
2555182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                    }
2565182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                }
2575182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                String type = getContext().getContentResolver().getType(attachmentUri);
25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                try {
2595182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                    InputStream in =
2605182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                        getContext().getContentResolver().openInputStream(attachmentUri);
26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    Bitmap thumbnail = createThumbnail(type, in);
2625182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                    thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true);
26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    FileOutputStream out = new FileOutputStream(file);
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out);
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    out.close();
26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    in.close();
26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                catch (IOException ioe) {
26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return null;
27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        else {
27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return ParcelFileDescriptor.open(
2763d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                    new File(getContext().getDatabasePath(accountId + ".db_att"), id),
27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
28296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public int delete(Uri uri, String arg1, String[] arg2) {
28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return 0;
28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
28596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public Uri insert(Uri uri, ContentValues values) {
28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return null;
28996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
29096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
291301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler    /**
292301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * Returns a cursor based on the data in the attachments table, or null if the attachment
293301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * is not recorded in the table.
294301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *
295301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * Supports REST Uri only, for a single row - selection, selection args, and sortOrder are
296301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * ignored (non-null values should probably throw an exception....)
297301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     */
29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
29996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
30096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String sortOrder) {
30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (projection == null) {
30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            projection =
30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                new String[] {
30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    AttachmentProviderColumns._ID,
30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    AttachmentProviderColumns.DATA,
30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    };
30796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        List<String> segments = uri.getPathSegments();
3103d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        String accountId = segments.get(0);
31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String id = segments.get(1);
31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String format = segments.get(2);
31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String name = null;
31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int size = -1;
3155182d80674a1ef90586f52355a21a82e8265c10aMihai Preda        String contentUri = null;
3163d25a519abf676f050b546d34401a277aea5de40Andrew Stadler
3173d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id));
3183d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        Cursor c = getContext().getContentResolver().query(uri, PROJECTION_QUERY,
3193d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                null, null, null);
32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        try {
3213d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            if (c.moveToFirst()) {
3223d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                name = c.getString(0);
3233d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                size = c.getInt(1);
3243d25a519abf676f050b546d34401a277aea5de40Andrew Stadler                contentUri = c.getString(2);
3253d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            } else {
32696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return null;
32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
3283d25a519abf676f050b546d34401a277aea5de40Andrew Stadler        } finally {
3293d25a519abf676f050b546d34401a277aea5de40Andrew Stadler            c.close();
33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
33196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        MatrixCursor ret = new MatrixCursor(projection);
33396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Object[] values = new Object[projection.length];
33496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0, count = projection.length; i < count; i++) {
33596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String column = projection[i];
33696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (AttachmentProviderColumns._ID.equals(column)) {
33796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                values[i] = id;
33896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
33996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            else if (AttachmentProviderColumns.DATA.equals(column)) {
3405182d80674a1ef90586f52355a21a82e8265c10aMihai Preda                values[i] = contentUri;
34196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
34296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            else if (AttachmentProviderColumns.DISPLAY_NAME.equals(column)) {
34396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                values[i] = name;
34496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
34596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            else if (AttachmentProviderColumns.SIZE.equals(column)) {
34696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                values[i] = size;
34796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
34896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
34996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        ret.addRow(values);
35096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return ret;
35196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
35296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
35396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
35496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
35596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return 0;
35696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
35796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
35896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private Bitmap createThumbnail(String type, InputStream data) {
35996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if(MimeUtility.mimeTypeMatches(type, "image/*")) {
36096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return createImageThumbnail(data);
36196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
36296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return null;
36396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
36496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
36596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private Bitmap createImageThumbnail(InputStream data) {
36696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        try {
36796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Bitmap bitmap = BitmapFactory.decodeStream(data);
36896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return bitmap;
36996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
37096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        catch (OutOfMemoryError oome) {
37196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /*
37296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * Improperly downloaded images, corrupt bitmaps and the like can commonly
37396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * cause OOME due to invalid allocation sizes. We're happy with a null bitmap in
37496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * that case. If the system is really out of memory we'll know about it soon
37596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * enough.
37696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
37796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
37896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
37996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        catch (Exception e) {
38096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
38196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
38296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
3837436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda    /**
384301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * Resolve attachment id to content URI.  Returns the resolved content URI (from the attachment
385301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * DB) or, if not found, simply returns the incoming value.
3867436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda     *
3877436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda     * @param attachmentUri
3887436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda     * @return resolved content URI
389301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     *
390301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * TODO:  Throws an SQLite exception on a missing DB file (e.g. unknown URI) instead of just
391301ac18bb7f211b473c4f6fa6ec9bb276bbc6c10Andrew Stadler     * returning the incoming uri, as it should.
3927436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda     */
3937436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda    public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) {
3947436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda        Cursor c = resolver.query(attachmentUri,
3957436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda                new String[] { AttachmentProvider.AttachmentProviderColumns.DATA },
3967436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda                null, null, null);
3977436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda        if (c != null) {
3987436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda            try {
3997436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda                if (c.moveToFirst()) {
4009ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda                    final String strUri = c.getString(0);
4019ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda                    if (strUri != null) {
4029ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda                        return Uri.parse(strUri);
4039ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda                    } else {
4049ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda                        Email.log("AttachmentProvider: attachment with null contentUri");
4059ef6f645f57d869a600113f555389b5d5e368c21Mihai Preda                    }
4067436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda                }
4077436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda            } finally {
4087436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda                c.close();
4097436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda            }
4107436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda        }
4117436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda        return attachmentUri;
4127436601fae756e8bc1f916560a1c2d1f8ce576fcMihai Preda    }
4134b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler
4144b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler    /**
4154b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler     * In support of deleting a message, find all attachments and delete associated attachment
4164b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler     * files.
4174b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler     * @param context
4184b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler     * @param accountId the account for the message
4194b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler     * @param messageId the message
4204b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler     */
4214b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler    public static void deleteAllAttachmentFiles(Context context, long accountId, long messageId) {
4224b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler        Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId);
4234b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler        Cursor c = context.getContentResolver().query(uri, Attachment.ID_PROJECTION,
4244b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                null, null, null);
4254b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler        try {
4264b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler            while (c.moveToNext()) {
4274b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                long attachmentId = c.getLong(Attachment.ID_PROJECTION_COLUMN);
4284b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                File attachmentFile = getAttachmentFilename(context, accountId, attachmentId);
4294b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                // Note, delete() throws no exceptions for basic FS errors (e.g. file not found)
4304b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                // it just returns false, which we ignore, and proceed to the next file.
4314b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                // This entire loop is best-effort only.
4324b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler                attachmentFile.delete();
4334b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler            }
4344b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler        } finally {
4354b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler            c.close();
4364b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler        }
4374b41bae270ea4c49ec8403084db43ee9b37cdda4Andrew Stadler    }
43871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler
43971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler    /**
44071754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler     * In support of deleting a mailbox, find all messages and delete their attachments.
44171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler     *
44271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler     * @param context
44371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler     * @param accountId the account for the mailbox
44471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler     * @param mailboxId the mailbox for the messages
44571754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler     */
44671754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler    public static void deleteAllMailboxAttachmentFiles(Context context, long accountId,
44771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler            long mailboxId) {
44871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler        Cursor c = context.getContentResolver().query(Message.CONTENT_URI,
44971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler                Message.ID_COLUMN_PROJECTION, MessageColumns.MAILBOX_KEY + "=?",
45071754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler                new String[] { Long.toString(mailboxId) }, null);
45171754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler        try {
45271754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler            while (c.moveToNext()) {
45371754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler                long messageId = c.getLong(Message.ID_PROJECTION_COLUMN);
45471754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler                deleteAllAttachmentFiles(context, accountId, messageId);
45571754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler            }
45671754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler        } finally {
45771754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler            c.close();
45871754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler        }
45971754d3f940fe82e251c274e3e56781e702cfd6fAndrew Stadler    }
46096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
461