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