From b7a8c9b70758f9e2f1c2c9a894455d8250057878 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 19:30:20 -0400
Subject: [PATCH 01/27] Remove unused code
---
src/com/fsck/k9/view/AttachmentView.java | 37 +-----------------------
1 file changed, 1 insertion(+), 36 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 9c5de50a0..ea748218f 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -10,7 +10,6 @@ import org.apache.commons.io.IOUtils;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
@@ -55,7 +54,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public String name;
public String contentType;
public long size;
- public ImageView iconView;
private AttachmentFileDownloadCallback callback;
@@ -310,37 +308,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
- /**
- * Check the {@link PackageManager} if the phone has an application
- * installed to view this type of attachment.
- * If not, {@link #viewButton} is disabled.
- * This should be done in any place where
- * attachment.viewButton.setEnabled(enabled); is called.
- * This method is safe to be called from the UI-thread.
- */
- public void checkViewable() {
- if (viewButton.getVisibility() == View.GONE) {
- // nothing to do
- return;
- }
- if (!viewButton.isEnabled()) {
- // nothing to do
- return;
- }
- try {
- Uri uri = AttachmentProvider.getAttachmentUriForViewing(mAccount, part.getAttachmentId());
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(uri);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- if (intent.resolveActivity(mContext.getPackageManager()) == null) {
- viewButton.setEnabled(false);
- }
- // currently we do not cache re result.
- } catch (Exception e) {
- Log.e(K9.LOG_TAG, "Cannot resolve activity to determine if we shall show the 'view'-button for an attachment", e);
- }
- }
-
public void attachmentSaved(final String filename) {
Toast.makeText(mContext, String.format(
mContext.getString(R.string.message_view_status_attachment_saved), filename),
@@ -352,9 +319,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
mContext.getString(R.string.message_view_status_attachment_not_saved),
Toast.LENGTH_LONG).show();
}
- public AttachmentFileDownloadCallback getCallback() {
- return callback;
- }
+
public void setCallback(AttachmentFileDownloadCallback callback) {
this.callback = callback;
}
From dfe1771fcbcafd0e0583ee989d8509ffc55ac9e6 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 19:39:16 -0400
Subject: [PATCH 02/27] Fix formatting
---
src/com/fsck/k9/view/AttachmentView.java | 38 ++++++++++++++----------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index ea748218f..9c16777fc 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -1,5 +1,6 @@
package com.fsck.k9.view;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -42,6 +43,7 @@ import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart;
import com.fsck.k9.provider.AttachmentProvider;
+
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
private Context mContext;
public Button viewButton;
@@ -61,10 +63,12 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
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;
@@ -79,6 +83,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
* and save the reference to the attachment view for later.
* in his onActivityResult he can get the saved reference and
* call the saveFile method of AttachmentView
+ *
* @param view
*/
public void showFileBrowser(AttachmentView caller);
@@ -86,7 +91,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
/**
* Populates this view with information about the attachment.
- *
*
* This method also decides which attachments are displayed when the "show attachments" button
* is pressed, and which attachments are only displayed after the "show more attachments"
@@ -103,7 +107,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
* @return {@code true} for a regular attachment. {@code false}, otherwise.
*
* @throws MessagingException
- * In case of an error
+ * In case of an error
*/
public boolean populateFromPart(Part inputPart, Message message, Account account,
MessagingController controller, MessagingListener listener) throws MessagingException {
@@ -216,10 +220,10 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
Bitmap icon = null;
try {
InputStream input = mContext.getContentResolver().openInputStream(
- AttachmentProvider.getAttachmentThumbnailUri(mAccount,
- part.getAttachmentId(),
- 62,
- 62));
+ AttachmentProvider.getAttachmentThumbnailUri(mAccount,
+ part.getAttachmentId(),
+ 62,
+ 62));
icon = BitmapFactory.decodeStream(input);
input.close();
} catch (Exception e) {
@@ -232,7 +236,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
private void onViewButtonClicked() {
if (mMessage != null) {
- mController.loadAttachment(mAccount, mMessage, part, new Object[] { false, this }, mListener);
+ mController.loadAttachment(mAccount, mMessage, part, new Object[] {false, this}, mListener);
}
}
@@ -243,7 +247,9 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
/**
* Writes the attachment onto the given path
- * @param directory: the base dir where the file should be saved.
+ *
+ * @param directory
+ * the base dir where the file should be saved.
*/
public void writeFile(File directory) {
try {
@@ -282,8 +288,8 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
* the time downloading it and then abort.
*/
Toast.makeText(mContext,
- mContext.getString(R.string.message_view_status_attachment_not_saved),
- Toast.LENGTH_SHORT).show();
+ mContext.getString(R.string.message_view_status_attachment_not_saved),
+ Toast.LENGTH_SHORT).show();
return;
}
if (mMessage != null) {
@@ -303,25 +309,25 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
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 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();
+ 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();
+ mContext.getString(R.string.message_view_status_attachment_not_saved),
+ Toast.LENGTH_LONG).show();
}
public void setCallback(AttachmentFileDownloadCallback callback) {
this.callback = callback;
}
-
}
From 187d760e5f48b42250f2f994ca7989631f1721e0 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 19:50:41 -0400
Subject: [PATCH 03/27] Extract code to display error/status messages to a
separate method
---
src/com/fsck/k9/view/AttachmentView.java | 25 ++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 9c16777fc..c7dc3841e 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -287,9 +287,8 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
* Abort early if there's no place to save the attachment. We don't want to spend
* the time downloading it and then abort.
*/
- Toast.makeText(mContext,
- mContext.getString(R.string.message_view_status_attachment_not_saved),
- Toast.LENGTH_SHORT).show();
+ String message = mContext.getString(R.string.message_view_status_attachment_not_saved);
+ displayMessageToUser(message);
return;
}
if (mMessage != null) {
@@ -309,22 +308,24 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
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();
+
+ String message = mContext.getString(R.string.message_view_no_viewer, contentType);
+ displayMessageToUser(message);
}
}
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();
+ String message = mContext.getString(R.string.message_view_status_attachment_saved, filename);
+ displayMessageToUser(message);
}
public void attachmentNotSaved() {
- Toast.makeText(mContext,
- mContext.getString(R.string.message_view_status_attachment_not_saved),
- Toast.LENGTH_LONG).show();
+ String message = mContext.getString(R.string.message_view_status_attachment_not_saved);
+ displayMessageToUser(message);
+ }
+
+ private void displayMessageToUser(String message) {
+ Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
}
public void setCallback(AttachmentFileDownloadCallback callback) {
From 33d12e416900acfb35b7870e275ff45aaeced02f Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 19:52:42 -0400
Subject: [PATCH 04/27] Fix russian translation of 'message_view_no_viewer'
---
res/values-ru/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 0760d9ce1..9e3963f91 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -229,7 +229,7 @@ K-9 Mail — почтовый клиент для Android.
Вложения
Показать вложения
Извлечение вложений
- Отсутствует просмотрщик %s %s
+ Отсутствует просмотрщик %s
Загрузить полностью
Загрузка…
From 0ced8746af6f2f0b61a489d2fe56a1cb0e6f2d54 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 20:04:55 -0400
Subject: [PATCH 05/27] Remove old/unused code from click handler
---
src/com/fsck/k9/fragment/MessageViewFragment.java | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java
index 5f267bb64..c267797b1 100644
--- a/src/com/fsck/k9/fragment/MessageViewFragment.java
+++ b/src/com/fsck/k9/fragment/MessageViewFragment.java
@@ -503,15 +503,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
@Override
public void onClick(View view) {
- switch (view.getId()) {
- case R.id.download: {
- ((AttachmentView)view).saveFile();
- break;
- }
- case R.id.download_remainder: {
- onDownloadRemainder();
- break;
- }
+ if (view.getId() == R.id.download_remainder) {
+ onDownloadRemainder();
}
}
From 4a6c52947d22315028d926ab621aa4e329d85fc6 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 20:14:04 -0400
Subject: [PATCH 06/27] Make all fields of AttachmentView private
---
src/com/fsck/k9/view/AttachmentView.java | 17 +++++++++++------
src/com/fsck/k9/view/SingleMessageView.java | 3 +--
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index c7dc3841e..27ab0e657 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -46,16 +46,16 @@ import com.fsck.k9.provider.AttachmentProvider;
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
private Context mContext;
- public Button viewButton;
- public Button downloadButton;
- public LocalAttachmentBodyPart part;
+ private Button viewButton;
+ private Button downloadButton;
+ private LocalAttachmentBodyPart part;
private Message mMessage;
private Account mAccount;
private MessagingController mController;
private MessagingListener mListener;
- public String name;
- public String contentType;
- public long size;
+ private String name;
+ private String contentType;
+ private long size;
private AttachmentFileDownloadCallback callback;
@@ -89,6 +89,11 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public void showFileBrowser(AttachmentView caller);
}
+ public void setButtonsEnabled(boolean enabled) {
+ viewButton.setEnabled(enabled);
+ downloadButton.setEnabled(enabled);
+ }
+
/**
* Populates this view with information about the attachment.
*
diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java
index 58a5821cf..2921ed494 100644
--- a/src/com/fsck/k9/view/SingleMessageView.java
+++ b/src/com/fsck/k9/view/SingleMessageView.java
@@ -607,8 +607,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
public void setAttachmentsEnabled(boolean enabled) {
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
AttachmentView attachment = (AttachmentView) mAttachments.getChildAt(i);
- attachment.viewButton.setEnabled(enabled);
- attachment.downloadButton.setEnabled(enabled);
+ attachment.setButtonsEnabled(enabled);
}
}
From 4299eb9771d63b53a94a7829f13c7c3855f243b5 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 20:28:38 -0400
Subject: [PATCH 07/27] Rename method and improve documentation
---
.../fsck/k9/fragment/MessageViewFragment.java | 2 +-
src/com/fsck/k9/view/AttachmentView.java | 19 +++++++------------
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java
index c267797b1..b66ebc618 100644
--- a/src/com/fsck/k9/fragment/MessageViewFragment.java
+++ b/src/com/fsck/k9/fragment/MessageViewFragment.java
@@ -207,7 +207,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
mMessageView.setAttachmentCallback(new AttachmentFileDownloadCallback() {
@Override
- public void showFileBrowser(final AttachmentView caller) {
+ public void pickDirectoryToSaveAttachmentTo(final AttachmentView caller) {
FileBrowserHelper.getInstance()
.showFileBrowserActivity(MessageViewFragment.this,
null,
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 27ab0e657..9a9fbf05e 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -7,8 +7,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import org.apache.commons.io.IOUtils;
-
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -42,6 +40,7 @@ import com.fsck.k9.mail.internet.MimeHeader;
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;
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
@@ -77,16 +76,12 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public interface AttachmentFileDownloadCallback {
/**
- * this method i called by the attachmentview when
- * he wants to show a filebrowser
- * the provider should show the filebrowser activity
- * and save the reference to the attachment view for later.
- * in his onActivityResult he can get the saved reference and
- * call the saveFile method of AttachmentView
- *
- * @param view
+ * This method is called to ask the user to pick a directory to save the attachment to.
+ *
+ * After the user has selected a directory, the implementation of this interface has to call
+ * {@link #writeFile(File)} on the object supplied as argument in order for the attachment to be saved.
*/
- public void showFileBrowser(AttachmentView caller);
+ public void pickDirectoryToSaveAttachmentTo(AttachmentView caller);
}
public void setButtonsEnabled(boolean enabled) {
@@ -214,7 +209,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
@Override
public boolean onLongClick(View view) {
if (view.getId() == R.id.download) {
- callback.showFileBrowser(this);
+ callback.pickDirectoryToSaveAttachmentTo(this);
return true;
}
From ca88f59c0550f65182674c9bb86b75e39fd2eafb Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 20:46:46 -0400
Subject: [PATCH 08/27] Create named class for loading and displaying
attachment thumbnails
---
src/com/fsck/k9/view/AttachmentView.java | 71 +++++++++++++-----------
1 file changed, 39 insertions(+), 32 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 9a9fbf05e..144ecf40f 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -152,7 +152,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
contentType = MimeUtility.getMimeTypeForViewing(part.getMimeType(), name);
TextView attachmentName = (TextView) findViewById(R.id.attachment_name);
TextView attachmentInfo = (TextView) findViewById(R.id.attachment_info);
- final 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))
@@ -174,20 +173,9 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
attachmentName.setText(name);
attachmentInfo.setText(SizeFormatter.formatSize(mContext, size));
- new AsyncTask() {
- protected Bitmap doInBackground(Void... asyncTaskArgs) {
- Bitmap previewIcon = getPreviewIcon();
- return previewIcon;
- }
- protected void onPostExecute(Bitmap previewIcon) {
- if (previewIcon != null) {
- attachmentIcon.setImageBitmap(previewIcon);
- } else {
- attachmentIcon.setImageResource(R.drawable.attached_image_placeholder);
- }
- }
- }.execute();
+ ImageView thumbnail = (ImageView) findViewById(R.id.attachment_icon);
+ new LoadAndDisplayThumbnailAsyncTask(thumbnail).execute();
return firstClassAttachment;
}
@@ -216,24 +204,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
return false;
}
- private Bitmap getPreviewIcon() {
- Bitmap icon = null;
- try {
- InputStream input = mContext.getContentResolver().openInputStream(
- AttachmentProvider.getAttachmentThumbnailUri(mAccount,
- part.getAttachmentId(),
- 62,
- 62));
- icon = BitmapFactory.decodeStream(input);
- input.close();
- } catch (Exception e) {
- /*
- * We don't care what happened, we just return null for the preview icon.
- */
- }
- return icon;
- }
-
private void onViewButtonClicked() {
if (mMessage != null) {
mController.loadAttachment(mAccount, mMessage, part, new Object[] {false, this}, mListener);
@@ -331,4 +301,41 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public void setCallback(AttachmentFileDownloadCallback callback) {
this.callback = callback;
}
+
+ private class LoadAndDisplayThumbnailAsyncTask extends AsyncTask {
+ private final ImageView thumbnail;
+
+ public LoadAndDisplayThumbnailAsyncTask(ImageView thumbnail) {
+ this.thumbnail = thumbnail;
+ }
+
+ protected Bitmap doInBackground(Void... asyncTaskArgs) {
+ return getPreviewIcon();
+ }
+
+ private Bitmap getPreviewIcon() {
+ Bitmap icon = null;
+ try {
+ InputStream input = mContext.getContentResolver().openInputStream(
+ AttachmentProvider.getAttachmentThumbnailUri(mAccount,
+ part.getAttachmentId(),
+ 62,
+ 62));
+ icon = BitmapFactory.decodeStream(input);
+ input.close();
+ } catch (Exception e) {
+ // We don't care what happened, we just return null for the preview icon.
+ }
+
+ return icon;
+ }
+
+ protected void onPostExecute(Bitmap previewIcon) {
+ if (previewIcon != null) {
+ thumbnail.setImageBitmap(previewIcon);
+ } else {
+ thumbnail.setImageResource(R.drawable.attached_image_placeholder);
+ }
+ }
+ }
}
From b3bc85ba109c8a482cb2660d7e7a0bfb8d9b46c9 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:06:31 -0400
Subject: [PATCH 09/27] Split 'populateFromPart' into multiple methods
---
src/com/fsck/k9/view/AttachmentView.java | 29 +++++++++++++++++-------
1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 144ecf40f..7fa29bb07 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -111,8 +111,23 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
*/
public boolean populateFromPart(Part inputPart, Message message, Account account,
MessagingController controller, MessagingListener listener) throws MessagingException {
- boolean firstClassAttachment = true;
+
part = (LocalAttachmentBodyPart) inputPart;
+ mMessage = message;
+ mAccount = account;
+ mController = controller;
+ mListener = listener;
+
+ boolean firstClassAttachment = extractAttachmentInformation(part);
+
+ displayAttachmentInformation();
+
+ return firstClassAttachment;
+ }
+
+ //TODO: extract this code to a helper class
+ private boolean extractAttachmentInformation(Part part) throws MessagingException {
+ boolean firstClassAttachment = true;
contentType = MimeUtility.unfoldAndDecode(part.getContentType());
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
@@ -137,11 +152,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
firstClassAttachment = false;
}
- mAccount = account;
- mMessage = message;
- mController = controller;
- mListener = listener;
-
String sizeParam = MimeUtility.getHeaderParameter(contentDisposition, "size");
if (sizeParam != null) {
try {
@@ -150,10 +160,15 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
contentType = MimeUtility.getMimeTypeForViewing(part.getMimeType(), name);
+ return firstClassAttachment;
+ }
+
+ private void displayAttachmentInformation() {
TextView attachmentName = (TextView) findViewById(R.id.attachment_name);
TextView attachmentInfo = (TextView) findViewById(R.id.attachment_info);
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);
@@ -176,8 +191,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
ImageView thumbnail = (ImageView) findViewById(R.id.attachment_icon);
new LoadAndDisplayThumbnailAsyncTask(thumbnail).execute();
-
- return firstClassAttachment;
}
@Override
From 1e89314f3e1e5758cf2cf3022943b3f4d45fb328 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:09:23 -0400
Subject: [PATCH 10/27] Remove "m" prefix for field names
---
src/com/fsck/k9/view/AttachmentView.java | 58 ++++++++++++------------
1 file changed, 29 insertions(+), 29 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 7fa29bb07..0dbec5bab 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -44,14 +44,14 @@ import org.apache.commons.io.IOUtils;
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
- private Context mContext;
+ private Context context;
private Button viewButton;
private Button downloadButton;
private LocalAttachmentBodyPart part;
- private Message mMessage;
- private Account mAccount;
- private MessagingController mController;
- private MessagingListener mListener;
+ private Message message;
+ private Account account;
+ private MessagingController controller;
+ private MessagingListener listener;
private String name;
private String contentType;
private long size;
@@ -60,17 +60,17 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public AttachmentView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mContext = context;
+ this.context = context;
}
public AttachmentView(Context context, AttributeSet attrs) {
super(context, attrs);
- mContext = context;
+ this.context = context;
}
public AttachmentView(Context context) {
super(context);
- mContext = context;
+ this.context = context;
}
@@ -113,10 +113,10 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
MessagingController controller, MessagingListener listener) throws MessagingException {
part = (LocalAttachmentBodyPart) inputPart;
- mMessage = message;
- mAccount = account;
- mController = controller;
- mListener = listener;
+ this.message = message;
+ this.account = account;
+ this.controller = controller;
+ this.listener = listener;
boolean firstClassAttachment = extractAttachmentInformation(part);
@@ -187,7 +187,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
downloadButton.setOnLongClickListener(this);
attachmentName.setText(name);
- attachmentInfo.setText(SizeFormatter.formatSize(mContext, size));
+ attachmentInfo.setText(SizeFormatter.formatSize(context, size));
ImageView thumbnail = (ImageView) findViewById(R.id.attachment_icon);
new LoadAndDisplayThumbnailAsyncTask(thumbnail).execute();
@@ -218,8 +218,8 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
private void onViewButtonClicked() {
- if (mMessage != null) {
- mController.loadAttachment(mAccount, mMessage, part, new Object[] {false, this}, mListener);
+ if (message != null) {
+ controller.loadAttachment(account, message, part, new Object[] {false, this}, listener);
}
}
@@ -238,15 +238,15 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
try {
String filename = Utility.sanitizeFilename(name);
File file = Utility.createUniqueFile(directory, filename);
- Uri uri = AttachmentProvider.getAttachmentUri(mAccount, part.getAttachmentId());
- InputStream in = mContext.getContentResolver().openInputStream(uri);
+ Uri uri = AttachmentProvider.getAttachmentUri(account, part.getAttachmentId());
+ InputStream in = context.getContentResolver().openInputStream(uri);
OutputStream out = new FileOutputStream(file);
IOUtils.copy(in, out);
out.flush();
out.close();
in.close();
attachmentSaved(file.toString());
- new MediaScannerNotifier(mContext, file);
+ new MediaScannerNotifier(context, file);
} catch (IOException ioe) {
if (K9.DEBUG) {
Log.e(K9.LOG_TAG, "Error saving attachment", ioe);
@@ -270,45 +270,45 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
* Abort early if there's no place to save the attachment. We don't want to spend
* the time downloading it and then abort.
*/
- String message = mContext.getString(R.string.message_view_status_attachment_not_saved);
+ String message = context.getString(R.string.message_view_status_attachment_not_saved);
displayMessageToUser(message);
return;
}
- if (mMessage != null) {
- mController.loadAttachment(mAccount, mMessage, part, new Object[] {true, this}, mListener);
+ if (message != null) {
+ controller.loadAttachment(account, message, part, new Object[] {true, this}, listener);
}
}
public void showFile() {
- Uri uri = AttachmentProvider.getAttachmentUriForViewing(mAccount, part.getAttachmentId());
+ Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
Intent intent = new Intent(Intent.ACTION_VIEW);
// We explicitly set the ContentType in addition to the URI because some attachment viewers (such as Polaris office 3.0.x) choke on documents without a mime type
intent.setDataAndType(uri, contentType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
try {
- mContext.startActivity(intent);
+ context.startActivity(intent);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Could not display attachment of type " + contentType, e);
- String message = mContext.getString(R.string.message_view_no_viewer, contentType);
+ String message = context.getString(R.string.message_view_no_viewer, contentType);
displayMessageToUser(message);
}
}
public void attachmentSaved(final String filename) {
- String message = mContext.getString(R.string.message_view_status_attachment_saved, filename);
+ String message = context.getString(R.string.message_view_status_attachment_saved, filename);
displayMessageToUser(message);
}
public void attachmentNotSaved() {
- String message = mContext.getString(R.string.message_view_status_attachment_not_saved);
+ String message = context.getString(R.string.message_view_status_attachment_not_saved);
displayMessageToUser(message);
}
private void displayMessageToUser(String message) {
- Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+ Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
public void setCallback(AttachmentFileDownloadCallback callback) {
@@ -329,8 +329,8 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
private Bitmap getPreviewIcon() {
Bitmap icon = null;
try {
- InputStream input = mContext.getContentResolver().openInputStream(
- AttachmentProvider.getAttachmentThumbnailUri(mAccount,
+ InputStream input = context.getContentResolver().openInputStream(
+ AttachmentProvider.getAttachmentThumbnailUri(account,
part.getAttachmentId(),
62,
62));
From f6822c973dd67656fe4be665a202b89238535041 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:13:58 -0400
Subject: [PATCH 11/27] Clean up comments
---
src/com/fsck/k9/view/AttachmentView.java | 23 ++++-------------------
1 file changed, 4 insertions(+), 19 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 0dbec5bab..55a874544 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -98,16 +98,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
* Inline attachments with content ID and unnamed attachments fall into the second category.
*
*
- * @param inputPart
- * @param message
- * @param account
- * @param controller
- * @param listener
- *
- * @return {@code true} for a regular attachment. {@code false}, otherwise.
- *
- * @throws MessagingException
- * In case of an error
+ * @return {@code true} for a regular attachment. {@code false} for attachments that should be initially hidden.
*/
public boolean populateFromPart(Part inputPart, Message message, Account account,
MessagingController controller, MessagingListener listener) throws MessagingException {
@@ -229,10 +220,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
/**
- * Writes the attachment onto the given path
- *
- * @param directory
- * the base dir where the file should be saved.
+ * Saves the attachment as file in the given directory
*/
public void writeFile(File directory) {
try {
@@ -255,10 +243,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
- /**
- * saves the file to the defaultpath setting in the config, or if the config
- * is not set => to the Environment
- */
public void writeFile() {
writeFile(new File(K9.getAttachmentDefaultPath()));
}
@@ -283,7 +267,8 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public void showFile() {
Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
Intent intent = new Intent(Intent.ACTION_VIEW);
- // We explicitly set the ContentType in addition to the URI because some attachment viewers (such as Polaris office 3.0.x) choke on documents without a mime type
+ // We explicitly set the content type in addition to the URI because some attachment viewers
+ // (such as Polaris office 3.0.x) choke on documents without a MIME type.
intent.setDataAndType(uri, contentType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
From 83d876f246734a2880ca3544c1cf2093a358993b Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:16:31 -0400
Subject: [PATCH 12/27] Inline method
---
src/com/fsck/k9/view/AttachmentView.java | 28 +++++++++---------------
1 file changed, 10 insertions(+), 18 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 55a874544..289ea4318 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -216,7 +216,16 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
private void onSaveButtonClicked() {
- saveFile();
+ boolean isExternalStorageMounted = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+ if (!isExternalStorageMounted) {
+ String message = context.getString(R.string.message_view_status_attachment_not_saved);
+ displayMessageToUser(message);
+ return;
+ }
+
+ if (message != null) {
+ controller.loadAttachment(account, message, part, new Object[] {true, this}, listener);
+ }
}
/**
@@ -247,23 +256,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
writeFile(new File(K9.getAttachmentDefaultPath()));
}
- public void saveFile() {
- //TODO: Can the user save attachments on the internal filesystem or sd card only?
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- /*
- * Abort early if there's no place to save the attachment. We don't want to spend
- * the time downloading it and then abort.
- */
- String message = context.getString(R.string.message_view_status_attachment_not_saved);
- displayMessageToUser(message);
- return;
- }
- if (message != null) {
- controller.loadAttachment(account, message, part, new Object[] {true, this}, listener);
- }
- }
-
-
public void showFile() {
Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
Intent intent = new Intent(Intent.ACTION_VIEW);
From d3073be89a794a6d6889c3d9e04d5086d2d019ce Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:21:09 -0400
Subject: [PATCH 13/27] Rearrange fields/methods
---
src/com/fsck/k9/view/AttachmentView.java | 42 +++++++++++++-----------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 289ea4318..1fc9bec71 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -45,18 +45,20 @@ import org.apache.commons.io.IOUtils;
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
private Context context;
- private Button viewButton;
- private Button downloadButton;
- private LocalAttachmentBodyPart part;
private Message message;
+ private LocalAttachmentBodyPart part;
private Account account;
private MessagingController controller;
private MessagingListener listener;
+ private AttachmentFileDownloadCallback callback;
+
+ private Button viewButton;
+ private Button downloadButton;
+
private String name;
private String contentType;
private long size;
- private AttachmentFileDownloadCallback callback;
public AttachmentView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@@ -73,17 +75,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
this.context = context;
}
-
- public interface AttachmentFileDownloadCallback {
- /**
- * This method is called to ask the user to pick a directory to save the attachment to.
- *
- * After the user has selected a directory, the implementation of this interface has to call
- * {@link #writeFile(File)} on the object supplied as argument in order for the attachment to be saved.
- */
- public void pickDirectoryToSaveAttachmentTo(AttachmentView caller);
- }
-
public void setButtonsEnabled(boolean enabled) {
viewButton.setEnabled(enabled);
downloadButton.setEnabled(enabled);
@@ -214,7 +205,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
-
private void onSaveButtonClicked() {
boolean isExternalStorageMounted = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if (!isExternalStorageMounted) {
@@ -228,6 +218,10 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
+ public void writeFile() {
+ writeFile(new File(K9.getAttachmentDefaultPath()));
+ }
+
/**
* Saves the attachment as file in the given directory
*/
@@ -252,10 +246,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
- public void writeFile() {
- writeFile(new File(K9.getAttachmentDefaultPath()));
- }
-
public void showFile() {
Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
Intent intent = new Intent(Intent.ACTION_VIEW);
@@ -292,6 +282,18 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
this.callback = callback;
}
+
+ public interface AttachmentFileDownloadCallback {
+ /**
+ * This method is called to ask the user to pick a directory to save the attachment to.
+ *
+ * After the user has selected a directory, the implementation of this interface has to call
+ * {@link #writeFile(File)} on the object supplied as argument in order for the attachment to be saved.
+ */
+ public void pickDirectoryToSaveAttachmentTo(AttachmentView caller);
+ }
+
+
private class LoadAndDisplayThumbnailAsyncTask extends AsyncTask {
private final ImageView thumbnail;
From 3c4ad9161471be7d60872496d2dc03035ee3a99e Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:24:18 -0400
Subject: [PATCH 14/27] Fix method names/visibility
---
src/com/fsck/k9/view/AttachmentView.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 1fc9bec71..c4558d3b1 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -236,13 +236,13 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
out.flush();
out.close();
in.close();
- attachmentSaved(file.toString());
+ displayAttachmentSavedMessage(file.toString());
new MediaScannerNotifier(context, file);
} catch (IOException ioe) {
if (K9.DEBUG) {
Log.e(K9.LOG_TAG, "Error saving attachment", ioe);
}
- attachmentNotSaved();
+ displayAttachmentNotSavedMessage();
}
}
@@ -264,12 +264,12 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
- public void attachmentSaved(final String filename) {
+ private void displayAttachmentSavedMessage(final String filename) {
String message = context.getString(R.string.message_view_status_attachment_saved, filename);
displayMessageToUser(message);
}
- public void attachmentNotSaved() {
+ private void displayAttachmentNotSavedMessage() {
String message = context.getString(R.string.message_view_status_attachment_not_saved);
displayMessageToUser(message);
}
From 6cb3c991dbee74656d80bcd554e2d03896308e4e Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 20 Oct 2014 21:26:18 -0400
Subject: [PATCH 15/27] No longer pretend there are attachment types we don't
care about
---
src/com/fsck/k9/K9.java | 27 ------------------------
src/com/fsck/k9/view/AttachmentView.java | 8 -------
2 files changed, 35 deletions(-)
diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java
index 593bc07a9..39051565a 100644
--- a/src/com/fsck/k9/K9.java
+++ b/src/com/fsck/k9/K9.java
@@ -282,33 +282,6 @@ public class K9 extends Application {
*/
private static boolean sDatabasesUpToDate = false;
-
- /**
- * The MIME type(s) of attachments we're willing to view.
- */
- public static final String[] ACCEPTABLE_ATTACHMENT_VIEW_TYPES = new String[] {
- "*/*",
- };
-
- /**
- * The MIME type(s) of attachments we're not willing to view.
- */
- public static final String[] UNACCEPTABLE_ATTACHMENT_VIEW_TYPES = new String[] {
- };
-
- /**
- * The MIME type(s) of attachments we're willing to download to SD.
- */
- public static final String[] ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
- "*/*",
- };
-
- /**
- * The MIME type(s) of attachments we're not willing to download to SD.
- */
- public static final String[] UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
- };
-
/**
* For use when displaying that no folder is selected
*/
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index c4558d3b1..dc20de7ec 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -151,14 +151,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
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);
From 11a9eff109495ea20977898a1c7669c57185098f Mon Sep 17 00:00:00 2001
From: cketti
Date: Sun, 9 Nov 2014 20:46:46 +0100
Subject: [PATCH 16/27] Extract intent creation code to method
---
src/com/fsck/k9/view/AttachmentView.java | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index dc20de7ec..35740beeb 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -240,11 +240,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
public void showFile() {
Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
- Intent intent = new Intent(Intent.ACTION_VIEW);
- // We explicitly set the content type in addition to the URI because some attachment viewers
- // (such as Polaris office 3.0.x) choke on documents without a MIME type.
- intent.setDataAndType(uri, contentType);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ Intent intent = createViewIntentForContentUri(contentType, uri);
try {
context.startActivity(intent);
@@ -256,6 +252,14 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
+ private Intent createViewIntentForContentUri(String mimeType, Uri uri) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(uri, mimeType);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+
+ return intent;
+ }
+
private void displayAttachmentSavedMessage(final String filename) {
String message = context.getString(R.string.message_view_status_attachment_saved, filename);
displayMessageToUser(message);
From f87ab53b9bdbf0a712c74ec235d63029a51f9503 Mon Sep 17 00:00:00 2001
From: cketti
Date: Sun, 9 Nov 2014 21:57:16 +0100
Subject: [PATCH 17/27] Try original and inferred MIME type to find best viewer
for attachment
In order for Android to find apps that are capable of opening an attachment for
viewing the ACTION_VIEW Intent needs to contain an appropriate MIME type.
Ideally, we'd use the MIME type specified for the attachment in the message.
But often the supplied MIME type is wrong/useless. So we look at the file
extension to try to come up with a sensible MIME type on our own. We then go
on to ask Android which of the two MIME types leads to more apps claiming to
be able to open our attachment for viewing and use that one.
---
src/com/fsck/k9/view/AttachmentView.java | 49 ++++++++++++++++++++----
1 file changed, 42 insertions(+), 7 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 35740beeb..bf1a918b6 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -6,9 +6,13 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.List;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
@@ -111,10 +115,11 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
private boolean extractAttachmentInformation(Part part) throws MessagingException {
boolean firstClassAttachment = true;
- contentType = MimeUtility.unfoldAndDecode(part.getContentType());
+ contentType = part.getMimeType();
+ String contentTypeHeader = MimeUtility.unfoldAndDecode(part.getContentType());
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
- name = MimeUtility.getHeaderParameter(contentType, "name");
+ name = MimeUtility.getHeaderParameter(contentTypeHeader, "name");
if (name == null) {
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
}
@@ -141,7 +146,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
} catch (NumberFormatException e) { /* ignore */ }
}
- contentType = MimeUtility.getMimeTypeForViewing(part.getMimeType(), name);
return firstClassAttachment;
}
@@ -239,12 +243,10 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
public void showFile() {
- Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
- Intent intent = createViewIntentForContentUri(contentType, uri);
-
+ Intent intent = constructViewIntent();
try {
context.startActivity(intent);
- } catch (Exception e) {
+ } catch (ActivityNotFoundException e) {
Log.e(K9.LOG_TAG, "Could not display attachment of type " + contentType, e);
String message = context.getString(R.string.message_view_no_viewer, contentType);
@@ -252,6 +254,30 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
+ private Intent constructViewIntent() {
+ Intent intent;
+ Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
+
+ Intent originalMimeTypeIntent = createViewIntentForContentUri(contentType, uri);
+ int originalMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(originalMimeTypeIntent);
+
+ String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
+ if (inferredMimeType.equals(contentType)) {
+ intent = originalMimeTypeIntent;
+ } else {
+ Intent inferredMimeTypeIntent = createViewIntentForContentUri(inferredMimeType, uri);
+ int inferredMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(inferredMimeTypeIntent);
+
+ if (inferredMimeTypeActivitiesCount > originalMimeTypeActivitiesCount) {
+ intent = inferredMimeTypeIntent;
+ } else {
+ intent = originalMimeTypeIntent;
+ }
+ }
+
+ return intent;
+ }
+
private Intent createViewIntentForContentUri(String mimeType, Uri uri) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
@@ -260,6 +286,15 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
return intent;
}
+ private int getResolvedIntentActivitiesCount(Intent intent) {
+ PackageManager packageManager = context.getPackageManager();
+
+ List resolveInfos =
+ packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+
+ return resolveInfos.size();
+ }
+
private void displayAttachmentSavedMessage(final String filename) {
String message = context.getString(R.string.message_view_status_attachment_saved, filename);
displayMessageToUser(message);
From 44ecf5d588aaa01e398e802455aeb13e2240134c Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 10 Nov 2014 00:56:24 +0100
Subject: [PATCH 18/27] Use MIME type used for intent resolution in content
provider
---
.../fsck/k9/provider/AttachmentProvider.java | 35 ++++++++++---------
src/com/fsck/k9/view/AttachmentView.java | 9 +++--
2 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/src/com/fsck/k9/provider/AttachmentProvider.java b/src/com/fsck/k9/provider/AttachmentProvider.java
index f3ba78590..5cc7e242c 100644
--- a/src/com/fsck/k9/provider/AttachmentProvider.java
+++ b/src/com/fsck/k9/provider/AttachmentProvider.java
@@ -51,11 +51,20 @@ public class AttachmentProvider extends ContentProvider {
public static Uri getAttachmentUri(Account account, long id) {
- return getAttachmentUri(account.getUuid(), id, true);
+ return CONTENT_URI.buildUpon()
+ .appendPath(account.getUuid())
+ .appendPath(Long.toString(id))
+ .appendPath(FORMAT_RAW)
+ .build();
}
- public static Uri getAttachmentUriForViewing(Account account, long id) {
- return getAttachmentUri(account.getUuid(), id, false);
+ public static Uri getAttachmentUriForViewing(Account account, long id, String mimeType) {
+ return CONTENT_URI.buildUpon()
+ .appendPath(account.getUuid())
+ .appendPath(Long.toString(id))
+ .appendPath(FORMAT_VIEW)
+ .appendPath(mimeType)
+ .build();
}
public static Uri getAttachmentThumbnailUri(Account account, long id, int width, int height) {
@@ -68,14 +77,6 @@ public class AttachmentProvider extends ContentProvider {
.build();
}
- private static Uri getAttachmentUri(String db, long id, boolean raw) {
- return CONTENT_URI.buildUpon()
- .appendPath(db)
- .appendPath(Long.toString(id))
- .appendPath(raw ? FORMAT_RAW : FORMAT_VIEW)
- .build();
- }
-
public static void clear(Context context) {
/*
* We use the cache dir as a temporary directory (since Android doesn't give us one) so
@@ -146,8 +147,9 @@ public class AttachmentProvider extends ContentProvider {
String dbName = segments.get(0);
String id = segments.get(1);
String format = segments.get(2);
+ String mimeType = (segments.size() < 4) ? null : segments.get(3);
- return getType(dbName, id, format);
+ return getType(dbName, id, format, mimeType);
}
@Override
@@ -165,7 +167,7 @@ public class AttachmentProvider extends ContentProvider {
file = getThumbnailFile(getContext(), accountUuid, attachmentId);
if (!file.exists()) {
- String type = getType(accountUuid, attachmentId, FORMAT_VIEW);
+ String type = getType(accountUuid, attachmentId, FORMAT_VIEW, null);
try {
FileInputStream in = new FileInputStream(getFile(accountUuid, attachmentId));
try {
@@ -258,7 +260,7 @@ public class AttachmentProvider extends ContentProvider {
return null;
}
- private String getType(String dbName, String id, String format) {
+ private String getType(String dbName, String id, String format, String mimeType) {
String type;
if (FORMAT_THUMBNAIL.equals(format)) {
type = "image/png";
@@ -269,10 +271,9 @@ public class AttachmentProvider extends ContentProvider {
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
- if (FORMAT_VIEW.equals(format)) {
- type = MimeUtility.getMimeTypeForViewing(attachmentInfo.type, attachmentInfo.name);
+ if (FORMAT_VIEW.equals(format) && mimeType != null) {
+ type = mimeType;
} else {
- // When accessing the "raw" message we deliver the original MIME type.
type = attachmentInfo.type;
}
} catch (MessagingException e) {
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index bf1a918b6..9e962a4e4 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -256,16 +256,19 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
private Intent constructViewIntent() {
Intent intent;
- Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId());
- Intent originalMimeTypeIntent = createViewIntentForContentUri(contentType, uri);
+ Uri originalMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
+ contentType);
+ Intent originalMimeTypeIntent = createViewIntentForContentUri(contentType, originalMimeTypeUri);
int originalMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(originalMimeTypeIntent);
String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
if (inferredMimeType.equals(contentType)) {
intent = originalMimeTypeIntent;
} else {
- Intent inferredMimeTypeIntent = createViewIntentForContentUri(inferredMimeType, uri);
+ Uri inferredMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
+ inferredMimeType);
+ Intent inferredMimeTypeIntent = createViewIntentForContentUri(inferredMimeType, inferredMimeTypeUri);
int inferredMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(inferredMimeTypeIntent);
if (inferredMimeTypeActivitiesCount > originalMimeTypeActivitiesCount) {
From 6a1905b7b7977acffc7b39309fcc347b40f9817e Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 10 Nov 2014 05:37:22 +0100
Subject: [PATCH 19/27] If we can't find an app to view an attachment try again
with a file:// URI
Sadly, some apps only support the 'file' scheme in their intent filters. Among
them is Android's own package installer.
---
src/com/fsck/k9/view/AttachmentView.java | 167 ++++++++++++++++++++---
1 file changed, 147 insertions(+), 20 deletions(-)
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 9e962a4e4..19595c920 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -223,16 +223,10 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
*/
public void writeFile(File directory) {
try {
- String filename = Utility.sanitizeFilename(name);
- File file = Utility.createUniqueFile(directory, filename);
- Uri uri = AttachmentProvider.getAttachmentUri(account, part.getAttachmentId());
- InputStream in = context.getContentResolver().openInputStream(uri);
- OutputStream out = new FileOutputStream(file);
- IOUtils.copy(in, out);
- out.flush();
- out.close();
- in.close();
+ File file = writeAttachmentToStorage(directory);
+
displayAttachmentSavedMessage(file.toString());
+
new MediaScannerNotifier(context, file);
} catch (IOException ioe) {
if (K9.DEBUG) {
@@ -242,20 +236,55 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
- public void showFile() {
- Intent intent = constructViewIntent();
- try {
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.e(K9.LOG_TAG, "Could not display attachment of type " + contentType, e);
+ private File writeAttachmentToStorage(File directory) throws IOException {
+ String filename = Utility.sanitizeFilename(name);
+ File file = Utility.createUniqueFile(directory, filename);
- String message = context.getString(R.string.message_view_no_viewer, contentType);
- displayMessageToUser(message);
+ Uri uri = AttachmentProvider.getAttachmentUri(account, part.getAttachmentId());
+ InputStream in = context.getContentResolver().openInputStream(uri);
+ try {
+ OutputStream out = new FileOutputStream(file);
+ try {
+ IOUtils.copy(in, out);
+ out.flush();
+ } finally {
+ out.close();
+ }
+ } finally {
+ in.close();
}
+
+ return file;
}
- private Intent constructViewIntent() {
+ public void showFile() {
+ new ViewAttachmentAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private Intent getBestViewIntentAndSaveFileIfNecessary() {
+ IntentAndResolvedActivitiesCount resultForContentUri = getBestViewIntentForContentUri();
+ if (resultForContentUri.getActivitiesCount() > 0) {
+ return resultForContentUri.getIntent();
+ }
+
+ IntentAndResolvedActivitiesCount resultForFileUri = getBestViewIntentForFileUri();
+ if (resultForFileUri.getActivitiesCount() > 0) {
+ try {
+ File file = writeAttachmentToStorage(new File(K9.getAttachmentDefaultPath()));
+ return createViewIntentForFileUri(resultForFileUri.getMimeType(), Uri.fromFile(file));
+ } catch (IOException e) {
+ if (K9.DEBUG) {
+ Log.e(K9.LOG_TAG, "Error while saving attachment to use file:// URI with ACTION_VIEW Intent", e);
+ }
+ }
+ }
+
+ return resultForContentUri.getIntent();
+ }
+
+ private IntentAndResolvedActivitiesCount getBestViewIntentForContentUri() {
Intent intent;
+ int activitiesCount;
Uri originalMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
contentType);
@@ -265,6 +294,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
if (inferredMimeType.equals(contentType)) {
intent = originalMimeTypeIntent;
+ activitiesCount = originalMimeTypeActivitiesCount;
} else {
Uri inferredMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
inferredMimeType);
@@ -273,22 +303,67 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
if (inferredMimeTypeActivitiesCount > originalMimeTypeActivitiesCount) {
intent = inferredMimeTypeIntent;
+ activitiesCount = inferredMimeTypeActivitiesCount;
} else {
intent = originalMimeTypeIntent;
+ activitiesCount = originalMimeTypeActivitiesCount;
}
}
- return intent;
+ return new IntentAndResolvedActivitiesCount(intent, activitiesCount);
+ }
+
+ private IntentAndResolvedActivitiesCount getBestViewIntentForFileUri() {
+ Intent intent;
+ int activitiesCount;
+
+ File dummyFile = new File(Utility.sanitizeFilename(name));
+ Uri fileUri = Uri.fromFile(dummyFile);
+
+ Intent originalMimeTypeIntent = createViewIntentForFileUri(contentType, fileUri);
+ int originalMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(originalMimeTypeIntent);
+
+ String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
+ if (inferredMimeType.equals(contentType)) {
+ intent = originalMimeTypeIntent;
+ activitiesCount = originalMimeTypeActivitiesCount;
+ } else {
+ Intent inferredMimeTypeIntent = createViewIntentForFileUri(inferredMimeType, fileUri);
+ int inferredMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(inferredMimeTypeIntent);
+
+ if (inferredMimeTypeActivitiesCount > originalMimeTypeActivitiesCount) {
+ intent = inferredMimeTypeIntent;
+ activitiesCount = inferredMimeTypeActivitiesCount;
+ } else {
+ intent = originalMimeTypeIntent;
+ activitiesCount = originalMimeTypeActivitiesCount;
+ }
+ }
+
+ return new IntentAndResolvedActivitiesCount(intent, activitiesCount);
}
private Intent createViewIntentForContentUri(String mimeType, Uri uri) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ addUiIntentFlags(intent);
return intent;
}
+ private Intent createViewIntentForFileUri(String mimeType, Uri uri) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(uri, mimeType);
+ addUiIntentFlags(intent);
+
+ return intent;
+ }
+
+ private void addUiIntentFlags(Intent intent) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ }
+
private int getResolvedIntentActivitiesCount(Intent intent) {
PackageManager packageManager = context.getPackageManager();
@@ -328,6 +403,28 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
+ private static class IntentAndResolvedActivitiesCount {
+ private Intent intent;
+ private int activitiesCount;
+
+ IntentAndResolvedActivitiesCount(Intent intent, int activitiesCount) {
+ this.intent = intent;
+ this.activitiesCount = activitiesCount;
+ }
+
+ public Intent getIntent() {
+ return intent;
+ }
+
+ public int getActivitiesCount() {
+ return activitiesCount;
+ }
+
+ public String getMimeType() {
+ return intent.getType();
+ }
+ }
+
private class LoadAndDisplayThumbnailAsyncTask extends AsyncTask {
private final ImageView thumbnail;
@@ -364,4 +461,34 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
}
+
+ private class ViewAttachmentAsyncTask extends AsyncTask {
+
+ @Override
+ protected void onPreExecute() {
+ viewButton.setEnabled(false);
+ }
+
+ @Override
+ protected Intent doInBackground(Void... params) {
+ return getBestViewIntentAndSaveFileIfNecessary();
+ }
+
+ @Override
+ protected void onPostExecute(Intent intent) {
+ viewAttachment(intent);
+ viewButton.setEnabled(true);
+ }
+
+ private void viewAttachment(Intent intent) {
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(K9.LOG_TAG, "Could not display attachment of type " + contentType, e);
+
+ String message = context.getString(R.string.message_view_no_viewer, contentType);
+ displayMessageToUser(message);
+ }
+ }
+ }
}
From dfd40659d140367bd2a564c3443aa6892294e281 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 10 Nov 2014 05:46:38 +0100
Subject: [PATCH 20/27] Append file name to content:// URI
This allows intent filters with patterns for file extensions to match.
---
src/com/fsck/k9/provider/AttachmentProvider.java | 3 ++-
src/com/fsck/k9/view/AttachmentView.java | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/com/fsck/k9/provider/AttachmentProvider.java b/src/com/fsck/k9/provider/AttachmentProvider.java
index 5cc7e242c..15c81e6a2 100644
--- a/src/com/fsck/k9/provider/AttachmentProvider.java
+++ b/src/com/fsck/k9/provider/AttachmentProvider.java
@@ -58,12 +58,13 @@ public class AttachmentProvider extends ContentProvider {
.build();
}
- public static Uri getAttachmentUriForViewing(Account account, long id, String mimeType) {
+ public static Uri getAttachmentUriForViewing(Account account, long id, String mimeType, String filename) {
return CONTENT_URI.buildUpon()
.appendPath(account.getUuid())
.appendPath(Long.toString(id))
.appendPath(FORMAT_VIEW)
.appendPath(mimeType)
+ .appendPath(filename)
.build();
}
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 19595c920..8dcef251d 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -287,7 +287,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
int activitiesCount;
Uri originalMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
- contentType);
+ contentType, name);
Intent originalMimeTypeIntent = createViewIntentForContentUri(contentType, originalMimeTypeUri);
int originalMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(originalMimeTypeIntent);
@@ -297,7 +297,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
activitiesCount = originalMimeTypeActivitiesCount;
} else {
Uri inferredMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
- inferredMimeType);
+ inferredMimeType, name);
Intent inferredMimeTypeIntent = createViewIntentForContentUri(inferredMimeType, inferredMimeTypeUri);
int inferredMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(inferredMimeTypeIntent);
From c3f1420ef6102b636e659c0c939079a26ac2bc9c Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 10 Nov 2014 06:43:00 +0100
Subject: [PATCH 21/27] Don't open the file after the media scanner added it to
the media content provider
---
src/com/fsck/k9/helper/MediaScannerNotifier.java | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/src/com/fsck/k9/helper/MediaScannerNotifier.java b/src/com/fsck/k9/helper/MediaScannerNotifier.java
index 8229b8297..dd8bfcdff 100644
--- a/src/com/fsck/k9/helper/MediaScannerNotifier.java
+++ b/src/com/fsck/k9/helper/MediaScannerNotifier.java
@@ -1,7 +1,6 @@
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;
@@ -12,14 +11,11 @@ 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() {
@@ -27,14 +23,6 @@ public class MediaScannerNotifier implements MediaScannerConnectionClient {
}
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();
- }
+ mConnection.disconnect();
}
}
From e64ca84f1bc9e7e09c0ba87735d0982f4a476d55 Mon Sep 17 00:00:00 2001
From: cketti
Date: Mon, 10 Nov 2014 06:43:26 +0100
Subject: [PATCH 22/27] Simplify MediaScannerNotifier
---
.../fsck/k9/helper/MediaScannerNotifier.java | 25 +++++--------------
src/com/fsck/k9/view/AttachmentView.java | 2 +-
2 files changed, 7 insertions(+), 20 deletions(-)
diff --git a/src/com/fsck/k9/helper/MediaScannerNotifier.java b/src/com/fsck/k9/helper/MediaScannerNotifier.java
index dd8bfcdff..9dbed11ce 100644
--- a/src/com/fsck/k9/helper/MediaScannerNotifier.java
+++ b/src/com/fsck/k9/helper/MediaScannerNotifier.java
@@ -1,28 +1,15 @@
package com.fsck.k9.helper;
-import android.content.Context;
-import android.media.MediaScannerConnection;
-import android.media.MediaScannerConnection.MediaScannerConnectionClient;
-import android.net.Uri;
import java.io.File;
+import android.content.Context;
+import android.media.MediaScannerConnection;
-public 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) {
- mConnection.disconnect();
+public class MediaScannerNotifier {
+ public static void notify(Context context, File file) {
+ String[] paths = { file.getAbsolutePath() };
+ MediaScannerConnection.scanFile(context, paths, null, null);
}
}
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 8dcef251d..000cf2b87 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -227,7 +227,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
displayAttachmentSavedMessage(file.toString());
- new MediaScannerNotifier(context, file);
+ MediaScannerNotifier.notify(context, file);
} catch (IOException ioe) {
if (K9.DEBUG) {
Log.e(K9.LOG_TAG, "Error saving attachment", ioe);
From 34cfd8e5b422fa8c5b30fc0c526fb747e519feb0 Mon Sep 17 00:00:00 2001
From: cketti
Date: Tue, 11 Nov 2014 01:04:39 +0100
Subject: [PATCH 23/27] Extract file related helper functions into separate
class
---
src/com/fsck/k9/helper/FileHelper.java | 165 +++++++++++++++++
src/com/fsck/k9/helper/Utility.java | 172 ------------------
.../fsck/k9/mail/store/LockableDatabase.java | 11 +-
.../fsck/k9/preferences/SettingsExporter.java | 5 +-
src/com/fsck/k9/view/AttachmentView.java | 8 +-
src/com/fsck/k9/view/SingleMessageView.java | 5 +-
6 files changed, 181 insertions(+), 185 deletions(-)
create mode 100644 src/com/fsck/k9/helper/FileHelper.java
diff --git a/src/com/fsck/k9/helper/FileHelper.java b/src/com/fsck/k9/helper/FileHelper.java
new file mode 100644
index 000000000..c33ed4cb9
--- /dev/null
+++ b/src/com/fsck/k9/helper/FileHelper.java
@@ -0,0 +1,165 @@
+package com.fsck.k9.helper;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Locale;
+
+import android.util.Log;
+
+import com.fsck.k9.K9;
+
+
+public class FileHelper {
+ /**
+ * Regular expression that represents characters we won't allow in file names.
+ *
+ *
+ * Allowed are:
+ *
+ * - word characters (letters, digits, and underscores): {@code \w}
+ * - spaces: {@code " "}
+ * - special characters: {@code !}, {@code #}, {@code $}, {@code %}, {@code &}, {@code '},
+ * {@code (}, {@code )}, {@code -}, {@code @}, {@code ^}, {@code `},
{
,
+ * }
, {@code ~}, {@code .}, {@code ,}
+ *
+ *
+ * @see #sanitizeFilename(String)
+ */
+ private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]+";
+
+ /**
+ * Invalid characters in a file name are replaced by this character.
+ *
+ * @see #sanitizeFilename(String)
+ */
+ private static final String REPLACEMENT_CHARACTER = "_";
+
+
+ /**
+ * Creates a unique file in the given directory by appending a hyphen
+ * and a number to the given filename.
+ */
+ public static File createUniqueFile(File directory, String filename) {
+ File file = new File(directory, filename);
+ if (!file.exists()) {
+ return file;
+ }
+ // Get the extension of the file, if any.
+ int index = filename.lastIndexOf('.');
+ String format;
+ if (index != -1) {
+ String name = filename.substring(0, index);
+ String extension = filename.substring(index);
+ format = name + "-%d" + extension;
+ } else {
+ format = filename + "-%d";
+ }
+ for (int i = 2; i < Integer.MAX_VALUE; i++) {
+ file = new File(directory, String.format(Locale.US, format, i));
+ if (!file.exists()) {
+ return file;
+ }
+ }
+ return null;
+ }
+
+ public static void touchFile(final File parentDir, final String name) {
+ final File file = new File(parentDir, name);
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ } else {
+ file.setLastModified(System.currentTimeMillis());
+ }
+ } catch (Exception e) {
+ Log.d(K9.LOG_TAG, "Unable to touch file: " + file.getAbsolutePath(), e);
+ }
+ }
+
+ public static boolean move(final File from, final File to) {
+ if (to.exists()) {
+ to.delete();
+ }
+ to.getParentFile().mkdirs();
+
+ try {
+ FileInputStream in = new FileInputStream(from);
+ try {
+ FileOutputStream out = new FileOutputStream(to);
+ try {
+ byte[] buffer = new byte[1024];
+ int count = -1;
+ while ((count = in.read(buffer)) > 0) {
+ out.write(buffer, 0, count);
+ }
+ } finally {
+ out.close();
+ }
+ } finally {
+ try { in.close(); } catch (Throwable ignore) {}
+ }
+ from.delete();
+ return true;
+ } catch (Exception e) {
+ Log.w(K9.LOG_TAG, "cannot move " + from.getAbsolutePath() + " to " + to.getAbsolutePath(), e);
+ return false;
+ }
+
+ }
+
+ public static void moveRecursive(final File fromDir, final File toDir) {
+ if (!fromDir.exists()) {
+ return;
+ }
+ if (!fromDir.isDirectory()) {
+ if (toDir.exists()) {
+ if (!toDir.delete()) {
+ Log.w(K9.LOG_TAG, "cannot delete already existing file/directory " + toDir.getAbsolutePath());
+ }
+ }
+ if (!fromDir.renameTo(toDir)) {
+ Log.w(K9.LOG_TAG, "cannot rename " + fromDir.getAbsolutePath() + " to " + toDir.getAbsolutePath() + " - moving instead");
+ move(fromDir, toDir);
+ }
+ return;
+ }
+ if (!toDir.exists() || !toDir.isDirectory()) {
+ if (toDir.exists()) {
+ toDir.delete();
+ }
+ if (!toDir.mkdirs()) {
+ Log.w(K9.LOG_TAG, "cannot create directory " + toDir.getAbsolutePath());
+ }
+ }
+ File[] files = fromDir.listFiles();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ moveRecursive(file, new File(toDir, file.getName()));
+ file.delete();
+ } else {
+ File target = new File(toDir, file.getName());
+ if (!file.renameTo(target)) {
+ Log.w(K9.LOG_TAG, "cannot rename " + file.getAbsolutePath() + " to " + target.getAbsolutePath() + " - moving instead");
+ move(file, target);
+ }
+ }
+ }
+ if (!fromDir.delete()) {
+ Log.w(K9.LOG_TAG, "cannot delete " + fromDir.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Replace characters we don't allow in file names with a replacement character.
+ *
+ * @param filename
+ * The original file name.
+ *
+ * @return The sanitized file name containing only allowed characters.
+ */
+ public static String sanitizeFilename(String filename) {
+ return filename.replaceAll(INVALID_CHARACTERS, REPLACEMENT_CHARACTER);
+ }
+}
diff --git a/src/com/fsck/k9/helper/Utility.java b/src/com/fsck/k9/helper/Utility.java
index a5bea3448..16466a7c1 100644
--- a/src/com/fsck/k9/helper/Utility.java
+++ b/src/com/fsck/k9/helper/Utility.java
@@ -17,40 +17,13 @@ import android.widget.TextView;
import com.fsck.k9.K9;
import com.fsck.k9.mail.filter.Base64;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Utility {
- /**
- * Regular expression that represents characters we won't allow in file names.
- *
- *
- * Allowed are:
- *
- * - word characters (letters, digits, and underscores): {@code \w}
- * - spaces: {@code " "}
- * - special characters: {@code !}, {@code #}, {@code $}, {@code %}, {@code &}, {@code '},
- * {@code (}, {@code )}, {@code -}, {@code @}, {@code ^}, {@code `},
{
,
- * }
, {@code ~}, {@code .}, {@code ,}
- *
- *
- * @see #sanitizeFilename(String)
- */
- private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]+";
-
- /**
- * Invalid characters in a file name are replaced by this character.
- *
- * @see #sanitizeFilename(String)
- */
- private static final String REPLACEMENT_CHARACTER = "_";
// \u00A0 (non-breaking space) happens to be used by French MUA
@@ -454,139 +427,6 @@ public class Utility {
}
}
- /**
- * @param parentDir
- * @param name
- * Never null
.
- */
- public static void touchFile(final File parentDir, final String name) {
- final File file = new File(parentDir, name);
- try {
- if (!file.exists()) {
- file.createNewFile();
- } else {
- file.setLastModified(System.currentTimeMillis());
- }
- } catch (Exception e) {
- Log.d(K9.LOG_TAG, "Unable to touch file: " + file.getAbsolutePath(), e);
- }
- }
-
- /**
- * Creates a unique file in the given directory by appending a hyphen
- * and a number to the given filename.
- *
- * @param directory
- * @param filename
- * @return
- */
- public static File createUniqueFile(File directory, String filename) {
- File file = new File(directory, filename);
- if (!file.exists()) {
- return file;
- }
- // Get the extension of the file, if any.
- int index = filename.lastIndexOf('.');
- String format;
- if (index != -1) {
- String name = filename.substring(0, index);
- String extension = filename.substring(index);
- format = name + "-%d" + extension;
- } else {
- format = filename + "-%d";
- }
- for (int i = 2; i < Integer.MAX_VALUE; i++) {
- file = new File(directory, String.format(Locale.US, format, i));
- if (!file.exists()) {
- return file;
- }
- }
- return null;
- }
-
-
-
- /**
- * @param from
- * @param to
- * @return
- */
- public static boolean move(final File from, final File to) {
- if (to.exists()) {
- to.delete();
- }
- to.getParentFile().mkdirs();
-
- try {
- FileInputStream in = new FileInputStream(from);
- try {
- FileOutputStream out = new FileOutputStream(to);
- try {
- byte[] buffer = new byte[1024];
- int count = -1;
- while ((count = in.read(buffer)) > 0) {
- out.write(buffer, 0, count);
- }
- } finally {
- out.close();
- }
- } finally {
- try { in.close(); } catch (Throwable ignore) {}
- }
- from.delete();
- return true;
- } catch (Exception e) {
- Log.w(K9.LOG_TAG, "cannot move " + from.getAbsolutePath() + " to " + to.getAbsolutePath(), e);
- return false;
- }
-
- }
-
- /**
- * @param fromDir
- * @param toDir
- */
- public static void moveRecursive(final File fromDir, final File toDir) {
- if (!fromDir.exists()) {
- return;
- }
- if (!fromDir.isDirectory()) {
- if (toDir.exists()) {
- if (!toDir.delete()) {
- Log.w(K9.LOG_TAG, "cannot delete already existing file/directory " + toDir.getAbsolutePath());
- }
- }
- if (!fromDir.renameTo(toDir)) {
- Log.w(K9.LOG_TAG, "cannot rename " + fromDir.getAbsolutePath() + " to " + toDir.getAbsolutePath() + " - moving instead");
- move(fromDir, toDir);
- }
- return;
- }
- if (!toDir.exists() || !toDir.isDirectory()) {
- if (toDir.exists()) {
- toDir.delete();
- }
- if (!toDir.mkdirs()) {
- Log.w(K9.LOG_TAG, "cannot create directory " + toDir.getAbsolutePath());
- }
- }
- File[] files = fromDir.listFiles();
- for (File file : files) {
- if (file.isDirectory()) {
- moveRecursive(file, new File(toDir, file.getName()));
- file.delete();
- } else {
- File target = new File(toDir, file.getName());
- if (!file.renameTo(target)) {
- Log.w(K9.LOG_TAG, "cannot rename " + file.getAbsolutePath() + " to " + target.getAbsolutePath() + " - moving instead");
- move(file, target);
- }
- }
- }
- if (!fromDir.delete()) {
- Log.w(K9.LOG_TAG, "cannot delete " + fromDir.getAbsolutePath());
- }
- }
private static final String IMG_SRC_REGEX = "(?is:]+src\\s*=\\s*['\"]?([a-z]+)\\:)";
private static final Pattern IMG_PATTERN = Pattern.compile(IMG_SRC_REGEX);
@@ -624,18 +464,6 @@ public class Utility {
}
}
- /**
- * Replace characters we don't allow in file names with a replacement character.
- *
- * @param filename
- * The original file name.
- *
- * @return The sanitized file name containing only allowed characters.
- */
- public static String sanitizeFilename(String filename) {
- return filename.replaceAll(INVALID_CHARACTERS, REPLACEMENT_CHARACTER);
- }
-
/**
* Check to see if we have network connectivity.
* @param app Current application (Hint: see if your base class has a getApplication() method.)
diff --git a/src/com/fsck/k9/mail/store/LockableDatabase.java b/src/com/fsck/k9/mail/store/LockableDatabase.java
index 81138d6e8..d1ede902b 100644
--- a/src/com/fsck/k9/mail/store/LockableDatabase.java
+++ b/src/com/fsck/k9/mail/store/LockableDatabase.java
@@ -14,7 +14,7 @@ import android.os.Build;
import android.util.Log;
import com.fsck.k9.K9;
-import com.fsck.k9.helper.Utility;
+import com.fsck.k9.helper.FileHelper;
import com.fsck.k9.mail.MessagingException;
public class LockableDatabase {
@@ -337,9 +337,10 @@ public class LockableDatabase {
prepareStorage(newProviderId);
// move all database files
- Utility.moveRecursive(oldDatabase, storageManager.getDatabase(uUid, newProviderId));
+ FileHelper.moveRecursive(oldDatabase, storageManager.getDatabase(uUid, newProviderId));
// move all attachment files
- Utility.moveRecursive(storageManager.getAttachmentDirectory(uUid, oldProviderId), storageManager.getAttachmentDirectory(uUid, newProviderId));
+ FileHelper.moveRecursive(storageManager.getAttachmentDirectory(uUid, oldProviderId),
+ storageManager.getAttachmentDirectory(uUid, newProviderId));
// remove any remaining old journal files
deleteDatabase(oldDatabase);
@@ -425,7 +426,7 @@ public class LockableDatabase {
// Android seems to be unmounting the storage...
throw new UnavailableStorageException("Unable to access: " + databaseParentDir);
}
- Utility.touchFile(databaseParentDir, ".nomedia");
+ FileHelper.touchFile(databaseParentDir, ".nomedia");
}
final File attachmentDir;
@@ -435,7 +436,7 @@ public class LockableDatabase {
attachmentParentDir = attachmentDir.getParentFile();
if (!attachmentParentDir.exists()) {
attachmentParentDir.mkdirs();
- Utility.touchFile(attachmentParentDir, ".nomedia");
+ FileHelper.touchFile(attachmentParentDir, ".nomedia");
}
if (!attachmentDir.exists()) {
attachmentDir.mkdirs();
diff --git a/src/com/fsck/k9/preferences/SettingsExporter.java b/src/com/fsck/k9/preferences/SettingsExporter.java
index e753e31f3..3921cfc41 100644
--- a/src/com/fsck/k9/preferences/SettingsExporter.java
+++ b/src/com/fsck/k9/preferences/SettingsExporter.java
@@ -12,6 +12,8 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
+
+import com.fsck.k9.helper.FileHelper;
import org.xmlpull.v1.XmlSerializer;
import android.content.Context;
@@ -23,7 +25,6 @@ import android.util.Xml;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
-import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport;
@@ -87,7 +88,7 @@ public class SettingsExporter {
File dir = new File(Environment.getExternalStorageDirectory() + File.separator
+ context.getPackageName());
dir.mkdirs();
- File file = Utility.createUniqueFile(dir, EXPORT_FILENAME);
+ File file = FileHelper.createUniqueFile(dir, EXPORT_FILENAME);
filename = file.getAbsolutePath();
os = new FileOutputStream(filename);
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 000cf2b87..03a199b20 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -34,9 +34,9 @@ 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.FileHelper;
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.MessagingException;
import com.fsck.k9.mail.Part;
@@ -237,8 +237,8 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
private File writeAttachmentToStorage(File directory) throws IOException {
- String filename = Utility.sanitizeFilename(name);
- File file = Utility.createUniqueFile(directory, filename);
+ String filename = FileHelper.sanitizeFilename(name);
+ File file = FileHelper.createUniqueFile(directory, filename);
Uri uri = AttachmentProvider.getAttachmentUri(account, part.getAttachmentId());
InputStream in = context.getContentResolver().openInputStream(uri);
@@ -317,7 +317,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
Intent intent;
int activitiesCount;
- File dummyFile = new File(Utility.sanitizeFilename(name));
+ File dummyFile = new File(FileHelper.sanitizeFilename(name));
Uri fileUri = Uri.fromFile(dummyFile);
Intent originalMimeTypeIntent = createViewIntentForFileUri(contentType, fileUri);
diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java
index 2921ed494..aeb23c3e3 100644
--- a/src/com/fsck/k9/view/SingleMessageView.java
+++ b/src/com/fsck/k9/view/SingleMessageView.java
@@ -46,6 +46,7 @@ import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.fragment.MessageViewFragment;
import com.fsck.k9.helper.ClipboardManager;
import com.fsck.k9.helper.Contacts;
+import com.fsck.k9.helper.FileHelper;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
@@ -842,10 +843,10 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
filename += "." + extension;
}
- String sanitized = Utility.sanitizeFilename(filename);
+ String sanitized = FileHelper.sanitizeFilename(filename);
File directory = new File(K9.getAttachmentDefaultPath());
- File file = Utility.createUniqueFile(directory, sanitized);
+ File file = FileHelper.createUniqueFile(directory, sanitized);
FileOutputStream out = new FileOutputStream(file);
try {
IOUtils.copy(in, out);
From 87ca0d3d2a9be0d4321e3d9c2532f33b0213b46d Mon Sep 17 00:00:00 2001
From: cketti
Date: Tue, 11 Nov 2014 02:08:50 +0100
Subject: [PATCH 24/27] Use TemporaryAttachmentStore when viewing attachments
using file:// URI
---
.../k9/cache/TemporaryAttachmentStore.java | 59 +++++++++++++++++++
src/com/fsck/k9/view/AttachmentView.java | 18 ++++--
2 files changed, 71 insertions(+), 6 deletions(-)
create mode 100644 src/com/fsck/k9/cache/TemporaryAttachmentStore.java
diff --git a/src/com/fsck/k9/cache/TemporaryAttachmentStore.java b/src/com/fsck/k9/cache/TemporaryAttachmentStore.java
new file mode 100644
index 000000000..c2a4a264a
--- /dev/null
+++ b/src/com/fsck/k9/cache/TemporaryAttachmentStore.java
@@ -0,0 +1,59 @@
+package com.fsck.k9.cache;
+
+
+import java.io.File;
+import java.io.IOException;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.fsck.k9.K9;
+import com.fsck.k9.helper.FileHelper;
+
+
+public class TemporaryAttachmentStore {
+ private static String TEMPORARY_ATTACHMENT_DIRECTORY = "attachments";
+ private static long MAX_FILE_AGE = 12 * 60 * 60 * 1000; // 12h
+
+ public static File getFile(Context context, String attachmentName) throws IOException {
+ File directory = createOrCleanAttachmentDirectory(context);
+ String filename = FileHelper.sanitizeFilename(attachmentName);
+ return new File(directory, filename);
+ }
+
+ private static File createOrCleanAttachmentDirectory(Context context) throws IOException {
+ File directory = getTemporaryAttachmentDirectory(context);
+ if (directory.exists()) {
+ cleanOldFiles(directory);
+ } else {
+ if (!directory.mkdir()) {
+ throw new IOException("Couldn't create temporary attachment store: " + directory.getAbsolutePath());
+ }
+ }
+ return directory;
+ }
+
+ private static File getTemporaryAttachmentDirectory(Context context) {
+ return new File(context.getExternalCacheDir(), TEMPORARY_ATTACHMENT_DIRECTORY);
+ }
+
+ private static void cleanOldFiles(File directory) {
+ File[] files = directory.listFiles();
+ if (files == null) {
+ return;
+ }
+
+ long cutOffTime = System.currentTimeMillis() - MAX_FILE_AGE;
+ for (File file : files) {
+ if (file.lastModified() < cutOffTime) {
+ if (file.delete()) {
+ if (K9.DEBUG) {
+ Log.d(K9.LOG_TAG, "Deleted from temporary attachment store: " + file.getName());
+ }
+ } else {
+ Log.w(K9.LOG_TAG, "Couldn't delete from temporary attachment store: " + file.getName());
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 03a199b20..5367e66cd 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -32,6 +32,7 @@ import android.widget.Toast;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
+import com.fsck.k9.cache.TemporaryAttachmentStore;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.helper.FileHelper;
@@ -223,7 +224,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
*/
public void writeFile(File directory) {
try {
- File file = writeAttachmentToStorage(directory);
+ File file = saveAttachmentWithUniqueFileName(directory);
displayAttachmentSavedMessage(file.toString());
@@ -236,10 +237,16 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
}
- private File writeAttachmentToStorage(File directory) throws IOException {
+ private File saveAttachmentWithUniqueFileName(File directory) throws IOException {
String filename = FileHelper.sanitizeFilename(name);
File file = FileHelper.createUniqueFile(directory, filename);
+ writeAttachmentToStorage(file);
+
+ return file;
+ }
+
+ private void writeAttachmentToStorage(File file) throws IOException {
Uri uri = AttachmentProvider.getAttachmentUri(account, part.getAttachmentId());
InputStream in = context.getContentResolver().openInputStream(uri);
try {
@@ -253,8 +260,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
} finally {
in.close();
}
-
- return file;
}
public void showFile() {
@@ -270,8 +275,9 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
IntentAndResolvedActivitiesCount resultForFileUri = getBestViewIntentForFileUri();
if (resultForFileUri.getActivitiesCount() > 0) {
try {
- File file = writeAttachmentToStorage(new File(K9.getAttachmentDefaultPath()));
- return createViewIntentForFileUri(resultForFileUri.getMimeType(), Uri.fromFile(file));
+ File tempFile = TemporaryAttachmentStore.getFile(context, name);
+ writeAttachmentToStorage(tempFile);
+ return createViewIntentForFileUri(resultForFileUri.getMimeType(), Uri.fromFile(tempFile));
} catch (IOException e) {
if (K9.DEBUG) {
Log.e(K9.LOG_TAG, "Error while saving attachment to use file:// URI with ACTION_VIEW Intent", e);
From d9b6e10cbeda0bf189478ec18733b4fc7451c596 Mon Sep 17 00:00:00 2001
From: cketti
Date: Tue, 18 Nov 2014 22:55:51 +0100
Subject: [PATCH 25/27] Change the way the best view intent is determined
First we try the original MIME type unless it's application/octet-stream.
Then we try the MIME type inferred from the attachment's file extension.
Then we fall back to application/octet-stream.
In all cases we first try the content:// URI, then a file:// URI.
---
.../fsck/k9/mail/internet/MimeUtility.java | 16 +--
src/com/fsck/k9/view/AttachmentView.java | 115 ++++++++----------
2 files changed, 51 insertions(+), 80 deletions(-)
diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java
index c97e25df6..e787536b5 100644
--- a/src/com/fsck/k9/mail/internet/MimeUtility.java
+++ b/src/com/fsck/k9/mail/internet/MimeUtility.java
@@ -1138,20 +1138,8 @@ public class MimeUtility {
return p.matcher(mimeType).matches();
}
- /**
- * Returns true if the given mimeType matches any of the matchAgainst specifications.
- * @param mimeType A MIME type to check.
- * @param matchAgainst An array of MIME types to check against. May include wildcards such
- * as image/* or * /*.
- * @return
- */
- public static boolean mimeTypeMatches(String mimeType, String[] matchAgainst) {
- for (String matchType : matchAgainst) {
- if (mimeTypeMatches(mimeType, matchType)) {
- return true;
- }
- }
- return false;
+ public static boolean isDefaultMimeType(String mimeType) {
+ return DEFAULT_ATTACHMENT_MIME_TYPE.equalsIgnoreCase(mimeType);
}
/**
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index 5367e66cd..e18884734 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -267,89 +267,68 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
}
private Intent getBestViewIntentAndSaveFileIfNecessary() {
- IntentAndResolvedActivitiesCount resultForContentUri = getBestViewIntentForContentUri();
- if (resultForContentUri.getActivitiesCount() > 0) {
- return resultForContentUri.getIntent();
+ String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
+
+ IntentAndResolvedActivitiesCount resolvedIntentInfo;
+ if (MimeUtility.isDefaultMimeType(contentType)) {
+ resolvedIntentInfo = getBestViewIntentForMimeType(inferredMimeType);
+ } else {
+ resolvedIntentInfo = getBestViewIntentForMimeType(contentType);
+ if (!resolvedIntentInfo.hasResolvedActivities() && !inferredMimeType.equals(contentType)) {
+ resolvedIntentInfo = getBestViewIntentForMimeType(inferredMimeType);
+ }
}
- IntentAndResolvedActivitiesCount resultForFileUri = getBestViewIntentForFileUri();
- if (resultForFileUri.getActivitiesCount() > 0) {
+ if (!resolvedIntentInfo.hasResolvedActivities()) {
+ resolvedIntentInfo = getBestViewIntentForMimeType(MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE);
+ }
+
+ Intent viewIntent;
+ if (resolvedIntentInfo.hasResolvedActivities() && resolvedIntentInfo.containsFileUri()) {
try {
File tempFile = TemporaryAttachmentStore.getFile(context, name);
writeAttachmentToStorage(tempFile);
- return createViewIntentForFileUri(resultForFileUri.getMimeType(), Uri.fromFile(tempFile));
+ viewIntent = createViewIntentForFileUri(resolvedIntentInfo.getMimeType(), Uri.fromFile(tempFile));
} catch (IOException e) {
if (K9.DEBUG) {
Log.e(K9.LOG_TAG, "Error while saving attachment to use file:// URI with ACTION_VIEW Intent", e);
}
+ viewIntent = createViewIntentForAttachmentProviderUri(MimeUtility.DEFAULT_ATTACHMENT_MIME_TYPE);
}
- }
-
- return resultForContentUri.getIntent();
- }
-
- private IntentAndResolvedActivitiesCount getBestViewIntentForContentUri() {
- Intent intent;
- int activitiesCount;
-
- Uri originalMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
- contentType, name);
- Intent originalMimeTypeIntent = createViewIntentForContentUri(contentType, originalMimeTypeUri);
- int originalMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(originalMimeTypeIntent);
-
- String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
- if (inferredMimeType.equals(contentType)) {
- intent = originalMimeTypeIntent;
- activitiesCount = originalMimeTypeActivitiesCount;
} else {
- Uri inferredMimeTypeUri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(),
- inferredMimeType, name);
- Intent inferredMimeTypeIntent = createViewIntentForContentUri(inferredMimeType, inferredMimeTypeUri);
- int inferredMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(inferredMimeTypeIntent);
-
- if (inferredMimeTypeActivitiesCount > originalMimeTypeActivitiesCount) {
- intent = inferredMimeTypeIntent;
- activitiesCount = inferredMimeTypeActivitiesCount;
- } else {
- intent = originalMimeTypeIntent;
- activitiesCount = originalMimeTypeActivitiesCount;
- }
+ viewIntent = resolvedIntentInfo.getIntent();
}
- return new IntentAndResolvedActivitiesCount(intent, activitiesCount);
+ return viewIntent;
}
- private IntentAndResolvedActivitiesCount getBestViewIntentForFileUri() {
- Intent intent;
- int activitiesCount;
+ private IntentAndResolvedActivitiesCount getBestViewIntentForMimeType(String mimeType) {
+ Intent contentUriIntent = createViewIntentForAttachmentProviderUri(mimeType);
+ int contentUriActivitiesCount = getResolvedIntentActivitiesCount(contentUriIntent);
+ if (contentUriActivitiesCount > 0) {
+ return new IntentAndResolvedActivitiesCount(contentUriIntent, contentUriActivitiesCount);
+ }
+
+ Uri dummyFileUri = getDummyFileUri();
+ Intent fileUriIntent = createViewIntentForFileUri(mimeType, dummyFileUri);
+ int fileUriActivitiesCount = getResolvedIntentActivitiesCount(fileUriIntent);
+
+ if (fileUriActivitiesCount > 0) {
+ return new IntentAndResolvedActivitiesCount(fileUriIntent, fileUriActivitiesCount);
+ }
+
+ return new IntentAndResolvedActivitiesCount(contentUriIntent, contentUriActivitiesCount);
+ }
+
+ private Uri getDummyFileUri() {
File dummyFile = new File(FileHelper.sanitizeFilename(name));
- Uri fileUri = Uri.fromFile(dummyFile);
-
- Intent originalMimeTypeIntent = createViewIntentForFileUri(contentType, fileUri);
- int originalMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(originalMimeTypeIntent);
-
- String inferredMimeType = MimeUtility.getMimeTypeByExtension(name);
- if (inferredMimeType.equals(contentType)) {
- intent = originalMimeTypeIntent;
- activitiesCount = originalMimeTypeActivitiesCount;
- } else {
- Intent inferredMimeTypeIntent = createViewIntentForFileUri(inferredMimeType, fileUri);
- int inferredMimeTypeActivitiesCount = getResolvedIntentActivitiesCount(inferredMimeTypeIntent);
-
- if (inferredMimeTypeActivitiesCount > originalMimeTypeActivitiesCount) {
- intent = inferredMimeTypeIntent;
- activitiesCount = inferredMimeTypeActivitiesCount;
- } else {
- intent = originalMimeTypeIntent;
- activitiesCount = originalMimeTypeActivitiesCount;
- }
- }
-
- return new IntentAndResolvedActivitiesCount(intent, activitiesCount);
+ return Uri.fromFile(dummyFile);
}
- private Intent createViewIntentForContentUri(String mimeType, Uri uri) {
+ private Intent createViewIntentForAttachmentProviderUri(String mimeType) {
+ Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(), mimeType, name);
+
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -422,13 +401,17 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
return intent;
}
- public int getActivitiesCount() {
- return activitiesCount;
+ public boolean hasResolvedActivities() {
+ return activitiesCount > 0;
}
public String getMimeType() {
return intent.getType();
}
+
+ public boolean containsFileUri() {
+ return "file".equals(intent.getData().getScheme());
+ }
}
private class LoadAndDisplayThumbnailAsyncTask extends AsyncTask {
From 56c30095e2bd638b2860b37f7202965dde3adab9 Mon Sep 17 00:00:00 2001
From: cketti
Date: Tue, 18 Nov 2014 23:02:20 +0100
Subject: [PATCH 26/27] Don't use dummy file path when resolving intents
---
src/com/fsck/k9/cache/TemporaryAttachmentStore.java | 8 +++++++-
src/com/fsck/k9/view/AttachmentView.java | 12 ++++--------
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/src/com/fsck/k9/cache/TemporaryAttachmentStore.java b/src/com/fsck/k9/cache/TemporaryAttachmentStore.java
index c2a4a264a..098e919e9 100644
--- a/src/com/fsck/k9/cache/TemporaryAttachmentStore.java
+++ b/src/com/fsck/k9/cache/TemporaryAttachmentStore.java
@@ -15,7 +15,13 @@ public class TemporaryAttachmentStore {
private static String TEMPORARY_ATTACHMENT_DIRECTORY = "attachments";
private static long MAX_FILE_AGE = 12 * 60 * 60 * 1000; // 12h
- public static File getFile(Context context, String attachmentName) throws IOException {
+ public static File getFile(Context context, String attachmentName) {
+ File directory = getTemporaryAttachmentDirectory(context);
+ String filename = FileHelper.sanitizeFilename(attachmentName);
+ return new File(directory, filename);
+ }
+
+ public static File getFileForWriting(Context context, String attachmentName) throws IOException {
File directory = createOrCleanAttachmentDirectory(context);
String filename = FileHelper.sanitizeFilename(attachmentName);
return new File(directory, filename);
diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java
index e18884734..93966d9b4 100644
--- a/src/com/fsck/k9/view/AttachmentView.java
+++ b/src/com/fsck/k9/view/AttachmentView.java
@@ -286,7 +286,7 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
Intent viewIntent;
if (resolvedIntentInfo.hasResolvedActivities() && resolvedIntentInfo.containsFileUri()) {
try {
- File tempFile = TemporaryAttachmentStore.getFile(context, name);
+ File tempFile = TemporaryAttachmentStore.getFileForWriting(context, name);
writeAttachmentToStorage(tempFile);
viewIntent = createViewIntentForFileUri(resolvedIntentInfo.getMimeType(), Uri.fromFile(tempFile));
} catch (IOException e) {
@@ -310,8 +310,9 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
return new IntentAndResolvedActivitiesCount(contentUriIntent, contentUriActivitiesCount);
}
- Uri dummyFileUri = getDummyFileUri();
- Intent fileUriIntent = createViewIntentForFileUri(mimeType, dummyFileUri);
+ File tempFile = TemporaryAttachmentStore.getFile(context, name);
+ Uri tempFileUri = Uri.fromFile(tempFile);
+ Intent fileUriIntent = createViewIntentForFileUri(mimeType, tempFileUri);
int fileUriActivitiesCount = getResolvedIntentActivitiesCount(fileUriIntent);
if (fileUriActivitiesCount > 0) {
@@ -321,11 +322,6 @@ public class AttachmentView extends FrameLayout implements OnClickListener, OnLo
return new IntentAndResolvedActivitiesCount(contentUriIntent, contentUriActivitiesCount);
}
- private Uri getDummyFileUri() {
- File dummyFile = new File(FileHelper.sanitizeFilename(name));
- return Uri.fromFile(dummyFile);
- }
-
private Intent createViewIntentForAttachmentProviderUri(String mimeType) {
Uri uri = AttachmentProvider.getAttachmentUriForViewing(account, part.getAttachmentId(), mimeType, name);
From 552e552e88dc9f1385e556cfed11f465e35b3c88 Mon Sep 17 00:00:00 2001
From: Art O Cathain
Date: Tue, 11 Nov 2014 18:07:00 +0000
Subject: [PATCH 27/27] Add unit tests for FileHelper.sanitizeFilename()
---
src/com/fsck/k9/helper/FileHelper.java | 3 +-
.../com/fsck/k9/helper/FileHelperTest.java | 32 +++++++++++++++++++
2 files changed, 34 insertions(+), 1 deletion(-)
create mode 100644 tests/src/com/fsck/k9/helper/FileHelperTest.java
diff --git a/src/com/fsck/k9/helper/FileHelper.java b/src/com/fsck/k9/helper/FileHelper.java
index c33ed4cb9..e4f98c322 100644
--- a/src/com/fsck/k9/helper/FileHelper.java
+++ b/src/com/fsck/k9/helper/FileHelper.java
@@ -12,6 +12,7 @@ import com.fsck.k9.K9;
public class FileHelper {
+
/**
* Regular expression that represents characters we won't allow in file names.
*
@@ -27,7 +28,7 @@ public class FileHelper {
*
* @see #sanitizeFilename(String)
*/
- private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]+";
+ private static final String INVALID_CHARACTERS = "[^\\w !#$%&'()\\-@\\^`{}~.,]";
/**
* Invalid characters in a file name are replaced by this character.
diff --git a/tests/src/com/fsck/k9/helper/FileHelperTest.java b/tests/src/com/fsck/k9/helper/FileHelperTest.java
new file mode 100644
index 000000000..1e8260256
--- /dev/null
+++ b/tests/src/com/fsck/k9/helper/FileHelperTest.java
@@ -0,0 +1,32 @@
+package com.fsck.k9.helper;
+
+import junit.framework.TestCase;
+
+import java.lang.String;
+
+public class FileHelperTest extends TestCase {
+
+ public void testSanitize1() {
+ checkSanitization(".._bla_", "../bla_");
+ }
+
+ public void testSanitize2() {
+ checkSanitization("_etc_bla", "/etc/bla");
+ }
+
+ public void testSanitize3() {
+ checkSanitization("_пPп", "+пPп");
+ }
+
+ public void testSanitize4() {
+ checkSanitization(".東京_!", ".東京?!");
+ }
+
+ public void testSanitize5() {
+ checkSanitization("Plan 9", "Plan 9");
+ }
+
+ private void checkSanitization(String expected, String actual) {
+ assertEquals(expected, FileHelper.sanitizeFilename(actual));
+ }
+}