AttachmentProvider.java revision a290f503f14432163f74548a5e5d1dc5003ad049
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.provider;
18
19import com.android.email.mail.internet.MimeUtility;
20
21import android.content.ContentProvider;
22import android.content.ContentResolver;
23import android.content.ContentValues;
24import android.database.Cursor;
25import android.database.MatrixCursor;
26import android.database.sqlite.SQLiteDatabase;
27import android.graphics.Bitmap;
28import android.graphics.BitmapFactory;
29import android.net.Uri;
30import android.os.ParcelFileDescriptor;
31
32import java.io.File;
33import java.io.FileNotFoundException;
34import java.io.FileOutputStream;
35import java.io.IOException;
36import java.io.InputStream;
37import java.util.List;
38
39/*
40 * A simple ContentProvider that allows file access to Email's attachments.
41 */
42public class AttachmentProvider extends ContentProvider {
43    public static final Uri CONTENT_URI = Uri.parse( "content://com.android.email.attachmentprovider");
44
45    private static final String FORMAT_RAW = "RAW";
46    private static final String FORMAT_THUMBNAIL = "THUMBNAIL";
47
48    public static class AttachmentProviderColumns {
49        public static final String _ID = "_id";
50        public static final String DATA = "_data";
51        public static final String DISPLAY_NAME = "_display_name";
52        public static final String SIZE = "_size";
53    }
54
55    public static Uri getAttachmentUri(EmailContent.Account account, long id) {
56        return CONTENT_URI.buildUpon()
57                .appendPath(account.getUuid() + ".db")
58                .appendPath(Long.toString(id))
59                .appendPath(FORMAT_RAW)
60                .build();
61    }
62
63    public static Uri getAttachmentThumbnailUri(EmailContent.Account account, long id,
64            int width, int height) {
65        return CONTENT_URI.buildUpon()
66                .appendPath(account.getUuid() + ".db")
67                .appendPath(Long.toString(id))
68                .appendPath(FORMAT_THUMBNAIL)
69                .appendPath(Integer.toString(width))
70                .appendPath(Integer.toString(height))
71                .build();
72    }
73
74    public static Uri getAttachmentUri(String db, long id) {
75        return CONTENT_URI.buildUpon()
76                .appendPath(db)
77                .appendPath(Long.toString(id))
78                .appendPath(FORMAT_RAW)
79                .build();
80    }
81
82    @Override
83    public boolean onCreate() {
84        /*
85         * We use the cache dir as a temporary directory (since Android doesn't give us one) so
86         * on startup we'll clean up any .tmp files from the last run.
87         */
88        File[] files = getContext().getCacheDir().listFiles();
89        for (File file : files) {
90            if (file.getName().endsWith(".tmp")) {
91                file.delete();
92            }
93        }
94        return true;
95    }
96
97    @Override
98    public String getType(Uri uri) {
99        List<String> segments = uri.getPathSegments();
100        String dbName = segments.get(0);
101        String id = segments.get(1);
102        String format = segments.get(2);
103        if (FORMAT_THUMBNAIL.equals(format)) {
104            return "image/png";
105        }
106        else {
107            String path = getContext().getDatabasePath(dbName).getAbsolutePath();
108            SQLiteDatabase db = null;
109            Cursor cursor = null;
110            try {
111                db = SQLiteDatabase.openDatabase(path, null, 0);
112                cursor = db.query(
113                        "attachments",
114                        new String[] { "mime_type" },
115                        "id = ?",
116                        new String[] { id },
117                        null,
118                        null,
119                        null);
120                cursor.moveToFirst();
121                String type = cursor.getString(0);
122                cursor.close();
123                db.close();
124                return type;
125
126            }
127            finally {
128                if (cursor != null) {
129                    cursor.close();
130                }
131                if (db != null) {
132                    db.close();
133                }
134
135            }
136        }
137    }
138
139    @Override
140    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
141        List<String> segments = uri.getPathSegments();
142        String dbName = segments.get(0);
143        String id = segments.get(1);
144        String format = segments.get(2);
145        if (FORMAT_THUMBNAIL.equals(format)) {
146            int width = Integer.parseInt(segments.get(3));
147            int height = Integer.parseInt(segments.get(4));
148            String filename = "thmb_" + dbName + "_" + id;
149            File dir = getContext().getCacheDir();
150            File file = new File(dir, filename);
151            if (!file.exists()) {
152                Uri attachmentUri = getAttachmentUri(dbName, Long.parseLong(id));
153                Cursor c = query(attachmentUri,
154                        new String[] { AttachmentProviderColumns.DATA }, null, null, null);
155                if (c != null) {
156                    try {
157                        if (c.moveToFirst()) {
158                            attachmentUri = Uri.parse(c.getString(0));
159                        }
160                    } finally {
161                        c.close();
162                    }
163                }
164                String type = getContext().getContentResolver().getType(attachmentUri);
165                try {
166                    InputStream in =
167                        getContext().getContentResolver().openInputStream(attachmentUri);
168                    Bitmap thumbnail = createThumbnail(type, in);
169                    thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true);
170                    FileOutputStream out = new FileOutputStream(file);
171                    thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out);
172                    out.close();
173                    in.close();
174                }
175                catch (IOException ioe) {
176                    return null;
177                }
178            }
179            return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
180        }
181        else {
182            return ParcelFileDescriptor.open(
183                    new File(getContext().getDatabasePath(dbName + "_att"), id),
184                    ParcelFileDescriptor.MODE_READ_ONLY);
185        }
186    }
187
188    @Override
189    public int delete(Uri uri, String arg1, String[] arg2) {
190        return 0;
191    }
192
193    @Override
194    public Uri insert(Uri uri, ContentValues values) {
195        return null;
196    }
197
198    @Override
199    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
200            String sortOrder) {
201        if (projection == null) {
202            projection =
203                new String[] {
204                    AttachmentProviderColumns._ID,
205                    AttachmentProviderColumns.DATA,
206                    };
207        }
208
209        List<String> segments = uri.getPathSegments();
210        String dbName = segments.get(0);
211        String id = segments.get(1);
212        String format = segments.get(2);
213        String path = getContext().getDatabasePath(dbName).getAbsolutePath();
214        String name = null;
215        int size = -1;
216        String contentUri = null;
217        SQLiteDatabase db = null;
218        Cursor cursor = null;
219        try {
220            db = SQLiteDatabase.openDatabase(path, null, 0);
221            cursor = db.query(
222                    "attachments",
223                    new String[] { "name", "size", "content_uri" },
224                    "id = ?",
225                    new String[] { id },
226                    null,
227                    null,
228                    null);
229            if (!cursor.moveToFirst()) {
230                return null;
231            }
232            name = cursor.getString(0);
233            size = cursor.getInt(1);
234            contentUri = cursor.getString(2);
235        }
236        finally {
237            if (cursor != null) {
238                cursor.close();
239            }
240            if (db != null) {
241                db.close();
242            }
243        }
244
245        MatrixCursor ret = new MatrixCursor(projection);
246        Object[] values = new Object[projection.length];
247        for (int i = 0, count = projection.length; i < count; i++) {
248            String column = projection[i];
249            if (AttachmentProviderColumns._ID.equals(column)) {
250                values[i] = id;
251            }
252            else if (AttachmentProviderColumns.DATA.equals(column)) {
253                values[i] = contentUri;
254            }
255            else if (AttachmentProviderColumns.DISPLAY_NAME.equals(column)) {
256                values[i] = name;
257            }
258            else if (AttachmentProviderColumns.SIZE.equals(column)) {
259                values[i] = size;
260            }
261        }
262        ret.addRow(values);
263        return ret;
264    }
265
266    @Override
267    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
268        return 0;
269    }
270
271    private Bitmap createThumbnail(String type, InputStream data) {
272        if(MimeUtility.mimeTypeMatches(type, "image/*")) {
273            return createImageThumbnail(data);
274        }
275        return null;
276    }
277
278    private Bitmap createImageThumbnail(InputStream data) {
279        try {
280            Bitmap bitmap = BitmapFactory.decodeStream(data);
281            return bitmap;
282        }
283        catch (OutOfMemoryError oome) {
284            /*
285             * Improperly downloaded images, corrupt bitmaps and the like can commonly
286             * cause OOME due to invalid allocation sizes. We're happy with a null bitmap in
287             * that case. If the system is really out of memory we'll know about it soon
288             * enough.
289             */
290            return null;
291        }
292        catch (Exception e) {
293            return null;
294        }
295    }
296    /**
297     * Resolve attachment id to content URI.
298     *
299     * @param attachmentUri
300     * @return resolved content URI
301     */
302    public static Uri resolveAttachmentIdToContentUri(ContentResolver resolver, Uri attachmentUri) {
303        Cursor c = resolver.query(attachmentUri,
304                new String[] { AttachmentProvider.AttachmentProviderColumns.DATA },
305                null, null, null);
306        if (c != null) {
307            try {
308                if (c.moveToFirst()) {
309                    return Uri.parse(c.getString(0));
310                }
311            } finally {
312                c.close();
313            }
314        }
315        return attachmentUri;
316    }
317}
318