diff --git a/res/layout/message_view_attachment.xml b/res/layout/message_view_attachment.xml index 8ce72d567..6d8612333 100644 --- a/res/layout/message_view_attachment.xml +++ b/res/layout/message_view_attachment.xml @@ -1,5 +1,5 @@ - - + diff --git a/src/com/fsck/k9/activity/MessageView.java b/src/com/fsck/k9/activity/MessageView.java index 40a6b1535..03955e244 100644 --- a/src/com/fsck/k9/activity/MessageView.java +++ b/src/com/fsck/k9/activity/MessageView.java @@ -1,19 +1,5 @@ package com.fsck.k9.activity; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.apache.commons.io.IOUtils; import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentResolver; @@ -23,12 +9,8 @@ import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.media.MediaScannerConnection; -import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.Bundle; import android.os.Environment; @@ -39,50 +21,29 @@ import android.text.style.StyleSpan; import android.util.Config; import android.util.Log; import android.util.TypedValue; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; +import android.view.*; import android.view.View.OnClickListener; -import android.view.Window; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; -import com.fsck.k9.Account; -import com.fsck.k9.FontSizes; -import com.fsck.k9.K9; -import com.fsck.k9.Preferences; -import com.fsck.k9.R; +import android.widget.*; +import com.fsck.k9.*; import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.crypto.CryptoProvider; import com.fsck.k9.crypto.PgpData; import com.fsck.k9.helper.Contacts; -import com.fsck.k9.helper.SizeFormatter; import com.fsck.k9.helper.Utility; -import com.fsck.k9.mail.Address; -import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.Multipart; -import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart; import com.fsck.k9.mail.store.LocalStore.LocalMessage; -import com.fsck.k9.provider.AttachmentProvider; +import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.view.AccessibleWebView; +import com.fsck.k9.view.AttachmentView; import com.fsck.k9.view.MessageWebView; import com.fsck.k9.view.ToggleScrollView; -import static com.fsck.k9.helper.Utility.*; +import java.io.Serializable; +import java.util.*; public class MessageView extends K9Activity implements OnClickListener { @@ -415,7 +376,7 @@ public class MessageView extends K9Activity implements OnClickListener { for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) { - AttachmentViewHolder attachment = (AttachmentViewHolder) mAttachments.getChildAt(i).getTag(); + AttachmentView attachment = (AttachmentView) mAttachments.getChildAt(i); attachment.viewButton.setEnabled(enabled); attachment.downloadButton.setEnabled(enabled); } @@ -516,31 +477,6 @@ public class MessageView extends K9Activity implements OnClickListener }); } - public void attachmentSaved(final String filename) - { - runOnUiThread(new Runnable() - { - public void run() - { - Toast.makeText(MessageView.this, String.format( - getString(R.string.message_view_status_attachment_saved), filename), - Toast.LENGTH_LONG).show(); - } - }); - } - - public void attachmentNotSaved() - { - runOnUiThread(new Runnable() - { - public void run() - { - Toast.makeText(MessageView.this, - getString(R.string.message_view_status_attachment_not_saved), - Toast.LENGTH_LONG).show(); - } - }); - } public void fetchingAttachment() { @@ -686,16 +622,6 @@ public class MessageView extends K9Activity implements OnClickListener } } - static class AttachmentViewHolder - { - public String name; - public String contentType; - public long size; - public LocalAttachmentBodyPart part; - public Button viewButton; - public Button downloadButton; - public ImageView iconView; - } public static void actionView(Context context, MessageReference messRef, List messReferences) { @@ -1558,7 +1484,7 @@ public class MessageView extends K9Activity implements OnClickListener mListener); } - private void onDownloadAttachment(AttachmentViewHolder attachment) + private void onDownloadAttachment(AttachmentView attachment) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { @@ -1582,19 +1508,6 @@ public class MessageView extends K9Activity implements OnClickListener } } - private void onViewAttachment(AttachmentViewHolder attachment) - { - if (mMessage != null) - { - MessagingController.getInstance(getApplication()).loadAttachment( - mAccount, - mMessage, - attachment.part, - new Object[] { false, attachment }, - mListener); - } - } - private void onShowPictures() { // TODO: Download attachments that are used as inline image @@ -1658,10 +1571,7 @@ public class MessageView extends K9Activity implements OnClickListener onPrevious(); break; case R.id.download: - onDownloadAttachment((AttachmentViewHolder) view.getTag()); - break; - case R.id.view: - onViewAttachment((AttachmentViewHolder) view.getTag()); + onDownloadAttachment((AttachmentView) view); break; case R.id.show_pictures: onShowPictures(); @@ -1799,25 +1709,6 @@ public class MessageView extends K9Activity implements OnClickListener } } - private Bitmap getPreviewIcon(AttachmentViewHolder attachment) - { - try - { - return BitmapFactory.decodeStream( - getContentResolver().openInputStream( - AttachmentProvider.getAttachmentThumbnailUri(mAccount, - attachment.part.getAttachmentId(), - 62, - 62))); - } - catch (Exception e) - { - /* - * We don't care what happened, we just return null for the preview icon. - */ - return null; - } - } private void renderAttachments(Part part, int depth) throws MessagingException { @@ -1829,7 +1720,7 @@ public class MessageView extends K9Activity implements OnClickListener renderAttachments(mp.getBodyPart(i), depth + 1); } } - else + else if (part instanceof LocalAttachmentBodyPart) { String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); // Inline parts with a content-id are almost certainly components of an HTML message @@ -1846,68 +1737,12 @@ public class MessageView extends K9Activity implements OnClickListener private void renderPartAsAttachment(Part part) throws MessagingException { - String contentType = MimeUtility.unfoldAndDecode(part.getContentType()); - String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); - String name = MimeUtility.getHeaderParameter(contentType, "name"); - if (name == null) - { - name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); - } - if (name == null) - { - return; - } - AttachmentViewHolder attachment = new AttachmentViewHolder(); - attachment.size = Integer.parseInt(MimeUtility.getHeaderParameter(contentDisposition, "size")); - attachment.contentType = part.getMimeType(); - if (MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE.equals(attachment.contentType)) - { - attachment.contentType = MimeUtility.getMimeTypeByExtension(name); - } - attachment.name = name; - attachment.part = (LocalAttachmentBodyPart) part; LayoutInflater inflater = getLayoutInflater(); - View view = inflater.inflate(R.layout.message_view_attachment, null); - TextView attachmentName = (TextView) view.findViewById(R.id.attachment_name); - TextView attachmentInfo = (TextView) view.findViewById(R.id.attachment_info); - ImageView attachmentIcon = (ImageView) view.findViewById(R.id.attachment_icon); - Button attachmentView = (Button) view.findViewById(R.id.view); - Button attachmentDownload = (Button) view.findViewById(R.id.download); - if ((!MimeUtility.mimeTypeMatches(attachment.contentType, K9.ACCEPTABLE_ATTACHMENT_VIEW_TYPES)) - || (MimeUtility.mimeTypeMatches(attachment.contentType, K9.UNACCEPTABLE_ATTACHMENT_VIEW_TYPES))) + AttachmentView view = (AttachmentView)inflater.inflate(R.layout.message_view_attachment, null); + if (view.populateFromPart(part, mMessage, mAccount, mController, mListener)) { - attachmentView.setVisibility(View.GONE); + mHandler.addAttachment((View)view); } - if ((!MimeUtility.mimeTypeMatches(attachment.contentType, K9.ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES)) - || (MimeUtility.mimeTypeMatches(attachment.contentType, K9.UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES))) - { - attachmentDownload.setVisibility(View.GONE); - } - if (attachment.size > K9.MAX_ATTACHMENT_DOWNLOAD_SIZE) - { - attachmentView.setVisibility(View.GONE); - attachmentDownload.setVisibility(View.GONE); - } - attachment.viewButton = attachmentView; - attachment.downloadButton = attachmentDownload; - attachment.iconView = attachmentIcon; - view.setTag(attachment); - attachmentView.setOnClickListener(this); - attachmentView.setTag(attachment); - attachmentDownload.setOnClickListener(this); - attachmentDownload.setTag(attachment); - attachmentName.setText(name); - attachmentInfo.setText(SizeFormatter.formatSize(getApplication(), attachment.size)); - Bitmap previewIcon = getPreviewIcon(attachment); - if (previewIcon != null) - { - attachmentIcon.setImageBitmap(previewIcon); - } - else - { - attachmentIcon.setImageResource(R.drawable.attached_image_placeholder); - } - mHandler.addAttachment(view); return; } @@ -1947,7 +1782,7 @@ public class MessageView extends K9Activity implements OnClickListener @Override public void loadMessageForViewBodyAvailable(Account account, String folder, String uid, - Message message) + Message message) { if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder) || !mMessageReference.accountUuid.equals(account.getUuid())) @@ -2136,48 +1971,15 @@ public class MessageView extends K9Activity implements OnClickListener mHandler.progress(false); Object[] params = (Object[]) tag; boolean download = (Boolean) params[0]; - AttachmentViewHolder attachment = (AttachmentViewHolder) params[1]; + AttachmentView attachment = (AttachmentView) params[1]; if (download) { - try - { - File file = createUniqueFile(Environment.getExternalStorageDirectory(), - attachment.name); - Uri uri = AttachmentProvider.getAttachmentUri( - mAccount, - attachment.part.getAttachmentId()); - InputStream in = getContentResolver().openInputStream(uri); - OutputStream out = new FileOutputStream(file); - IOUtils.copy(in, out); - out.flush(); - out.close(); - in.close(); - mHandler.attachmentSaved(file.getName()); - new MediaScannerNotifier(MessageView.this, file); - } - catch (IOException ioe) - { - mHandler.attachmentNotSaved(); - } + attachment.saveFile(); + } else { - Uri uri = AttachmentProvider.getAttachmentUri( - mAccount, - attachment.part.getAttachmentId()); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(uri); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - try - { - startActivity(intent); - } - catch (Exception e) - { - Log.e(K9.LOG_TAG, "Could not display attachment of type " + attachment.contentType, e); - Toast toast = Toast.makeText(MessageView.this, getString(R.string.message_view_no_viewer, attachment.contentType), Toast.LENGTH_LONG); - toast.show(); - } + attachment.showFile(); } } @@ -2195,40 +1997,6 @@ public class MessageView extends K9Activity implements OnClickListener } } - class MediaScannerNotifier implements MediaScannerConnectionClient - { - private MediaScannerConnection mConnection; - private File mFile; - - public MediaScannerNotifier(Context context, File file) - { - mFile = file; - mConnection = new MediaScannerConnection(context, this); - mConnection.connect(); - } - - public void onMediaScannerConnected() - { - mConnection.scanFile(mFile.getAbsolutePath(), null); - } - - public void onScanCompleted(String path, Uri uri) - { - try - { - if (uri != null) - { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(uri); - startActivity(intent); - } - } - finally - { - mConnection.disconnect(); - } - } - } private void initializeCrypto(PgpData data) { diff --git a/src/com/fsck/k9/helper/MediaScannerNotifier.java b/src/com/fsck/k9/helper/MediaScannerNotifier.java new file mode 100644 index 000000000..1a9c5436d --- /dev/null +++ b/src/com/fsck/k9/helper/MediaScannerNotifier.java @@ -0,0 +1,48 @@ +package com.fsck.k9.helper; + +import android.content.Context; +import android.content.Intent; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; +import android.net.Uri; + +import java.io.File; + + +public class MediaScannerNotifier implements MediaScannerConnectionClient +{ + private MediaScannerConnection mConnection; + private File mFile; + private Context mContext; + + public MediaScannerNotifier(Context context, File file) + { + mFile = file; + mConnection = new MediaScannerConnection(context, this); + mConnection.connect(); + mContext = context; + + } + + public void onMediaScannerConnected() + { + mConnection.scanFile(mFile.getAbsolutePath(), null); + } + + public void onScanCompleted(String path, Uri uri) + { + try + { + if (uri != null) + { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(uri); + mContext.startActivity(intent); + } + } + finally + { + mConnection.disconnect(); + } + } +} diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java new file mode 100644 index 000000000..d8cffed97 --- /dev/null +++ b/src/com/fsck/k9/view/AttachmentView.java @@ -0,0 +1,246 @@ +package com.fsck.k9.view; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Environment; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.*; +import com.fsck.k9.Account; +import com.fsck.k9.K9; +import com.fsck.k9.R; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.controller.MessagingListener; +import com.fsck.k9.helper.MediaScannerNotifier; +import com.fsck.k9.helper.SizeFormatter; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart; +import com.fsck.k9.provider.AttachmentProvider; +import org.apache.commons.io.IOUtils; + +import java.io.*; + +public class AttachmentView extends FrameLayout +{ + + private Context mContext; + public Button viewButton; + public Button downloadButton; + public LocalAttachmentBodyPart part; + private Message mMessage; + private Account mAccount; + private MessagingController mController; + private MessagingListener mListener; + public String name; + public String contentType; + public long size; + public ImageView iconView; + + public AttachmentView(Context context, AttributeSet attrs, int defStyle) + { + super(context, attrs, defStyle); + mContext = context; + } + public AttachmentView(Context context, AttributeSet attrs) + { + super(context, attrs); + mContext = context; + } + public AttachmentView(Context context) + { + super(context); + mContext = context; + } + + + + public boolean populateFromPart(Part inputPart, Message message, Account account, MessagingController controller, MessagingListener listener ) + { + try + { + part = (LocalAttachmentBodyPart) inputPart; + + contentType = MimeUtility.unfoldAndDecode(part.getContentType()); + String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); + + name = MimeUtility.getHeaderParameter(contentType, "name"); + if (name == null) + { + name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); + } + if (name == null) + { + return false; + } + + mAccount = account; + mMessage = message; + mController = controller; + mListener = listener; + + size = Integer.parseInt(MimeUtility.getHeaderParameter(contentDisposition, "size")); + contentType = part.getMimeType(); + if (MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE.equals(contentType)) + { + contentType = MimeUtility.getMimeTypeByExtension(name); + } + TextView attachmentName = (TextView) findViewById(R.id.attachment_name); + TextView attachmentInfo = (TextView) findViewById(R.id.attachment_info); + ImageView attachmentIcon = (ImageView) findViewById(R.id.attachment_icon); + viewButton = (Button) findViewById(R.id.view); + downloadButton = (Button) findViewById(R.id.download); + if ((!MimeUtility.mimeTypeMatches(contentType, K9.ACCEPTABLE_ATTACHMENT_VIEW_TYPES)) + || (MimeUtility.mimeTypeMatches(contentType, K9.UNACCEPTABLE_ATTACHMENT_VIEW_TYPES))) + { + viewButton.setVisibility(View.GONE); + } + if ((!MimeUtility.mimeTypeMatches(contentType, K9.ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES)) + || (MimeUtility.mimeTypeMatches(contentType, K9.UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES))) + { + downloadButton.setVisibility(View.GONE); + } + if (size > K9.MAX_ATTACHMENT_DOWNLOAD_SIZE) + { + viewButton.setVisibility(View.GONE); + downloadButton.setVisibility(View.GONE); + } + + viewButton.setOnClickListener(new OnClickListener() + { + @Override + public void onClick(View v) + { + onViewButtonClicked(); + return; + } + }); + + + downloadButton.setOnClickListener(new OnClickListener() + { + @Override + public void onClick(View v) + { + onSaveButtonClicked(); + return; + } + }); + + attachmentName.setText(name); + attachmentInfo.setText(SizeFormatter.formatSize(mContext, size)); + Bitmap previewIcon = getPreviewIcon(); + if (previewIcon != null) + { + attachmentIcon.setImageBitmap(previewIcon); + } + else + { + attachmentIcon.setImageResource(R.drawable.attached_image_placeholder); + } + } + + catch (Exception e) + { + Log.e(K9.LOG_TAG, "error ",e); + } + + return true; + } + + private Bitmap getPreviewIcon() + { + try + { + return BitmapFactory.decodeStream( + mContext.getContentResolver().openInputStream( + AttachmentProvider.getAttachmentThumbnailUri(mAccount, + part.getAttachmentId(), + 62, + 62))); + } + catch (Exception e) + { + /* + * We don't care what happened, we just return null for the preview icon. + */ + return null; + } + } + + private void onViewButtonClicked() + { + if (mMessage != null) + { + mController.loadAttachment( mAccount, mMessage, part, new Object[] { false, this }, mListener); + } + } + + + private void onSaveButtonClicked() + { + if (mMessage != null) + { + mController.loadAttachment( mAccount, mMessage, part, new Object[] { true, this }, mListener); + } + } + + public void saveFile () + { + try + { + File file = Utility.createUniqueFile(Environment.getExternalStorageDirectory(), name); + Uri uri = AttachmentProvider.getAttachmentUri( mAccount, part.getAttachmentId()); + InputStream in = mContext.getContentResolver().openInputStream(uri); + OutputStream out = new FileOutputStream(file); + IOUtils.copy(in, out); + out.flush(); + out.close(); + in.close(); + attachmentSaved(file.getName()); + new MediaScannerNotifier(mContext, file); + } + catch (IOException ioe) + { + attachmentNotSaved(); + } + } + + public void showFile() + { + Uri uri = AttachmentProvider.getAttachmentUri( mAccount, part.getAttachmentId()); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(uri); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + try + { + mContext.startActivity(intent); + } + catch (Exception e) + { + Log.e(K9.LOG_TAG, "Could not display attachment of type " + contentType, e); + Toast toast = Toast.makeText(mContext, mContext.getString(R.string.message_view_no_viewer, contentType), Toast.LENGTH_LONG); + toast.show(); + } + } + + public void attachmentSaved(final String filename) + { + Toast.makeText(mContext, String.format( + mContext.getString(R.string.message_view_status_attachment_saved), filename), + Toast.LENGTH_LONG).show(); + } + + public void attachmentNotSaved() + { + Toast.makeText(mContext, + mContext.getString(R.string.message_view_status_attachment_not_saved), + Toast.LENGTH_LONG).show(); + } +}