diff --git a/res/layout/message_compose_attachment.xml b/res/layout/message_compose_attachment.xml
index 263d5a13c..60546e184 100644
--- a/res/layout/message_compose_attachment.xml
+++ b/res/layout/message_compose_attachment.xml
@@ -1,36 +1,55 @@
-
-
-
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="54dip"
+ android:paddingRight="6dip"
+ android:paddingTop="6dip"
+ android:paddingBottom="6dip">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b306f4420..fd4bd3f0f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1147,4 +1147,6 @@ Please submit bug reports, contribute new features and ask questions at
on %s
Mark all as read
+
+ Loading attachment…
diff --git a/src/com/fsck/k9/activity/K9Activity.java b/src/com/fsck/k9/activity/K9Activity.java
index 64083758d..82ffc3835 100644
--- a/src/com/fsck/k9/activity/K9Activity.java
+++ b/src/com/fsck/k9/activity/K9Activity.java
@@ -3,12 +3,12 @@ package com.fsck.k9.activity;
import android.os.Bundle;
import android.view.MotionEvent;
-import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.fsck.k9.activity.K9ActivityCommon.K9ActivityMagic;
import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener;
-public class K9Activity extends SherlockActivity implements K9ActivityMagic {
+public class K9Activity extends SherlockFragmentActivity implements K9ActivityMagic {
private K9ActivityCommon mBase;
diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java
index 5d9d3759a..999bb6947 100644
--- a/src/com/fsck/k9/activity/MessageCompose.java
+++ b/src/com/fsck/k9/activity/MessageCompose.java
@@ -5,19 +5,18 @@ import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
-import android.provider.OpenableColumns;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
import android.text.TextWatcher;
import android.text.util.Rfc822Tokenizer;
import android.util.Log;
@@ -55,6 +54,9 @@ import com.fsck.k9.Identity;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
+import com.fsck.k9.activity.loader.AttachmentContentLoader;
+import com.fsck.k9.activity.loader.AttachmentInfoLoader;
+import com.fsck.k9.activity.misc.Attachment;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.crypto.CryptoProvider;
@@ -86,14 +88,13 @@ import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.SimpleHtmlSerializer;
import org.htmlcleaner.TagNode;
-import java.io.File;
-import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -147,6 +148,8 @@ public class MessageCompose extends K9Activity implements OnClickListener {
private static final String STATE_KEY_QUOTED_TEXT_FORMAT =
"com.fsck.k9.activity.MessageCompose.quotedTextFormat";
+ private static final String LOADER_ARG_ATTACHMENT = "attachment";
+
private static final int MSG_PROGRESS_ON = 1;
private static final int MSG_PROGRESS_OFF = 2;
private static final int MSG_SKIPPED_ATTACHMENTS = 3;
@@ -218,6 +221,7 @@ public class MessageCompose extends K9Activity implements OnClickListener {
* have already been added from the restore of the view state.
*/
private boolean mSourceMessageProcessed = false;
+ private int mMaxLoaderId = 0;
enum Action {
COMPOSE,
@@ -365,14 +369,6 @@ public class MessageCompose extends K9Activity implements OnClickListener {
private ContextThemeWrapper mThemeContext;
- static class Attachment implements Serializable {
- private static final long serialVersionUID = 3642382876618963734L;
- public String name;
- public String contentType;
- public long size;
- public Uri uri;
- }
-
/**
* Compose a new message using the given account. If account is null the default account
* will be used.
@@ -1077,12 +1073,13 @@ public class MessageCompose extends K9Activity implements OnClickListener {
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- ArrayList attachments = new ArrayList();
+ ArrayList attachments = new ArrayList();
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
View view = mAttachments.getChildAt(i);
Attachment attachment = (Attachment) view.getTag();
- attachments.add(attachment.uri);
+ attachments.add(attachment);
}
+
outState.putParcelableArrayList(STATE_KEY_ATTACHMENTS, attachments);
outState.putBoolean(STATE_KEY_CC_SHOWN, mCcWrapper.getVisibility() == View.VISIBLE);
outState.putBoolean(STATE_KEY_BCC_SHOWN, mBccWrapper.getVisibility() == View.VISIBLE);
@@ -1104,11 +1101,22 @@ public class MessageCompose extends K9Activity implements OnClickListener {
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
- ArrayList attachments = savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS);
+
mAttachments.removeAllViews();
- for (Parcelable p : attachments) {
- Uri uri = (Uri) p;
- addAttachment(uri);
+ mMaxLoaderId = 0;
+
+ ArrayList attachments = savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS);
+ for (Attachment attachment : attachments) {
+ addAttachmentView(attachment);
+ if (attachment.loaderId > mMaxLoaderId) {
+ mMaxLoaderId = attachment.loaderId;
+ }
+
+ if (attachment.state == Attachment.LoadingState.URI_ONLY) {
+ initAttachmentInfoLoader(attachment);
+ } else if (attachment.state == Attachment.LoadingState.METADATA) {
+ initAttachmentContentLoader(attachment);
+ }
}
mReadReceipt = savedInstanceState
@@ -1474,8 +1482,11 @@ public class MessageCompose extends K9Activity implements OnClickListener {
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
- MimeBodyPart bp = new MimeBodyPart(
- new LocalStore.LocalAttachmentBody(attachment.uri, getApplication()));
+ if (attachment.state != Attachment.LoadingState.COMPLETE) {
+ continue;
+ }
+
+ MimeBodyPart bp = new MimeBodyPart(new LocalStore.TempFileBody(attachment.filename));
/*
* Correctly encode the filename here. Otherwise the whole
@@ -1911,71 +1922,131 @@ public class MessageCompose extends K9Activity implements OnClickListener {
}
private void addAttachment(Uri uri, String contentType) {
- long size = -1;
- String name = null;
-
- ContentResolver contentResolver = getContentResolver();
-
- Cursor metadataCursor = contentResolver.query(
- uri,
- new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE },
- null,
- null,
- null);
-
- if (metadataCursor != null) {
- try {
- if (metadataCursor.moveToFirst()) {
- name = metadataCursor.getString(0);
- size = metadataCursor.getInt(1);
- }
- } finally {
- metadataCursor.close();
- }
- }
-
- if (name == null) {
- name = uri.getLastPathSegment();
- }
-
- String usableContentType = contentType;
- if ((usableContentType == null) || (usableContentType.indexOf('*') != -1)) {
- usableContentType = contentResolver.getType(uri);
- }
- if (usableContentType == null) {
- usableContentType = MimeUtility.getMimeTypeByExtension(name);
- }
-
- if (size <= 0) {
- String uriString = uri.toString();
- if (uriString.startsWith("file://")) {
- Log.v(K9.LOG_TAG, uriString.substring("file://".length()));
- File f = new File(uriString.substring("file://".length()));
- size = f.length();
- } else {
- Log.v(K9.LOG_TAG, "Not a file: " + uriString);
- }
- } else {
- Log.v(K9.LOG_TAG, "old attachment.size: " + size);
- }
- Log.v(K9.LOG_TAG, "new attachment.size: " + size);
-
Attachment attachment = new Attachment();
+ attachment.state = Attachment.LoadingState.URI_ONLY;
attachment.uri = uri;
- attachment.contentType = usableContentType;
- attachment.name = name;
- attachment.size = size;
+ attachment.contentType = contentType;
+ attachment.loaderId = ++mMaxLoaderId;
+
+ addAttachmentView(attachment);
+
+ initAttachmentInfoLoader(attachment);
+ }
+
+ private void initAttachmentInfoLoader(Attachment attachment) {
+ LoaderManager loaderManager = getSupportLoaderManager();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(LOADER_ARG_ATTACHMENT, attachment);
+ loaderManager.initLoader(attachment.loaderId, bundle, mAttachmentInfoLoaderCallback);
+ }
+
+ private void initAttachmentContentLoader(Attachment attachment) {
+ LoaderManager loaderManager = getSupportLoaderManager();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(LOADER_ARG_ATTACHMENT, attachment);
+ loaderManager.initLoader(attachment.loaderId, bundle, mAttachmentContentLoaderCallback);
+ }
+
+ private void addAttachmentView(Attachment attachment) {
+ boolean hasMetadata = (attachment.state != Attachment.LoadingState.URI_ONLY);
+ boolean isLoadingComplete = (attachment.state == Attachment.LoadingState.COMPLETE);
View view = getLayoutInflater().inflate(R.layout.message_compose_attachment, mAttachments, false);
- TextView nameView = (TextView)view.findViewById(R.id.attachment_name);
- ImageButton delete = (ImageButton)view.findViewById(R.id.attachment_delete);
- nameView.setText(attachment.name);
- delete.setOnClickListener(this);
+ TextView nameView = (TextView) view.findViewById(R.id.attachment_name);
+ View progressBar = view.findViewById(R.id.progressBar);
+
+ if (hasMetadata) {
+ nameView.setText(attachment.name);
+ } else {
+ nameView.setText(R.string.loading_attachment);
+ }
+
+ progressBar.setVisibility(isLoadingComplete ? View.GONE : View.VISIBLE);
+
+ ImageButton delete = (ImageButton) view.findViewById(R.id.attachment_delete);
+ delete.setOnClickListener(MessageCompose.this);
delete.setTag(view);
+
view.setTag(attachment);
mAttachments.addView(view);
}
+ private View getAttachmentView(int loaderId) {
+ for (int i = 0, childCount = mAttachments.getChildCount(); i < childCount; i++) {
+ View view = mAttachments.getChildAt(i);
+ Attachment tag = (Attachment) view.getTag();
+ if (tag != null && tag.loaderId == loaderId) {
+ return view;
+ }
+ }
+
+ return null;
+ }
+
+ private LoaderManager.LoaderCallbacks mAttachmentInfoLoaderCallback =
+ new LoaderManager.LoaderCallbacks() {
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ Attachment attachment = args.getParcelable(LOADER_ARG_ATTACHMENT);
+ return new AttachmentInfoLoader(MessageCompose.this, attachment);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Attachment attachment) {
+ int loaderId = loader.getId();
+
+ View view = getAttachmentView(loaderId);
+ if (view != null) {
+ view.setTag(attachment);
+
+ TextView nameView = (TextView) view.findViewById(R.id.attachment_name);
+ nameView.setText(attachment.name);
+
+ attachment.loaderId = ++mMaxLoaderId;
+ initAttachmentContentLoader(attachment);
+ }
+
+ getSupportLoaderManager().destroyLoader(loaderId);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ }
+ };
+
+ private LoaderManager.LoaderCallbacks mAttachmentContentLoaderCallback =
+ new LoaderManager.LoaderCallbacks() {
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ Attachment attachment = args.getParcelable(LOADER_ARG_ATTACHMENT);
+ return new AttachmentContentLoader(MessageCompose.this, attachment);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Attachment attachment) {
+ int loaderId = loader.getId();
+
+ View view = getAttachmentView(loaderId);
+ if (view != null) {
+ if (attachment.state == Attachment.LoadingState.COMPLETE) {
+ view.setTag(attachment);
+
+ View progressBar = view.findViewById(R.id.progressBar);
+ progressBar.setVisibility(View.GONE);
+ } else {
+ mAttachments.removeView(view);
+ }
+ }
+
+ getSupportLoaderManager().destroyLoader(loaderId);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ }
+ };
+
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// if a CryptoSystem activity is returning, then mPreventDraftSaving was set to true
diff --git a/src/com/fsck/k9/activity/loader/AttachmentContentLoader.java b/src/com/fsck/k9/activity/loader/AttachmentContentLoader.java
new file mode 100644
index 000000000..7d677ecbc
--- /dev/null
+++ b/src/com/fsck/k9/activity/loader/AttachmentContentLoader.java
@@ -0,0 +1,81 @@
+package com.fsck.k9.activity.loader;
+
+import android.content.Context;
+import android.support.v4.content.AsyncTaskLoader;
+import android.util.Log;
+
+import com.fsck.k9.K9;
+import com.fsck.k9.activity.misc.Attachment;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Loader to fetch the content of an attachment.
+ *
+ * This will copy the data to a temporary file in our app's cache directory.
+ */
+public class AttachmentContentLoader extends AsyncTaskLoader {
+ private static final String FILENAME_PREFIX = "attachment";
+
+ private final Attachment mAttachment;
+
+ public AttachmentContentLoader(Context context, Attachment attachment) {
+ super(context);
+ mAttachment = attachment;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mAttachment.state == Attachment.LoadingState.COMPLETE) {
+ deliverResult(mAttachment);
+ }
+
+ if (takeContentChanged() || mAttachment.state == Attachment.LoadingState.METADATA) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ public Attachment loadInBackground() {
+ Context context = getContext();
+
+ try {
+ File file = File.createTempFile(FILENAME_PREFIX, null, context.getCacheDir());
+ file.deleteOnExit();
+
+ if (K9.DEBUG) {
+ Log.v(K9.LOG_TAG, "Saving attachment to " + file.getAbsolutePath());
+ }
+
+ InputStream in = context.getContentResolver().openInputStream(mAttachment.uri);
+ try {
+ FileOutputStream out = new FileOutputStream(file);
+ try {
+ IOUtils.copy(in, out);
+ } finally {
+ out.close();
+ }
+ } finally {
+ in.close();
+ }
+
+ mAttachment.filename = file.getAbsolutePath();
+ mAttachment.state = Attachment.LoadingState.COMPLETE;
+
+ return mAttachment;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ mAttachment.filename = null;
+ mAttachment.state = Attachment.LoadingState.CANCELLED;
+
+ return mAttachment;
+ }
+}
diff --git a/src/com/fsck/k9/activity/loader/AttachmentInfoLoader.java b/src/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
new file mode 100644
index 000000000..74c874ae8
--- /dev/null
+++ b/src/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
@@ -0,0 +1,100 @@
+package com.fsck.k9.activity.loader;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.OpenableColumns;
+import android.support.v4.content.AsyncTaskLoader;
+import android.util.Log;
+
+import com.fsck.k9.K9;
+import com.fsck.k9.activity.misc.Attachment;
+import com.fsck.k9.mail.internet.MimeUtility;
+
+import java.io.File;
+
+/**
+ * Loader to fetch metadata of an attachment.
+ */
+public class AttachmentInfoLoader extends AsyncTaskLoader {
+ private final Attachment mAttachment;
+
+ public AttachmentInfoLoader(Context context, Attachment attachment) {
+ super(context);
+ mAttachment = attachment;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mAttachment.state == Attachment.LoadingState.METADATA) {
+ deliverResult(mAttachment);
+ }
+
+ if (takeContentChanged() || mAttachment.state == Attachment.LoadingState.URI_ONLY) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ public Attachment loadInBackground() {
+ Uri uri = mAttachment.uri;
+ String contentType = mAttachment.contentType;
+
+ long size = -1;
+ String name = null;
+
+ ContentResolver contentResolver = getContext().getContentResolver();
+
+ Cursor metadataCursor = contentResolver.query(
+ uri,
+ new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE },
+ null,
+ null,
+ null);
+
+ if (metadataCursor != null) {
+ try {
+ if (metadataCursor.moveToFirst()) {
+ name = metadataCursor.getString(0);
+ size = metadataCursor.getInt(1);
+ }
+ } finally {
+ metadataCursor.close();
+ }
+ }
+
+ if (name == null) {
+ name = uri.getLastPathSegment();
+ }
+
+ String usableContentType = contentType;
+ if ((usableContentType == null) || (usableContentType.indexOf('*') != -1)) {
+ usableContentType = contentResolver.getType(uri);
+ }
+ if (usableContentType == null) {
+ usableContentType = MimeUtility.getMimeTypeByExtension(name);
+ }
+
+ if (size <= 0) {
+ String uriString = uri.toString();
+ if (uriString.startsWith("file://")) {
+ Log.v(K9.LOG_TAG, uriString.substring("file://".length()));
+ File f = new File(uriString.substring("file://".length()));
+ size = f.length();
+ } else {
+ Log.v(K9.LOG_TAG, "Not a file: " + uriString);
+ }
+ } else {
+ Log.v(K9.LOG_TAG, "old attachment.size: " + size);
+ }
+ Log.v(K9.LOG_TAG, "new attachment.size: " + size);
+
+ mAttachment.contentType = usableContentType;
+ mAttachment.name = name;
+ mAttachment.size = size;
+ mAttachment.state = Attachment.LoadingState.METADATA;
+
+ return mAttachment;
+ }
+}
diff --git a/src/com/fsck/k9/activity/misc/Attachment.java b/src/com/fsck/k9/activity/misc/Attachment.java
new file mode 100644
index 000000000..e64165783
--- /dev/null
+++ b/src/com/fsck/k9/activity/misc/Attachment.java
@@ -0,0 +1,129 @@
+package com.fsck.k9.activity.misc;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container class for information about an attachment.
+ *
+ * This is used by {@link com.fsck.k9.activity.MessageCompose} to fetch and manage attachments.
+ */
+public class Attachment implements Parcelable {
+ /**
+ * The URI pointing to the source of the attachment.
+ *
+ * In most cases this will be a {@code content://}-URI.
+ */
+ public Uri uri;
+
+ /**
+ * The current loading state.
+ */
+ public LoadingState state;
+
+ /**
+ * The ID of the loader that is used to load the metadata or contents.
+ */
+ public int loaderId;
+
+ /**
+ * The content type of the attachment.
+ *
+ * Only valid when {@link #state} is {@link LoadingState#METADATA} or
+ * {@link LoadingState#COMPLETE}.
+ */
+ public String contentType;
+
+ /**
+ * The (file)name of the attachment.
+ *
+ * Only valid when {@link #state} is {@link LoadingState#METADATA} or
+ * {@link LoadingState#COMPLETE}.
+ */
+ public String name;
+
+ /**
+ * The size of the attachment.
+ *
+ * Only valid when {@link #state} is {@link LoadingState#METADATA} or
+ * {@link LoadingState#COMPLETE}.
+ */
+ public long size;
+
+ /**
+ * The name of the temporary file containing the local copy of the attachment.
+ *
+ * Only valid when {@link #state} is {@link LoadingState#COMPLETE}.
+ */
+ public String filename;
+
+
+ public Attachment() {}
+
+ public static enum LoadingState {
+ /**
+ * The only thing we know about this attachment is {@link #uri}.
+ */
+ URI_ONLY,
+
+ /**
+ * The metadata of this attachment have been loaded.
+ *
+ * {@link #contentType}, {@link #name}, and {@link #size} should contain usable values.
+ */
+ METADATA,
+
+ /**
+ * The contents of the attachments have been copied to the temporary file {@link #filename}.
+ */
+ COMPLETE,
+
+ /**
+ * Something went wrong while trying to fetch the attachment's contents.
+ */
+ CANCELLED
+ }
+
+
+ // === Parcelable ===
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(uri, flags);
+ dest.writeSerializable(state);
+ dest.writeInt(loaderId);
+ dest.writeString(contentType);
+ dest.writeString(name);
+ dest.writeLong(size);
+ dest.writeString(filename);
+ }
+
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ @Override
+ public Attachment createFromParcel(Parcel in) {
+ return new Attachment(in);
+ }
+
+ @Override
+ public Attachment[] newArray(int size) {
+ return new Attachment[size];
+ }
+ };
+
+ public Attachment(Parcel in) {
+ uri = in.readParcelable(Uri.class.getClassLoader());
+ state = (LoadingState) in.readSerializable();
+ loaderId = in.readInt();
+ contentType = in.readString();
+ name = in.readString();
+ size = in.readLong();
+ filename = in.readString();
+ }
+}
diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java
index 5575499a7..de1c4272e 100644
--- a/src/com/fsck/k9/mail/store/LocalStore.java
+++ b/src/com/fsck/k9/mail/store/LocalStore.java
@@ -3,6 +3,7 @@ package com.fsck.k9.mail.store;
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -90,6 +91,7 @@ public class LocalStore extends Store implements Serializable {
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/*
* a String containing the columns getMessages expects to work with
@@ -3984,8 +3986,39 @@ public class LocalStore extends Store implements Serializable {
}
}
+ public static class TempFileBody implements Body {
+ private final File mFile;
+
+ public TempFileBody(String filename) {
+ mFile = new File(filename);
+ }
+
+ @Override
+ public InputStream getInputStream() throws MessagingException {
+ try {
+ return new FileInputStream(mFile);
+ } catch (FileNotFoundException e) {
+ return new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
+ }
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException, MessagingException {
+ InputStream in = getInputStream();
+ try {
+ Base64OutputStream base64Out = new Base64OutputStream(out);
+ try {
+ IOUtils.copy(in, base64Out);
+ } finally {
+ base64Out.close();
+ }
+ } finally {
+ in.close();
+ }
+ }
+ }
+
public static class LocalAttachmentBody implements Body {
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private Application mApplication;
private Uri mUri;