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