mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-23 18:02:15 -05:00
Merge branch 'pick_attachment_fix'
Update LocalStore code to handle the newly introduced temporary files for attachments Conflicts: res/values/strings.xml src/com/fsck/k9/activity/MessageCompose.java
This commit is contained in:
commit
2b7f5e7b70
@ -1,36 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
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">
|
||||
<ImageButton
|
||||
android:id="@+id/attachment_delete"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_height="42dip"
|
||||
android:layout_width="42dip" />
|
||||
<TextView
|
||||
android:id="@+id/attachment_name"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_width="1dip"
|
||||
android:layout_height="42dip"
|
||||
android:background="?attr/messageViewAttachmentBackground"
|
||||
android:paddingLeft="36dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="start"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:layout_marginRight="4dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_toLeftOf="@id/attachment_delete" />
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_email_attachment"
|
||||
android:layout_marginLeft="1dip"
|
||||
android:layout_centerVertical="true" />
|
||||
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">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/attachment_delete"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_height="42dip"
|
||||
android:layout_width="42dip" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="1dip"
|
||||
android:layout_height="42dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_toLeftOf="@id/attachment_delete"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:layout_marginRight="4dip"
|
||||
android:paddingLeft="36dip"
|
||||
android:gravity="center_vertical"
|
||||
android:background="?attr/messageViewAttachmentBackground"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attachment_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="start"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleSmall"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_email_attachment"
|
||||
android:layout_marginLeft="1dip"
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -1152,4 +1152,9 @@ Please submit bug reports, contribute new features and ask questions at
|
||||
|
||||
<string name="global_settings_messageview_visible_refile_actions_title">Visible message actions</string>
|
||||
<string name="global_settings_messageview_visible_refile_actions_summary">Show selected actions in the message view menu</string>
|
||||
|
||||
<string name="loading_attachment">Loading attachment…</string>
|
||||
<string name="fetching_attachment_dialog_title_send">Sending message</string>
|
||||
<string name="fetching_attachment_dialog_title_save">Saving draft</string>
|
||||
<string name="fetching_attachment_dialog_message">Fetching attachment…</string>
|
||||
</resources>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,10 +54,14 @@ 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;
|
||||
import com.fsck.k9.crypto.PgpData;
|
||||
import com.fsck.k9.fragment.ProgressDialogFragment;
|
||||
import com.fsck.k9.helper.ContactItem;
|
||||
import com.fsck.k9.helper.Contacts;
|
||||
import com.fsck.k9.helper.HtmlConverter;
|
||||
@ -79,7 +82,8 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentMessageBody;
|
||||
import com.fsck.k9.mail.store.LocalStore.TempFileBody;
|
||||
import com.fsck.k9.mail.store.LocalStore.TempFileMessageBody;
|
||||
import com.fsck.k9.view.MessageWebView;
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
@ -87,8 +91,6 @@ 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;
|
||||
@ -103,7 +105,9 @@ import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
ProgressDialogFragment.CancelListener {
|
||||
|
||||
private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
|
||||
private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2;
|
||||
private static final int DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY = 3;
|
||||
@ -147,12 +151,19 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
"com.fsck.k9.activity.MessageCompose.forcePlainText";
|
||||
private static final String STATE_KEY_QUOTED_TEXT_FORMAT =
|
||||
"com.fsck.k9.activity.MessageCompose.quotedTextFormat";
|
||||
private static final String STATE_KEY_NUM_ATTACHMENTS_LOADING = "numAttachmentsLoading";
|
||||
private static final String STATE_KEY_WAITING_FOR_ATTACHMENTS = "waitingForAttachments";
|
||||
|
||||
private static final String LOADER_ARG_ATTACHMENT = "attachment";
|
||||
|
||||
private static final String FRAGMENT_WAITING_FOR_ATTACHMENT = "waitingForAttachment";
|
||||
|
||||
private static final int MSG_PROGRESS_ON = 1;
|
||||
private static final int MSG_PROGRESS_OFF = 2;
|
||||
private static final int MSG_SKIPPED_ATTACHMENTS = 3;
|
||||
private static final int MSG_SAVED_DRAFT = 4;
|
||||
private static final int MSG_DISCARDED_DRAFT = 5;
|
||||
private static final int MSG_PERFORM_STALLED_ACTION = 6;
|
||||
|
||||
private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1;
|
||||
private static final int CONTACT_PICKER_TO = 4;
|
||||
@ -219,6 +230,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,
|
||||
@ -323,6 +335,23 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
*/
|
||||
private long mDraftId = INVALID_DRAFT_ID;
|
||||
|
||||
/**
|
||||
* Number of attachments currently being fetched.
|
||||
*/
|
||||
private int mNumAttachmentsLoading = 0;
|
||||
|
||||
private enum WaitingAction {
|
||||
NONE,
|
||||
SEND,
|
||||
SAVE
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies what action to perform once attachments have been fetched.
|
||||
*/
|
||||
private WaitingAction mWaitingForAttachments = WaitingAction.NONE;
|
||||
|
||||
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(android.os.Message msg) {
|
||||
@ -351,6 +380,9 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
getString(R.string.message_discarded_toast),
|
||||
Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case MSG_PERFORM_STALLED_ACTION:
|
||||
performStalledAction();
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
break;
|
||||
@ -366,14 +398,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.
|
||||
@ -1078,12 +1102,15 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
ArrayList<Uri> attachments = new ArrayList<Uri>();
|
||||
ArrayList<Attachment> attachments = new ArrayList<Attachment>();
|
||||
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.putInt(STATE_KEY_NUM_ATTACHMENTS_LOADING, mNumAttachmentsLoading);
|
||||
outState.putString(STATE_KEY_WAITING_FOR_ATTACHMENTS, mWaitingForAttachments.name());
|
||||
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);
|
||||
@ -1105,11 +1132,32 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
ArrayList<Parcelable> attachments = savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS);
|
||||
|
||||
mAttachments.removeAllViews();
|
||||
for (Parcelable p : attachments) {
|
||||
Uri uri = (Uri) p;
|
||||
addAttachment(uri);
|
||||
mMaxLoaderId = 0;
|
||||
|
||||
mNumAttachmentsLoading = savedInstanceState.getInt(STATE_KEY_NUM_ATTACHMENTS_LOADING);
|
||||
mWaitingForAttachments = WaitingAction.NONE;
|
||||
try {
|
||||
String waitingFor = savedInstanceState.getString(STATE_KEY_WAITING_FOR_ATTACHMENTS);
|
||||
mWaitingForAttachments = WaitingAction.valueOf(waitingFor);
|
||||
} catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Couldn't read value \" + STATE_KEY_WAITING_FOR_ATTACHMENTS +" +
|
||||
"\" from saved instance state", e);
|
||||
}
|
||||
|
||||
ArrayList<Attachment> 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
|
||||
@ -1472,15 +1520,19 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private void addAttachmentsToMessage(final MimeMultipart mp) throws MessagingException {
|
||||
LocalAttachmentBody body;
|
||||
Body body;
|
||||
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
|
||||
Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
|
||||
|
||||
if (attachment.state != Attachment.LoadingState.COMPLETE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String contentType = attachment.contentType;
|
||||
if (MimeUtil.isMessage(contentType)) {
|
||||
body = new LocalAttachmentMessageBody(attachment.uri,
|
||||
getApplication());
|
||||
body = new TempFileMessageBody(attachment.filename);
|
||||
} else {
|
||||
body = new LocalAttachmentBody(attachment.uri, getApplication());
|
||||
body = new TempFileBody(attachment.filename);
|
||||
}
|
||||
MimeBodyPart bp = new MimeBodyPart(body);
|
||||
|
||||
@ -1780,6 +1832,20 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWaitingForAttachments != WaitingAction.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNumAttachmentsLoading > 0) {
|
||||
mWaitingForAttachments = WaitingAction.SEND;
|
||||
showWaitingForAttachmentDialog();
|
||||
} else {
|
||||
performSend();
|
||||
}
|
||||
}
|
||||
|
||||
private void performSend() {
|
||||
final CryptoProvider crypto = mAccount.getCryptoProvider();
|
||||
if (mEncryptCheckbox.isChecked() && !mPgpData.hasEncryptionKeys()) {
|
||||
// key selection before encryption
|
||||
@ -1843,6 +1909,19 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
}
|
||||
|
||||
private void onSave() {
|
||||
if (mWaitingForAttachments != WaitingAction.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNumAttachmentsLoading > 0) {
|
||||
mWaitingForAttachments = WaitingAction.SAVE;
|
||||
showWaitingForAttachmentDialog();
|
||||
} else {
|
||||
performSend();
|
||||
}
|
||||
}
|
||||
|
||||
private void performSave() {
|
||||
saveIfNeeded();
|
||||
finish();
|
||||
}
|
||||
@ -1918,71 +1997,169 @@ 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<Attachment> mAttachmentInfoLoaderCallback =
|
||||
new LoaderManager.LoaderCallbacks<Attachment>() {
|
||||
@Override
|
||||
public Loader<Attachment> onCreateLoader(int id, Bundle args) {
|
||||
onFetchAttachmentStarted();
|
||||
Attachment attachment = args.getParcelable(LOADER_ARG_ATTACHMENT);
|
||||
return new AttachmentInfoLoader(MessageCompose.this, attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Attachment> 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);
|
||||
} else {
|
||||
onFetchAttachmentFinished();
|
||||
}
|
||||
|
||||
getSupportLoaderManager().destroyLoader(loaderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Attachment> loader) {
|
||||
onFetchAttachmentFinished();
|
||||
}
|
||||
};
|
||||
|
||||
private LoaderManager.LoaderCallbacks<Attachment> mAttachmentContentLoaderCallback =
|
||||
new LoaderManager.LoaderCallbacks<Attachment>() {
|
||||
@Override
|
||||
public Loader<Attachment> onCreateLoader(int id, Bundle args) {
|
||||
Attachment attachment = args.getParcelable(LOADER_ARG_ATTACHMENT);
|
||||
return new AttachmentContentLoader(MessageCompose.this, attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Attachment> 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);
|
||||
}
|
||||
}
|
||||
|
||||
onFetchAttachmentFinished();
|
||||
|
||||
getSupportLoaderManager().destroyLoader(loaderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Attachment> loader) {
|
||||
onFetchAttachmentFinished();
|
||||
}
|
||||
};
|
||||
|
||||
private void onFetchAttachmentStarted() {
|
||||
mNumAttachmentsLoading += 1;
|
||||
}
|
||||
|
||||
private void onFetchAttachmentFinished() {
|
||||
// We're not allowed to perform fragment transactions when called from onLoadFinished().
|
||||
// So we use the Handler to call performStalledAction().
|
||||
mHandler.sendEmptyMessage(MSG_PERFORM_STALLED_ACTION);
|
||||
}
|
||||
|
||||
private void performStalledAction() {
|
||||
mNumAttachmentsLoading -= 1;
|
||||
|
||||
WaitingAction waitingFor = mWaitingForAttachments;
|
||||
mWaitingForAttachments = WaitingAction.NONE;
|
||||
|
||||
if (waitingFor != WaitingAction.NONE) {
|
||||
dismissWaitingForAttachmentDialog();
|
||||
}
|
||||
|
||||
switch (waitingFor) {
|
||||
case SEND: {
|
||||
performSend();
|
||||
break;
|
||||
}
|
||||
case SAVE: {
|
||||
performSave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// if a CryptoSystem activity is returning, then mPreventDraftSaving was set to true
|
||||
@ -2316,6 +2493,45 @@ public class MessageCompose extends K9Activity implements OnClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void showWaitingForAttachmentDialog() {
|
||||
String title;
|
||||
|
||||
switch (mWaitingForAttachments) {
|
||||
case SEND: {
|
||||
title = getString(R.string.fetching_attachment_dialog_title_send);
|
||||
break;
|
||||
}
|
||||
case SAVE: {
|
||||
title = getString(R.string.fetching_attachment_dialog_title_save);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProgressDialogFragment fragment = ProgressDialogFragment.newInstance(title,
|
||||
getString(R.string.fetching_attachment_dialog_message));
|
||||
fragment.show(getSupportFragmentManager(), FRAGMENT_WAITING_FOR_ATTACHMENT);
|
||||
}
|
||||
|
||||
public void onCancel(ProgressDialogFragment fragment) {
|
||||
attachmentProgressDialogCancelled();
|
||||
}
|
||||
|
||||
void attachmentProgressDialogCancelled() {
|
||||
mWaitingForAttachments = WaitingAction.NONE;
|
||||
}
|
||||
|
||||
private void dismissWaitingForAttachmentDialog() {
|
||||
ProgressDialogFragment fragment = (ProgressDialogFragment)
|
||||
getSupportFragmentManager().findFragmentByTag(FRAGMENT_WAITING_FOR_ATTACHMENT);
|
||||
|
||||
if (fragment != null) {
|
||||
fragment.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
|
81
src/com/fsck/k9/activity/loader/AttachmentContentLoader.java
Normal file
81
src/com/fsck/k9/activity/loader/AttachmentContentLoader.java
Normal file
@ -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<Attachment> {
|
||||
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;
|
||||
}
|
||||
}
|
100
src/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
Normal file
100
src/com/fsck/k9/activity/loader/AttachmentInfoLoader.java
Normal file
@ -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<Attachment> {
|
||||
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;
|
||||
}
|
||||
}
|
129
src/com/fsck/k9/activity/misc/Attachment.java
Normal file
129
src/com/fsck/k9/activity/misc/Attachment.java
Normal file
@ -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<Attachment> CREATOR =
|
||||
new Parcelable.Creator<Attachment>() {
|
||||
@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();
|
||||
}
|
||||
}
|
@ -752,8 +752,8 @@ public class MessageViewFragment extends SherlockFragment implements OnClickList
|
||||
break;
|
||||
}
|
||||
case R.id.dialog_attachment_progress: {
|
||||
String title = getString(R.string.dialog_attachment_progress_title);
|
||||
fragment = ProgressDialogFragment.newInstance(title);
|
||||
String message = getString(R.string.dialog_attachment_progress_title);
|
||||
fragment = ProgressDialogFragment.newInstance(null, message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -2,19 +2,22 @@ package com.fsck.k9.fragment;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockDialogFragment;
|
||||
|
||||
|
||||
public class ProgressDialogFragment extends SherlockDialogFragment {
|
||||
private static final String ARG_TITLE = "title";
|
||||
protected static final String ARG_TITLE = "title";
|
||||
protected static final String ARG_MESSAGE = "message";
|
||||
|
||||
public static ProgressDialogFragment newInstance(String title) {
|
||||
public static ProgressDialogFragment newInstance(String title, String message) {
|
||||
ProgressDialogFragment fragment = new ProgressDialogFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_TITLE, title);
|
||||
args.putString(ARG_MESSAGE, message);
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
@ -25,11 +28,28 @@ public class ProgressDialogFragment extends SherlockDialogFragment {
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Bundle args = getArguments();
|
||||
String title = args.getString(ARG_TITLE);
|
||||
String message = args.getString(ARG_MESSAGE);
|
||||
|
||||
ProgressDialog dialog = new ProgressDialog(getActivity());
|
||||
dialog.setIndeterminate(true);
|
||||
dialog.setTitle(title);
|
||||
dialog.setMessage(message);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
CancelListener listener = (CancelListener) getActivity();
|
||||
if (listener != null && listener instanceof CancelListener) {
|
||||
listener.onCancel(this);
|
||||
}
|
||||
|
||||
super.onCancel(dialog);
|
||||
}
|
||||
|
||||
|
||||
public interface CancelListener {
|
||||
void onCancel(ProgressDialogFragment fragment);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -93,6 +94,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
|
||||
@ -4002,11 +4004,67 @@ public class LocalStore extends Store implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public static class LocalAttachmentBody implements Body {
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
public abstract static class BinaryAttachmentBody implements Body {
|
||||
protected String mEncoding;
|
||||
|
||||
@Override
|
||||
public abstract InputStream getInputStream() throws MessagingException;
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
InputStream in = getInputStream();
|
||||
try {
|
||||
boolean closeStream = false;
|
||||
if (MimeUtil.isBase64Encoding(mEncoding)) {
|
||||
out = new Base64OutputStream(out);
|
||||
closeStream = true;
|
||||
} else if (MimeUtil.isQuotedPrintableEncoded(mEncoding)){
|
||||
out = new QuotedPrintableOutputStream(out, false);
|
||||
closeStream = true;
|
||||
}
|
||||
|
||||
try {
|
||||
IOUtils.copy(in, out);
|
||||
} finally {
|
||||
if (closeStream) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return mEncoding;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TempFileBody extends BinaryAttachmentBody {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LocalAttachmentBody extends BinaryAttachmentBody {
|
||||
private Application mApplication;
|
||||
private Uri mUri;
|
||||
protected String mEncoding;
|
||||
|
||||
public LocalAttachmentBody(Uri uri, Application application) {
|
||||
mApplication = application;
|
||||
@ -4054,10 +4112,6 @@ public class LocalStore extends Store implements Serializable {
|
||||
public Uri getContentUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
mEncoding = encoding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4071,25 +4125,8 @@ public class LocalStore extends Store implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException,
|
||||
MessagingException {
|
||||
InputStream in = getInputStream();
|
||||
try {
|
||||
if (MimeUtil.ENC_7BIT.equalsIgnoreCase(mEncoding)) {
|
||||
/*
|
||||
* If we knew the message was already 7bit clean, then it
|
||||
* could be sent along without processing. But since we
|
||||
* don't know, we recursively parse it.
|
||||
*/
|
||||
MimeMessage message = new MimeMessage(in, true);
|
||||
message.setUsing7bitTransport();
|
||||
message.writeTo(out);
|
||||
} else {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
AttachmentMessageBodyUtil.writeTo(this, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -4114,6 +4151,56 @@ public class LocalStore extends Store implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public static class TempFileMessageBody extends TempFileBody implements CompositeBody {
|
||||
|
||||
public TempFileMessageBody(String filename) {
|
||||
super(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
AttachmentMessageBodyUtil.writeTo(this, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsing7bitTransport() throws MessagingException {
|
||||
// see LocalAttachmentMessageBody.setUsing7bitTransport()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||
throw new MessagingException(
|
||||
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||
}
|
||||
mEncoding = encoding;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AttachmentMessageBodyUtil {
|
||||
public static void writeTo(BinaryAttachmentBody body, OutputStream out) throws IOException,
|
||||
MessagingException {
|
||||
InputStream in = body.getInputStream();
|
||||
try {
|
||||
if (MimeUtil.ENC_7BIT.equalsIgnoreCase(body.getEncoding())) {
|
||||
/*
|
||||
* If we knew the message was already 7bit clean, then it
|
||||
* could be sent along without processing. But since we
|
||||
* don't know, we recursively parse it.
|
||||
*/
|
||||
MimeMessage message = new MimeMessage(in, true);
|
||||
message.setUsing7bitTransport();
|
||||
message.writeTo(out);
|
||||
} else {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ThreadInfo {
|
||||
public final long threadId;
|
||||
public final long msgId;
|
||||
|
Loading…
Reference in New Issue
Block a user