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