From 80ebde2897dced46a0f24efb7c15a997b660a8fe Mon Sep 17 00:00:00 2001 From: Andrew Stadler Date: Fri, 30 Apr 2010 15:01:10 -0700 Subject: [PATCH] Better handling for untyped attachments * IMAP/POP rely on sender to set mime type of attachments * Which doesn't always work, because senders don't always provide it * Remap using filename extensions, when needed * This is applied as late as possible - in the MessageView, and in the content provider getType(). No changes to how we write databases, and no change to existing attachment rows. Bug: 2356638 Change-Id: Ie69e3fd12f406aac803583f9d1299a8af4fba010 --- .../android/email/activity/MessageView.java | 8 +-- .../email/provider/AttachmentProvider.java | 59 +++++++++++++++-- .../provider/AttachmentProviderTests.java | 66 +++++++++++++++++++ 3 files changed, 123 insertions(+), 10 deletions(-) diff --git a/src/com/android/email/activity/MessageView.java b/src/com/android/email/activity/MessageView.java index d9e5ea295..706b2ab91 100644 --- a/src/com/android/email/activity/MessageView.java +++ b/src/com/android/email/activity/MessageView.java @@ -1004,15 +1004,11 @@ public class MessageView extends Activity implements OnClickListener { AttachmentInfo attachmentInfo = new AttachmentInfo(); attachmentInfo.size = attachment.mSize; - attachmentInfo.contentType = attachment.mMimeType; + attachmentInfo.contentType = + AttachmentProvider.inferMimeType(attachment.mFileName, attachment.mMimeType); attachmentInfo.name = attachment.mFileName; attachmentInfo.attachmentId = attachment.mId; - // TODO: remove this when EAS writes mime types - if (attachmentInfo.contentType == null || attachmentInfo.contentType.length() == 0) { - attachmentInfo.contentType = "application/octet-stream"; - } - LayoutInflater inflater = getLayoutInflater(); View view = inflater.inflate(R.layout.message_view_attachment, null); diff --git a/src/com/android/email/provider/AttachmentProvider.java b/src/com/android/email/provider/AttachmentProvider.java index e8221c4b0..3b7818d74 100644 --- a/src/com/android/email/provider/AttachmentProvider.java +++ b/src/com/android/email/provider/AttachmentProvider.java @@ -34,6 +34,8 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.text.TextUtils; +import android.webkit.MimeTypeMap; import java.io.File; import java.io.FileNotFoundException; @@ -75,8 +77,12 @@ public class AttachmentProvider extends ContentProvider { public static final String SIZE = "_size"; } - private String[] PROJECTION_MIME_TYPE = new String[] { AttachmentColumns.MIME_TYPE }; - private String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME, + private static final String[] MIME_TYPE_PROJECTION = new String[] { + AttachmentColumns.MIME_TYPE, AttachmentColumns.FILENAME }; + private static final int MIME_TYPE_COLUMN_MIME_TYPE = 0; + private static final int MIME_TYPE_COLUMN_FILENAME = 1; + + private static final String[] PROJECTION_QUERY = new String[] { AttachmentColumns.FILENAME, AttachmentColumns.SIZE, AttachmentColumns.CONTENT_URI }; public static Uri getAttachmentUri(long accountId, long id) { @@ -152,11 +158,14 @@ public class AttachmentProvider extends ContentProvider { return "image/png"; } else { uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, Long.parseLong(id)); - Cursor c = getContext().getContentResolver().query(uri, PROJECTION_MIME_TYPE, + Cursor c = getContext().getContentResolver().query(uri, MIME_TYPE_PROJECTION, null, null, null); try { if (c.moveToFirst()) { - return c.getString(0); + String mimeType = c.getString(MIME_TYPE_COLUMN_MIME_TYPE); + String fileName = c.getString(MIME_TYPE_COLUMN_FILENAME); + mimeType = inferMimeType(fileName, mimeType); + return mimeType; } } finally { c.close(); @@ -165,6 +174,48 @@ public class AttachmentProvider extends ContentProvider { } } + /** + * Helper to convert unknown or unmapped attachments to something useful based on filename + * extensions. Imperfect, but helps. + * + * If the given mime type is non-empty and anything other than "application/octet-stream", + * just return it. (This is the most common case.) + * If the filename has a recognizable extension and it converts to a mime type, return that. + * If the filename has an unrecognized extension, return "application/extension" + * Otherwise return "application/octet-stream". + * + * @param fileName The given filename + * @param mimeType The given mime type + * @return A likely mime type for the attachment + */ + public static String inferMimeType(String fileName, String mimeType) { + // If the given mime type appears to be non-empty and non-generic - return it + if (!TextUtils.isEmpty(mimeType) && + !"application/octet-stream".equalsIgnoreCase(mimeType)) { + return mimeType; + } + + // Try to find an extension in the filename + if (!TextUtils.isEmpty(fileName)) { + int lastDot = fileName.lastIndexOf('.'); + String extension = null; + if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { + extension = fileName.substring(lastDot + 1).toLowerCase(); + } + if (!TextUtils.isEmpty(extension)) { + // Extension found. Look up mime type, or synthesize if none found. + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mimeType == null) { + mimeType = "application/" + extension; + } + return mimeType; + } + } + + // Fallback case - no good guess could be made. + return "application/octet-stream"; + } + /** * Open an attachment file. There are two "modes" - "raw", which returns an actual file, * and "thumbnail", which attempts to generate a thumbnail image. diff --git a/tests/src/com/android/email/provider/AttachmentProviderTests.java b/tests/src/com/android/email/provider/AttachmentProviderTests.java index f566a5648..2b67a28f0 100644 --- a/tests/src/com/android/email/provider/AttachmentProviderTests.java +++ b/tests/src/com/android/email/provider/AttachmentProviderTests.java @@ -199,6 +199,9 @@ public class AttachmentProviderTests extends ProviderTestCase2