17434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein/*
27434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * Copyright (C) 2013 Google Inc.
37434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * Licensed to The Android Open Source Project.
47434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein *
57434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License");
67434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * you may not use this file except in compliance with the License.
77434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * You may obtain a copy of the License at
87434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein *
97434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein *      http://www.apache.org/licenses/LICENSE-2.0
107434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein *
117434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * Unless required by applicable law or agreed to in writing, software
127434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS,
137434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
147434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * See the License for the specific language governing permissions and
157434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * limitations under the License.
167434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein */
177434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
187434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinpackage com.android.mail.providers;
197434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
207434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.app.DownloadManager;
217434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.content.ContentProvider;
22934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieuximport android.content.ContentResolver;
237434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.content.ContentValues;
247434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.content.Context;
257434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.content.Intent;
267434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.content.UriMatcher;
277434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.database.Cursor;
287434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.database.MatrixCursor;
297434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.net.Uri;
307434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.os.Environment;
317434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.os.ParcelFileDescriptor;
327434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport android.os.SystemClock;
33934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieuximport android.text.TextUtils;
347434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
357434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport com.android.ex.photo.provider.PhotoContract;
367434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport com.android.mail.R;
377434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport com.android.mail.utils.LogTag;
387434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport com.android.mail.utils.LogUtils;
391317896352698e41d2053d6f83175c181c6e39f0Andrew Sappersteinimport com.android.mail.utils.MimeType;
407434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport com.google.common.collect.Lists;
417434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport com.google.common.collect.Maps;
427434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
437434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.File;
447434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.FileInputStream;
457434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.FileNotFoundException;
467434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.FileOutputStream;
477434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.IOException;
487434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.InputStream;
497434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.io.OutputStream;
507434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.util.List;
517434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinimport java.util.Map;
527434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
537434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein/**
547434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * A {@link ContentProvider} for attachments created from eml files.
557434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * Supports all of the semantics (query/insert/update/delete/openFile)
567434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * of the regular attachment provider.
577434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein *
587434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * One major difference is that all attachment info is stored in memory (with the
597434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * exception of the attachment raw data which is stored in the cache). When
607434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * the process is killed, all of the attachments disappear if they still
617434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein * exist.
627434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein */
637434e800d4313a227120ca36bd95683752a7879fAndrew Sappersteinpublic class EmlAttachmentProvider extends ContentProvider {
647434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static final String LOG_TAG = LogTag.getLogTag();
657434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
667434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
677434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static boolean sUrisAddedToMatcher = false;
687434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
697434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static final int ATTACHMENT_LIST = 0;
707434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static final int ATTACHMENT = 1;
71934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux    private static final int ATTACHMENT_BY_CID = 2;
727434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
737434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
747434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * The buffer size used to copy data from cache to sd card.
757434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
767434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static final int BUFFER_SIZE = 4096;
777434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
787434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /** Any IO reads should be limited to this timeout */
797434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static final long READ_TIMEOUT = 3600 * 1000;
807434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
817434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private static Uri BASE_URI;
827434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
837434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private DownloadManager mDownloadManager;
847434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
857434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
867434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Map that contains a mapping from an attachment list uri to a list of uris.
877434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
887434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private Map<Uri, List<Uri>> mUriListMap;
897434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
907434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
917434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Map that contains a mapping from an attachment uri to an {@link Attachment} object.
927434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
937434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private Map<Uri, Attachment> mUriAttachmentMap;
947434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
957434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
967434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
977434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public boolean onCreate() {
987434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final String authority =
997434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                getContext().getResources().getString(R.string.eml_attachment_provider);
1007434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        BASE_URI = new Uri.Builder().scheme("content").authority(authority).build();
1017434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1027434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (!sUrisAddedToMatcher) {
1037434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            sUrisAddedToMatcher = true;
104934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            sUriMatcher.addURI(authority, "attachments/*/*", ATTACHMENT_LIST);
105934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            sUriMatcher.addURI(authority, "attachment/*/*/#", ATTACHMENT);
106934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            sUriMatcher.addURI(authority, "attachmentByCid/*/*/*", ATTACHMENT_BY_CID);
1077434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
1087434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1097434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        mDownloadManager =
1107434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
1117434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1127434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        mUriListMap = Maps.newHashMap();
1137434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        mUriAttachmentMap = Maps.newHashMap();
1147434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return true;
1157434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
1167434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1177434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
1187434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
1197434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            String sortOrder) {
1207434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final int match = sUriMatcher.match(uri);
1217434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // ignore other projections
1227434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final MatrixCursor cursor = new MatrixCursor(UIProvider.ATTACHMENT_PROJECTION);
123934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux        final ContentResolver cr = getContext().getContentResolver();
1247434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1257434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        switch (match) {
126934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            case ATTACHMENT_LIST: {
1277434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                final List<String> contentTypeQueryParameters =
1287434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                        uri.getQueryParameters(PhotoContract.ContentTypeParameters.CONTENT_TYPE);
1297434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                uri = uri.buildUpon().clearQuery().build();
1307434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                final List<Uri> attachmentUris = mUriListMap.get(uri);
1317434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                for (final Uri attachmentUri : attachmentUris) {
1327434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    addRow(cursor, attachmentUri, contentTypeQueryParameters);
1337434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
134934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                cursor.setNotificationUri(cr, uri);
1357434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                break;
136934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            }
137934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            case ATTACHMENT: {
1387434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                addRow(cursor, mUriAttachmentMap.get(uri));
139934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                cursor.setNotificationUri(cr, getListUriFromAttachmentUri(uri));
140934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                break;
141934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            }
142934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            case ATTACHMENT_BY_CID: {
143934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                // form the attachment lists uri by clipping off the cid from the given uri
144934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                final Uri attachmentsListUri = getListUriFromAttachmentUri(uri);
145934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                final String cid = uri.getPathSegments().get(3);
146934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux
147934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                // find all uris for the parent message
148934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                final List<Uri> attachmentUris = mUriListMap.get(attachmentsListUri);
149934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux
150934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                if (attachmentUris != null) {
151934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                    // find the attachment that contains the given cid
152934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                    for (Uri attachmentsUri : attachmentUris) {
153934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                        final Attachment attachment = mUriAttachmentMap.get(attachmentsUri);
154934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                        if (TextUtils.equals(cid, attachment.partId)) {
155934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                            addRow(cursor, attachment);
156934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                            cursor.setNotificationUri(cr, attachmentsListUri);
157934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                            break;
158934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                        }
159934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                    }
160934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                }
1617434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                break;
162934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux            }
1637434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            default:
1647434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                break;
1657434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
1667434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1677434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return cursor;
1687434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
1697434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1707434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
1717434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public String getType(Uri uri) {
1727434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final int match = sUriMatcher.match(uri);
1737434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        switch (match) {
1747434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            case ATTACHMENT:
1757434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return mUriAttachmentMap.get(uri).getContentType();
1767434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            default:
1777434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return null;
1787434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
1797434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
1807434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1817434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
1827434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public Uri insert(Uri uri, ContentValues values) {
1837434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final Uri listUri = getListUriFromAttachmentUri(uri);
1847434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1857434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // add mapping from uri to attachment
1867434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (mUriAttachmentMap.put(uri, new Attachment(values)) == null) {
1877434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            // only add uri to list if the list
1887434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            // get list of attachment uris, creating if necessary
1897434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            List<Uri> list = mUriListMap.get(listUri);
1907434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            if (list == null) {
1917434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                list = Lists.newArrayList();
1927434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                mUriListMap.put(listUri, list);
1937434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
1947434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1957434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            list.add(uri);
1967434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
1977434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
1987434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return uri;
1997434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
2007434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2017434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
2027434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public int delete(Uri uri, String selection, String[] selectionArgs) {
2037434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final int match = sUriMatcher.match(uri);
2047434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        switch (match) {
2057434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            case ATTACHMENT_LIST:
2067434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                // remove from list mapping
2077434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                final List<Uri> attachmentUris = mUriListMap.remove(uri);
2087434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2097434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                // delete each file and remove each element from the mapping
2107434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                for (final Uri attachmentUri : attachmentUris) {
2117434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    mUriAttachmentMap.remove(attachmentUri);
2127434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
2137434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2147434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                deleteDirectory(getCacheFileDirectory(uri));
2157434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                // return rows affected
2167434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return attachmentUris.size();
2177434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            default:
2187434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return 0;
2197434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
2207434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
2217434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2227434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
2237434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2247434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final int match = sUriMatcher.match(uri);
2257434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        switch (match) {
2267434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            case ATTACHMENT:
2277434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return copyAttachment(uri, values);
2287434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            default:
2297434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return 0;
2307434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
2317434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
2327434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2337434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
2347434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Adds a row to the cursor for the attachment at the specific attachment {@link Uri}
2357434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * if the attachment's mime type matches one of the query parameters.
2367434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     *
2377434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Matching is defined to be starting with one of the query parameters. If no
2387434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * parameters exist, all rows are added.
2397434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
2407434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private void addRow(MatrixCursor cursor, Uri uri,
2417434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            List<String> contentTypeQueryParameters) {
2427434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final Attachment attachment = mUriAttachmentMap.get(uri);
2437434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2447434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (contentTypeQueryParameters != null && !contentTypeQueryParameters.isEmpty()) {
2457434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            for (final String type : contentTypeQueryParameters) {
2467434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                if (attachment.getContentType().startsWith(type)) {
2477434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    addRow(cursor, attachment);
2487434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    return;
2497434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
2507434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
2517434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        } else {
2527434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            addRow(cursor, attachment);
2537434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
2547434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
2557434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2567434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
2577434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Adds a new row to the cursor for the specific attachment.
2587434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
2593b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy    private static void addRow(MatrixCursor cursor, Attachment attachment) {
2607434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        cursor.newRow()
2617434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.getName())                          // displayName
2627434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.size)                               // size
2637434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.uri)                                // uri
2647434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.getContentType())                   // contentType
2657434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.state)                              // state
2667434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.destination)                        // destination
2677434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.downloadedSize)                     // downloadedSize
2687434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.contentUri)                         // contentUri
2697434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.thumbnailUri)                       // thumbnailUri
2707434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.previewIntentUri)                   // previewIntentUri
2717434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                .add(attachment.providerData)                       // providerData
272e307785b44deec1eb2aad5ecf83ea4b581779baaAndrew Sapperstein                .add(attachment.supportsDownloadAgain() ? 1 : 0)    // supportsDownloadAgain
273e307785b44deec1eb2aad5ecf83ea4b581779baaAndrew Sapperstein                .add(attachment.type)                               // type
274934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .add(attachment.flags)                              // flags
275934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .add(attachment.partId);                            // partId (same as RFC822 cid)
2767434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
2777434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2787434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
2797434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Copies an attachment at the specified {@link Uri}
2807434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * from cache to the external downloads directory (usually the sd card).
2817434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * @return the number of attachments affected. Should be 1 or 0.
2827434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
2837434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private int copyAttachment(Uri uri, ContentValues values) {
2847434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final Integer newState = values.getAsInteger(UIProvider.AttachmentColumns.STATE);
2857434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final Integer newDestination =
2867434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                values.getAsInteger(UIProvider.AttachmentColumns.DESTINATION);
2877434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (newState == null && newDestination == null) {
2887434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            return 0;
2897434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
2907434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2917434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final int destination = newDestination != null ?
2927434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                newDestination.intValue() : UIProvider.AttachmentDestination.CACHE;
2937434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final boolean saveToSd =
2947434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                destination == UIProvider.AttachmentDestination.EXTERNAL;
2957434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2967434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final Attachment attachment = mUriAttachmentMap.get(uri);
2977434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
2987434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // 1. check if already saved to sd (via uri save to sd)
2997434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // and return if so (we shouldn't ever be here)
3007434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3017434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // if the call was not to save to sd or already saved to sd, just bail out
3027434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (!saveToSd || attachment.isSavedToExternal()) {
3037434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            return 0;
3047434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
3057434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3067434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3077434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // 2. copy file
3087434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final String oldFilePath = getFilePath(uri);
3097434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3107434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // update the destination before getting the new file path
3117434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // otherwise it will just point to the old location.
3127434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        attachment.destination = UIProvider.AttachmentDestination.EXTERNAL;
3137434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final String newFilePath = getFilePath(uri);
3147434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3157434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        InputStream inputStream = null;
3167434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        OutputStream outputStream = null;
3177434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3187434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        try {
3197434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            try {
3207434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                inputStream = new FileInputStream(oldFilePath);
3217434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            } catch (FileNotFoundException e) {
3227434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                LogUtils.e(LOG_TAG, "File not found for file %s", oldFilePath);
3237434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return 0;
3247434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
3257434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            try {
3267434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                outputStream = new FileOutputStream(newFilePath);
3277434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            } catch (FileNotFoundException e) {
3287434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                LogUtils.e(LOG_TAG, "File not found for file %s", newFilePath);
3297434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                return 0;
3307434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
3317434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            try {
3327434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                final long now = SystemClock.elapsedRealtime();
3337434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                final byte data[] = new byte[BUFFER_SIZE];
3347434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                int size = 0;
3357434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                while (true) {
3367434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    final int len = inputStream.read(data);
3377434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    if (len != -1) {
3387434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                        outputStream.write(data, 0, len);
3397434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3407434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                        size += len;
3417434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    } else {
3427434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                        break;
3437434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    }
3447434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    if (SystemClock.elapsedRealtime() - now > READ_TIMEOUT) {
3457434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                        throw new IOException("Timed out copying attachment.");
3467434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    }
3477434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
3487434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3491317896352698e41d2053d6f83175c181c6e39f0Andrew Sapperstein                // if the attachment is an APK, change contentUri to be a direct file uri
3501317896352698e41d2053d6f83175c181c6e39f0Andrew Sapperstein                if (MimeType.isInstallable(attachment.getContentType())) {
3511317896352698e41d2053d6f83175c181c6e39f0Andrew Sapperstein                    attachment.contentUri = Uri.parse("file://" + newFilePath);
3521317896352698e41d2053d6f83175c181c6e39f0Andrew Sapperstein                }
3531317896352698e41d2053d6f83175c181c6e39f0Andrew Sapperstein
3547434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                // 3. add file to download manager
3557434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3567434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                try {
3577434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    // TODO - make a better description
3587434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    final String description = attachment.getName();
3597434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    mDownloadManager.addCompletedDownload(attachment.getName(),
3607434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                            description, true, attachment.getContentType(),
3617434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                            newFilePath, size, false);
3627434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
3637434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                catch (IllegalArgumentException e) {
3647434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    // Even if we cannot save the download to the downloads app,
3657434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    // (likely due to a bad mimeType), we still want to save it.
3667434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    LogUtils.e(LOG_TAG, e, "Failed to save download to Downloads app.");
3677434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
3687434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
3697434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                intent.setData(Uri.parse("file://" + newFilePath));
3707434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                getContext().sendBroadcast(intent);
3717434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3727434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                // 4. delete old file
3737434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                new File(oldFilePath).delete();
3747434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            } catch (IOException e) {
3757434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                // Error writing file, delete partial file
3767434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                LogUtils.e(LOG_TAG, e, "Cannot write to file %s", newFilePath);
3777434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                new File(newFilePath).delete();
3787434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
3797434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        } finally {
3807434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            try {
3817434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                if (inputStream != null) {
3827434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    inputStream.close();
3837434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
3847434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            } catch (IOException e) {
3857434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
3867434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            try {
3877434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                if (outputStream != null) {
3887434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    outputStream.close();
3897434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                }
3907434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            } catch (IOException e) {
3917434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
3927434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
3937434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
3947434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // 5. notify that the list of attachments has changed so the UI will update
3957434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        getContext().getContentResolver().notifyChange(
3967434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                getListUriFromAttachmentUri(uri), null, false);
3977434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return 1;
3987434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
3997434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4007434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    @Override
4017434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
4027434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final String filePath = getFilePath(uri);
4037434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4047434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final int fileMode;
4057434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4067434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if ("rwt".equals(mode)) {
4077434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            fileMode = ParcelFileDescriptor.MODE_READ_WRITE |
4087434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    ParcelFileDescriptor.MODE_TRUNCATE |
4097434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                    ParcelFileDescriptor.MODE_CREATE;
4107434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        } else if ("rw".equals(mode)) {
4117434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            fileMode = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE;
4127434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        } else {
4137434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            fileMode = ParcelFileDescriptor.MODE_READ_ONLY;
4147434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
4157434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4167434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return ParcelFileDescriptor.open(new File(filePath), fileMode);
4177434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
4187434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4197434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
420934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux     * Returns an attachment list uri for the specific attachment uri passed.
421934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux     */
422934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux    private static Uri getListUriFromAttachmentUri(Uri uri) {
423934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux        final List<String> segments = uri.getPathSegments();
424934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux        return BASE_URI.buildUpon()
425934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath("attachments")
426934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(segments.get(1))
427934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(segments.get(2))
428934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .build();
429934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux    }
430934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux
431934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux    /**
432934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux     * Returns an attachment list uri for an eml file at the given uri with the given message id.
4337434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
4347434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public static Uri getAttachmentsListUri(Uri emlFileUri, String messageId) {
435934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux        return BASE_URI.buildUpon()
436934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath("attachments")
437934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(Integer.toString(emlFileUri.hashCode()))
438934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(messageId)
439934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .build();
4407434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
4417434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4427434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
443934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux     * Returns an attachment uri for an eml file at the given uri with the given message id.
444934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux     * The consumer of this uri must append a specific CID to it to complete the uri.
4457434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
446934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux    public static Uri getAttachmentByCidUri(Uri emlFileUri, String messageId) {
4477434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return BASE_URI.buildUpon()
448934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath("attachmentByCid")
449934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(Integer.toString(emlFileUri.hashCode()))
450934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(messageId)
451934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .build();
4527434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
4537434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4547434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
4557434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Returns an attachment uri for an attachment from the given eml file uri with
4567434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * the given message id and part id.
4577434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
4587434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    public static Uri getAttachmentUri(Uri emlFileUri, String messageId, String partId) {
459934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux        return BASE_URI.buildUpon()
460934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath("attachment")
461934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(Integer.toString(emlFileUri.hashCode()))
462934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(messageId)
463934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .appendPath(partId)
464934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux                .build();
4657434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
4667434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4677434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
4687434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Returns the absolute file path for the attachment at the given uri.
4697434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
4707434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private String getFilePath(Uri uri) {
4717434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final Attachment attachment = mUriAttachmentMap.get(uri);
4727434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final boolean saveToSd =
4737434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                attachment.destination == UIProvider.AttachmentDestination.EXTERNAL;
4747434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final String pathStart = (saveToSd) ?
4757434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                Environment.getExternalStoragePublicDirectory(
4767434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() : getCacheDir();
4777434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4787434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // we want the root of the downloads directory if the attachment is
4797434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        // saved to external (or we're saving to external)
4807434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final String directoryPath = (saveToSd) ? pathStart : pathStart + uri.getEncodedPath();
4817434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4827434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        final File directory = new File(directoryPath);
4837434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (!directory.exists()) {
4847434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            directory.mkdirs();
4857434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
4867434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return directoryPath + "/" + attachment.getName();
4877434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
4887434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4897434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
4907434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Returns the root directory for the attachments for the specific uri.
4917434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
4927434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private String getCacheFileDirectory(Uri uri) {
493934b1f470a646a4ca64fc63eeb823d4c5c12698eJames Lemieux        return getCacheDir() + "/" + Uri.encode(uri.getPathSegments().get(1));
4947434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
4957434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
4967434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
4977434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Returns the cache directory for eml attachment files.
4987434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
4997434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private String getCacheDir() {
5007434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        return getContext().getCacheDir().getAbsolutePath().concat("/eml");
5017434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
5027434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
5037434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
5047434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Recursively delete the directory at the passed file path.
5057434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
5067434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private void deleteDirectory(String cacheFileDirectory) {
5077434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        recursiveDelete(new File(cacheFileDirectory));
5087434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
5097434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
5107434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    /**
5117434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     * Recursively deletes a file or directory.
5127434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein     */
5137434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    private void recursiveDelete(File file) {
5147434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        if (file.isDirectory()) {
5157434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            final File[] children = file.listFiles();
5167434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            for (final File child : children) {
5177434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein                recursiveDelete(child);
5187434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein            }
5197434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        }
5207434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein
5217434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein        file.delete();
5227434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein    }
5237434e800d4313a227120ca36bd95683752a7879fAndrew Sapperstein}
524