1
0
mirror of https://github.com/moparisthebest/k-9 synced 2025-02-17 07:30:16 -05:00
This commit is contained in:
unknown 2011-02-12 16:24:56 -05:00
commit 42edb24c4b
14 changed files with 617 additions and 453 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="13001" android:versionCode="13002"
android:versionName="3.701" package="com.fsck.k9" android:versionName="3.702" package="com.fsck.k9"
> >
<uses-sdk <uses-sdk
android:minSdkVersion="3" android:minSdkVersion="3"

View File

@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <com.fsck.k9.view.SingleMessageView
android:id="@+id/message_view"
android:orientation="vertical"
android:layout_width="fill_parent"
android:background="@android:color/transparent"
android:layout_height="wrap_content"
android:layout_weight="1"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Header area --> <!-- Header area -->
<com.fsck.k9.view.MessageHeader <com.fsck.k9.view.MessageHeader
android:id="@+id/header_container" android:id="@+id/header_container"
@ -216,4 +223,4 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone" android:visibility="gone"
android:layout_width="fill_parent" /> android:layout_width="fill_parent" />
</merge> </com.fsck.k9.view.SingleMessageView>

View File

@ -18,13 +18,9 @@
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:layout_height="0dip" android:layout_height="wrap_content"
android:layout_weight="1"> android:layout_weight="1">
<include layout="@layout/message_view_header" /> <include layout="@layout/message" />
<View
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
<include layout="@layout/message_view_scrolling_buttons"/> <include layout="@layout/message_view_scrolling_buttons"/>
</LinearLayout> </LinearLayout>
</com.fsck.k9.view.ToggleScrollView> </com.fsck.k9.view.ToggleScrollView>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <com.fsck.k9.view.MessageCryptoView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_decrypt" android:id="@+id/layout_decrypt"
android:orientation="horizontal" android:orientation="horizontal"
@ -55,4 +55,4 @@
android:layout_width="0dip" android:layout_width="0dip"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"/> android:layout_weight="1"/>
</LinearLayout> </com.fsck.k9.view.MessageCryptoView>

View File

@ -1,88 +1,88 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout <LinearLayout
android:id="@+id/extra_buttons" android:id="@+id/extra_buttons"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dip"
android:gravity="center_vertical">
<Button
android:id="@+id/reply"
android:text="@string/reply_action"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="2dip" android:layout_width="0dip"
android:gravity="center_vertical"> android:layout_weight="1"/>
<Button <Button
android:id="@+id/reply" android:id="@+id/reply_all"
android:text="@string/reply_action" android:text="@string/reply_all_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
<Button
android:id="@+id/reply_all"
android:text="@string/reply_all_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
<Button
android:id="@+id/forward"
android:text="@string/forward_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:id="@+id/scrolling_move_buttons"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="2dip" android:layout_width="0dip"
android:gravity="center_vertical"> android:layout_weight="1"/>
<Button <Button
android:id="@+id/archive_scrolling" android:id="@+id/forward"
android:text="@string/message_view_archive_action" android:text="@string/forward_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
<Button
android:id="@+id/move_scrolling"
android:text="@string/message_view_move_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
<Button
android:id="@+id/spam_scrolling"
android:text="@string/message_view_spam_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:id="@+id/scrolling_buttons"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical"> android:layout_width="0dip"
<Button android:layout_weight="1"/>
android:id="@+id/previous_scrolling" </LinearLayout>
android:text="@string/message_view_prev_action" <LinearLayout
android:contentDescription="@string/previous_action" android:id="@+id/scrolling_move_buttons"
android:textSize="35dip" android:orientation="horizontal"
android:padding="0dip" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="0dip" android:paddingBottom="2dip"
android:layout_weight="1" /> android:gravity="center_vertical">
<Button
android:id="@+id/archive_scrolling"
android:text="@string/message_view_archive_action"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"/>
<ImageButton <Button
android:id="@+id/delete_scrolling" android:id="@+id/move_scrolling"
android:layout_height="wrap_content" android:text="@string/message_view_move_action"
android:src="@drawable/ic_button_delete" android:layout_height="wrap_content"
android:layout_width="0dip" android:layout_width="0dip"
android:layout_weight="1" /> android:layout_weight="1"/>
<Button
android:id="@+id/next_scrolling" <Button
android:padding="0dip" android:id="@+id/spam_scrolling"
android:text="@string/message_view_next_action" android:text="@string/message_view_spam_action"
android:textSize="35dip" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_width="0dip"
android:layout_width="0dip" android:layout_weight="1"/>
android:layout_weight="1" /> </LinearLayout>
</LinearLayout> <LinearLayout
android:id="@+id/scrolling_buttons"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<Button
android:id="@+id/previous_scrolling"
android:text="@string/message_view_prev_action"
android:contentDescription="@string/previous_action"
android:textSize="35dip"
android:padding="0dip"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"/>
<ImageButton
android:id="@+id/delete_scrolling"
android:layout_height="wrap_content"
android:src="@drawable/ic_button_delete"
android:layout_width="0dip"
android:layout_weight="1"/>
<Button
android:id="@+id/next_scrolling"
android:padding="0dip"
android:text="@string/message_view_next_action"
android:textSize="35dip"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1"/>
</LinearLayout>
</merge> </merge>

View File

@ -683,6 +683,8 @@ public class Account implements BaseAccount {
switchLocalStorage(id); switchLocalStorage(id);
successful = true; successful = true;
} catch (MessagingException e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Switching local storage provider from " +
mLocalStorageProviderId + " to " + id + " failed.", e);
} finally { } finally {
// if migration to/from SD-card failed once, it will fail again. // if migration to/from SD-card failed once, it will fail again.
if (!successful) { if (!successful) {

View File

@ -299,6 +299,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
finish(); finish();
} else if (startup && accounts.length == 1 && onOpenAccount(accounts[0])) { } else if (startup && accounts.length == 1 && onOpenAccount(accounts[0])) {
// fall through to "else" if !onOpenAccount() // fall through to "else" if !onOpenAccount()
finish();
} else { } else {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
requestWindowFeature(Window.FEATURE_PROGRESS); requestWindowFeature(Window.FEATURE_PROGRESS);

View File

@ -20,20 +20,15 @@ import android.widget.*;
import com.fsck.k9.*; import com.fsck.k9.*;
import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.crypto.CryptoProvider;
import com.fsck.k9.crypto.PgpData; import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.Contacts;
import com.fsck.k9.helper.Utility; import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.*; import com.fsck.k9.mail.*;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart;
import com.fsck.k9.mail.store.LocalStore.LocalMessage; import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.view.AccessibleWebView;
import com.fsck.k9.view.AttachmentView; import com.fsck.k9.view.AttachmentView;
import com.fsck.k9.view.MessageWebView;
import com.fsck.k9.view.ToggleScrollView; import com.fsck.k9.view.ToggleScrollView;
import com.fsck.k9.view.MessageHeader; import com.fsck.k9.view.SingleMessageView;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
@ -46,22 +41,15 @@ public class MessageView extends K9Activity implements OnClickListener {
private static final String STATE_PGP_DATA = "pgpData"; private static final String STATE_PGP_DATA = "pgpData";
private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1; private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2; private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
private View mDecryptLayout;
private Button mDecryptButton;
private LinearLayout mCryptoSignatureLayout = null; private SingleMessageView mMessageView;
private ImageView mCryptoSignatureStatusImage = null;
private TextView mCryptoSignatureUserId = null; private PgpData mPgpData;
private TextView mCryptoSignatureUserIdRest = null;
private MessageWebView mMessageContentView;
private boolean mScreenReaderEnabled; private View mNext;
private AccessibleWebView mAccessibleMessageContentView; private View mPrevious;
private MessageHeader mHeaderContainer;
private LinearLayout mAttachments;
private View mShowPicturesSection;
private boolean mShowPictures;
private Button mDownloadRemainder;
View next;
View previous;
private View mDelete; private View mDelete;
private View mArchive; private View mArchive;
private View mMove; private View mMove;
@ -71,14 +59,12 @@ public class MessageView extends K9Activity implements OnClickListener {
private MessageReference mMessageReference; private MessageReference mMessageReference;
private ArrayList<MessageReference> mMessageReferences; private ArrayList<MessageReference> mMessageReferences;
private Message mMessage; private Message mMessage;
private PgpData mPgpData = null;
private static final int PREVIOUS = 1; private static final int PREVIOUS = 1;
private static final int NEXT = 2; private static final int NEXT = 2;
private int mLastDirection = PREVIOUS; private int mLastDirection = PREVIOUS;
private MessagingController mController = MessagingController.getInstance(getApplication()); private MessagingController mController = MessagingController.getInstance(getApplication());
private MessageReference mNextMessage = null; private MessageReference mNextMessage = null;
private MessageReference mPreviousMessage = null; private MessageReference mPreviousMessage = null;
private Menu optionsMenu = null;
private Listener mListener = new Listener(); private Listener mListener = new Listener();
private MessageViewHandler mHandler = new MessageViewHandler(); private MessageViewHandler mHandler = new MessageViewHandler();
private Contacts mContacts; private Contacts mContacts;
@ -209,15 +195,7 @@ public class MessageView extends K9Activity implements OnClickListener {
case KeyEvent.KEYCODE_Z: { case KeyEvent.KEYCODE_Z: {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
public void run() { public void run() {
if (mScreenReaderEnabled) { mMessageView.zoom(event);
mAccessibleMessageContentView.zoomIn();
} else {
if (event.isShiftPressed()) {
mMessageContentView.zoomIn();
} else {
mMessageContentView.zoomOut();
}
}
} }
}); });
return true; return true;
@ -245,31 +223,6 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
class MessageViewHandler extends Handler { class MessageViewHandler extends Handler {
public void setHeaders(final Message message) {
runOnUiThread(new Runnable() {
public void run() {
try {
mHeaderContainer.populate(message, mAccount);
mHeaderContainer.setOnFlagListener(new OnClickListener() {
@Override public void onClick(View v) {
if (mMessage != null) {
onFlag();
}
}
});
} catch (Exception me) {
Log.e(K9.LOG_TAG, "setHeaders - error", me);
}
if (mMessage.isSet(Flag.X_DOWNLOADED_FULL)) {
mDownloadRemainder.setVisibility(View.GONE);
} else {
mDownloadRemainder.setEnabled(true);
mDownloadRemainder.setVisibility(View.VISIBLE);
}
}
});
}
public void progress(final boolean progress) { public void progress(final boolean progress) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@ -282,8 +235,7 @@ public class MessageView extends K9Activity implements OnClickListener {
public void addAttachment(final View attachmentView) { public void addAttachment(final View attachmentView) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
mAttachments.addView(attachmentView); mMessageView.addAttachment(attachmentView);
mAttachments.setVisibility(View.VISIBLE);
} }
}); });
} }
@ -291,32 +243,16 @@ public class MessageView extends K9Activity implements OnClickListener {
public void removeAllAttachments() { public void removeAllAttachments() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) { mMessageView.removeAllAttachments();
mAttachments.removeView(mAttachments.getChildAt(i));
}
} }
}); }
);
} }
public void setAttachmentsEnabled(final boolean enabled) {
runOnUiThread(new Runnable() {
public void run() {
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
AttachmentView attachment = (AttachmentView) mAttachments.getChildAt(i);
attachment.viewButton.setEnabled(enabled);
attachment.downloadButton.setEnabled(enabled);
}
}
});
}
public void networkError() { public void networkError() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
Toast.makeText(MessageView.this, Toast.makeText(MessageView.this, R.string.status_network_error, Toast.LENGTH_LONG).show();
R.string.status_network_error, Toast.LENGTH_LONG).show();
} }
}); });
} }
@ -324,8 +260,7 @@ public class MessageView extends K9Activity implements OnClickListener {
public void invalidIdError() { public void invalidIdError() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
Toast.makeText(MessageView.this, Toast.makeText(MessageView.this, R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
} }
}); });
} }
@ -344,13 +279,22 @@ public class MessageView extends K9Activity implements OnClickListener {
public void showShowPictures(final boolean show) { public void showShowPictures(final boolean show) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
mShowPicturesSection.setVisibility(show ? View.VISIBLE : View.GONE); mMessageView.showShowPicturesSection(show);
}
});
}
public void setHeaders(final Message message, final Account account) {
runOnUiThread(new Runnable() {
public void run() {
mMessageView.setHeaders(message, account);
} }
}); });
} }
} }
public static void actionView(Context context, MessageReference messRef, List<MessageReference> messReferences) { public static void actionView(Context context, MessageReference messRef, List<MessageReference> messReferences) {
actionView(context, messRef, messReferences, null); actionView(context, messRef, messReferences, null);
} }
@ -372,22 +316,11 @@ public class MessageView extends K9Activity implements OnClickListener {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
requestWindowFeature(Window.FEATURE_NO_TITLE); requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.message_view); setContentView(R.layout.message_view);
mMessageContentView = (MessageWebView) findViewById(R.id.message_content);
mAccessibleMessageContentView = (AccessibleWebView) findViewById(R.id.accessible_message_content);
mAttachments = (LinearLayout) findViewById(R.id.attachments);
mHeaderContainer = (MessageHeader) findViewById(R.id.header_container); mTopView = mToggleScrollView = (ToggleScrollView) findViewById(R.id.top_view);
mMessageView = (SingleMessageView) findViewById(R.id.message_view);
mScreenReaderEnabled = isScreenReaderActive(); mMessageView.initialize(this, isScreenReaderActive());
if (mScreenReaderEnabled) {
mAccessibleMessageContentView.setVisibility(View.VISIBLE);
mMessageContentView.setVisibility(View.GONE);
} else {
mAccessibleMessageContentView.setVisibility(View.GONE);
mMessageContentView.setVisibility(View.VISIBLE);
}
setupDecryptLayout();
setTitle(""); setTitle("");
Intent intent = getIntent(); Intent intent = getIntent();
@ -395,7 +328,6 @@ public class MessageView extends K9Activity implements OnClickListener {
if (icicle != null) { if (icicle != null) {
restoreMessageReferences(icicle); restoreMessageReferences(icicle);
mPgpData = (PgpData) icicle.getSerializable(STATE_PGP_DATA); mPgpData = (PgpData) icicle.getSerializable(STATE_PGP_DATA);
updateDecryptLayout();
} else { } else {
if (uri == null) { if (uri == null) {
restoreMessageReferencesExtra(intent); restoreMessageReferencesExtra(intent);
@ -437,12 +369,10 @@ public class MessageView extends K9Activity implements OnClickListener {
if (K9.DEBUG) if (K9.DEBUG)
Log.d(K9.LOG_TAG, "MessageView got message " + mMessageReference); Log.d(K9.LOG_TAG, "MessageView got message " + mMessageReference);
if (intent.getBooleanExtra(EXTRA_NEXT, false)) { if (intent.getBooleanExtra(EXTRA_NEXT, false)) {
next.requestFocus(); mNext.requestFocus();
} }
setupHeaderLayout();
setupButtonViews(); setupButtonViews();
displayMessage(mMessageReference); displayMessage(mMessageReference);
} }
@ -513,51 +443,6 @@ public class MessageView extends K9Activity implements OnClickListener {
buttons.setVisibility(View.GONE); buttons.setVisibility(View.GONE);
} }
} }
}
private void setupHeaderLayout() {
mShowPicturesSection = findViewById(R.id.show_pictures_section);
mShowPictures = false;
mDownloadRemainder = (Button) findViewById(R.id.download_remainder);
mMessageContentView.configure();
mTopView = mToggleScrollView = (ToggleScrollView) findViewById(R.id.top_view);
mAttachments.setVisibility(View.GONE);
}
private void setupDecryptLayout() {
mDecryptLayout = findViewById(R.id.layout_decrypt);
mDecryptButton = (Button) findViewById(R.id.btn_decrypt);
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
String data = null;
Part part = MimeUtility.findFirstPartByMimeType(mMessage, "text/plain");
if (part == null) {
part = MimeUtility.findFirstPartByMimeType(mMessage, "text/html");
}
if (part != null) {
data = MimeUtility.getTextFromPart(part);
}
mAccount.getCryptoProvider().decrypt(MessageView.this, data, mPgpData);
} catch (MessagingException me) {
Log.e(K9.LOG_TAG, "Unable to decrypt email.", me);
}
}
});
mCryptoSignatureLayout = (LinearLayout) findViewById(R.id.crypto_signature);
mCryptoSignatureStatusImage = (ImageView) findViewById(R.id.ic_crypto_signature_status);
mCryptoSignatureUserId = (TextView) findViewById(R.id.userId);
mCryptoSignatureUserIdRest = (TextView) findViewById(R.id.userIdRest);
mCryptoSignatureLayout.setVisibility(View.INVISIBLE);
} }
private boolean isScreenReaderActive() { private boolean isScreenReaderActive() {
@ -598,15 +483,15 @@ public class MessageView extends K9Activity implements OnClickListener {
outState.putSerializable(EXTRA_MESSAGE_REFERENCE, mMessageReference); outState.putSerializable(EXTRA_MESSAGE_REFERENCE, mMessageReference);
outState.putSerializable(EXTRA_MESSAGE_REFERENCES, mMessageReferences); outState.putSerializable(EXTRA_MESSAGE_REFERENCES, mMessageReferences);
outState.putSerializable(STATE_PGP_DATA, mPgpData); outState.putSerializable(STATE_PGP_DATA, mPgpData);
outState.putBoolean(SHOW_PICTURES, mShowPictures); outState.putBoolean(SHOW_PICTURES, mMessageView.showPictures());
} }
@Override @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) { protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState); super.onRestoreInstanceState(savedInstanceState);
setLoadPictures(savedInstanceState.getBoolean(SHOW_PICTURES)); setLoadPictures(savedInstanceState.getBoolean(SHOW_PICTURES));
initializeCrypto((PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA)); mPgpData = (PgpData) savedInstanceState.getSerializable(STATE_PGP_DATA);
updateDecryptLayout(); mMessageView.updateCryptoLayout(mAccount.getCryptoProvider(), mPgpData, mMessage);
} }
private void displayMessage(MessageReference ref) { private void displayMessage(MessageReference ref) {
@ -614,30 +499,28 @@ public class MessageView extends K9Activity implements OnClickListener {
if (K9.DEBUG) if (K9.DEBUG)
Log.d(K9.LOG_TAG, "MessageView displaying message " + mMessageReference); Log.d(K9.LOG_TAG, "MessageView displaying message " + mMessageReference);
mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid); mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
mTopView.setVisibility(View.GONE); clearMessageDisplay();
mTopView.scrollTo(0, 0);
mMessageContentView.scrollTo(0, 0);
mHeaderContainer.setVisibility(View.GONE);
mMessageContentView.clearView();
setLoadPictures(false);
mAttachments.removeAllViews();
findSurroundingMessagesUid(); findSurroundingMessagesUid();
// start with fresh, empty PGP data // start with fresh, empty PGP data
initializeCrypto(null); mPgpData = new PgpData();
mTopView.setVisibility(View.VISIBLE); mTopView.setVisibility(View.VISIBLE);
mController.loadMessageForView( mController.loadMessageForView(mAccount, mMessageReference.folderName, mMessageReference.uid, mListener);
mAccount,
mMessageReference.folderName,
mMessageReference.uid,
mListener);
setupDisplayMessageButtons(); setupDisplayMessageButtons();
} }
private void clearMessageDisplay() {
mTopView.setVisibility(View.GONE);
mTopView.scrollTo(0, 0);
setLoadPictures(false);
mMessageView.resetView();
}
private void setupDisplayMessageButtons() { private void setupDisplayMessageButtons() {
mDelete.setEnabled(true); mDelete.setEnabled(true);
next.setEnabled(mNextMessage != null); mNext.setEnabled(mNextMessage != null);
previous.setEnabled(mPreviousMessage != null); mPrevious.setEnabled(mPreviousMessage != null);
// If moving isn't support at all, then all of them must be disabled anyway. // If moving isn't support at all, then all of them must be disabled anyway.
if (mController.isMoveCapable(mAccount)) { if (mController.isMoveCapable(mAccount)) {
// Only enable the button if the Archive folder is not the current folder and not NONE. // Only enable the button if the Archive folder is not the current folder and not NONE.
@ -657,8 +540,8 @@ public class MessageView extends K9Activity implements OnClickListener {
if (buttons != null) { if (buttons != null) {
buttons.setVisibility(View.GONE); buttons.setVisibility(View.GONE);
} }
next = findViewById(R.id.next); mNext = findViewById(R.id.next);
previous = findViewById(R.id.previous); mPrevious = findViewById(R.id.previous);
mDelete = findViewById(R.id.delete); mDelete = findViewById(R.id.delete);
} }
@ -667,8 +550,8 @@ public class MessageView extends K9Activity implements OnClickListener {
if (buttons != null) { if (buttons != null) {
buttons.setVisibility(View.GONE); buttons.setVisibility(View.GONE);
} }
next = findViewById(R.id.next_scrolling); mNext = findViewById(R.id.next_scrolling);
previous = findViewById(R.id.previous_scrolling); mPrevious = findViewById(R.id.previous_scrolling);
mDelete = findViewById(R.id.delete_scrolling); mDelete = findViewById(R.id.delete_scrolling);
} }
@ -695,8 +578,8 @@ public class MessageView extends K9Activity implements OnClickListener {
private void disableButtons() { private void disableButtons() {
setLoadPictures(false); setLoadPictures(false);
disableMoveButtons(); disableMoveButtons();
next.setEnabled(false); mNext.setEnabled(false);
previous.setEnabled(false); mPrevious.setEnabled(false);
mDelete.setEnabled(false); mDelete.setEnabled(false);
} }
@ -869,8 +752,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mMessage.getFolder().getName(), new String[] {mMessage.getUid()}, Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED)); mMessage.getFolder().getName(), new String[] {mMessage.getUid()}, Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
try { try {
mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED)); mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
mHandler.setHeaders(mMessage); mMessageView.setHeaders(mMessage, mAccount);
prepareMenuItems();
} catch (MessagingException me) { } catch (MessagingException me) {
Log.e(K9.LOG_TAG, "Could not set flag on local message", me); Log.e(K9.LOG_TAG, "Could not set flag on local message", me);
} }
@ -967,7 +849,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mTopView.startAnimation(outToLeftAnimation()); mTopView.startAnimation(outToLeftAnimation());
} }
displayMessage(mNextMessage); displayMessage(mNextMessage);
next.requestFocus(); mNext.requestFocus();
} }
@Override @Override
@ -982,7 +864,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mTopView.startAnimation(inFromRightAnimation()); mTopView.startAnimation(inFromRightAnimation());
} }
displayMessage(mPreviousMessage); displayMessage(mPreviousMessage);
previous.requestFocus(); mPrevious.requestFocus();
} }
private void onMarkAsUnread() { private void onMarkAsUnread() {
@ -995,7 +877,7 @@ public class MessageView extends K9Activity implements OnClickListener {
false); false);
try { try {
mMessage.setFlag(Flag.SEEN, false); mMessage.setFlag(Flag.SEEN, false);
mHandler.setHeaders(mMessage); mMessageView.setHeaders(mMessage, mAccount);
String subject = mMessage.getSubject(); String subject = mMessage.getSubject();
setTitle(subject); setTitle(subject);
} catch (Exception e) { } catch (Exception e) {
@ -1009,17 +891,8 @@ public class MessageView extends K9Activity implements OnClickListener {
if (mMessage.isSet(Flag.X_DOWNLOADED_FULL)) { if (mMessage.isSet(Flag.X_DOWNLOADED_FULL)) {
return; return;
} }
mDownloadRemainder.setEnabled(false); mMessageView.downloadRemainderButton().setEnabled(false);
mController.loadMessageForViewRemote( mController.loadMessageForViewRemote(mAccount, mMessageReference.folderName, mMessageReference.uid, mListener);
mAccount,
mMessageReference.folderName,
mMessageReference.uid,
mListener);
}
private void onShowPictures() {
// TODO: Download attachments that are used as inline image
setLoadPictures(true);
} }
/** /**
@ -1030,11 +903,12 @@ public class MessageView extends K9Activity implements OnClickListener {
* false, otherwise. * false, otherwise.
*/ */
private void setLoadPictures(boolean enable) { private void setLoadPictures(boolean enable) {
mMessageContentView.blockNetworkData(!enable); mMessageView.blockNetworkData(!enable);
mShowPictures = enable; mMessageView.setShowPictures(enable);
mHandler.showShowPictures(false); mHandler.showShowPictures(false);
} }
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
case R.id.reply: case R.id.reply:
@ -1076,7 +950,7 @@ public class MessageView extends K9Activity implements OnClickListener {
((AttachmentView)view).saveFile(); ((AttachmentView)view).saveFile();
break; break;
case R.id.show_pictures: case R.id.show_pictures:
onShowPictures(); setLoadPictures(true);
break; break;
case R.id.download_remainder: case R.id.download_remainder:
onDownloadRemainder(); onDownloadRemainder();
@ -1122,14 +996,15 @@ public class MessageView extends K9Activity implements OnClickListener {
break; break;
case R.id.show_full_header: case R.id.show_full_header:
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override public void run() { @Override
mHeaderContainer.onShowAdditionalHeaders(); public void run() {
mMessageView.showAllHeaders();
} }
}); });
break; break;
case R.id.select_text: case R.id.select_text:
mToggleScrollView.setScrolling(false); mToggleScrollView.setScrolling(false);
mMessageContentView.emulateShiftHeld(); mMessageView.beginSelectingText();
break; break;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@ -1141,8 +1016,6 @@ public class MessageView extends K9Activity implements OnClickListener {
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.message_view_option, menu); getMenuInflater().inflate(R.menu.message_view_option, menu);
optionsMenu = menu;
prepareMenuItems();
if (!mController.isCopyCapable(mAccount)) { if (!mController.isCopyCapable(mAccount)) {
menu.findItem(R.id.copy).setVisible(false); menu.findItem(R.id.copy).setVisible(false);
} }
@ -1160,11 +1033,6 @@ public class MessageView extends K9Activity implements OnClickListener {
return true; return true;
} }
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
prepareMenuItems();
return super.onPrepareOptionsMenu(menu);
}
// TODO: when switching to API version 8, override onCreateDialog(int, Bundle) // TODO: when switching to API version 8, override onCreateDialog(int, Bundle)
/** /**
@ -1182,8 +1050,8 @@ public class MessageView extends K9Activity implements OnClickListener {
return super.onCreateDialog(id); return super.onCreateDialog(id);
} }
private void prepareMenuItems() { @Override
Menu menu = optionsMenu; public boolean onPrepareOptionsMenu(Menu menu) {
if (menu != null) { if (menu != null) {
MenuItem flagItem = menu.findItem(R.id.flag); MenuItem flagItem = menu.findItem(R.id.flag);
if (flagItem != null && mMessage != null) { if (flagItem != null && mMessage != null) {
@ -1191,10 +1059,11 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
MenuItem additionalHeadersItem = menu.findItem(R.id.show_full_header); MenuItem additionalHeadersItem = menu.findItem(R.id.show_full_header);
if (additionalHeadersItem != null) { if (additionalHeadersItem != null) {
additionalHeadersItem.setTitle(mHeaderContainer.additionalHeadersVisible() ? additionalHeadersItem.setTitle(mMessageView.additionalHeadersVisible() ?
R.string.hide_full_header_action : R.string.show_full_header_action); R.string.hide_full_header_action : R.string.show_full_header_action);
} }
} }
return super.onPrepareOptionsMenu(menu);
} }
public void displayMessage(Account account, String folder, String uid, Message message) { public void displayMessage(Account account, String folder, String uid, Message message) {
@ -1202,13 +1071,13 @@ public class MessageView extends K9Activity implements OnClickListener {
if (MessageView.this.mMessage != null if (MessageView.this.mMessage != null
&& MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL) && MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)
&& message.isSet(Flag.X_DOWNLOADED_FULL)) { && message.isSet(Flag.X_DOWNLOADED_FULL)) {
mHandler.setHeaders(message); mMessageView.setHeaders(message, account);
} }
MessageView.this.mMessage = message; MessageView.this.mMessage = message;
mHandler.removeAllAttachments(); mHandler.removeAllAttachments();
String text, type; String type;
if (mPgpData.getDecryptedData() != null) { String text = mPgpData.getDecryptedData();
text = mPgpData.getDecryptedData(); if (text != null) {
type = "text/plain"; type = "text/plain";
} else { } else {
// getTextForDisplay() always returns HTML-ified content. // getTextForDisplay() always returns HTML-ified content.
@ -1221,38 +1090,29 @@ public class MessageView extends K9Activity implements OnClickListener {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
public void run() { public void run() {
mTopView.scrollTo(0, 0); mTopView.scrollTo(0, 0);
if (mScreenReaderEnabled) { mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, emailText, contentType);
mAccessibleMessageContentView.loadDataWithBaseURL("http://",
emailText, contentType, "utf-8", null);
} else {
mMessageContentView.loadDataWithBaseURL("http://", emailText,
contentType, "utf-8", null);
mMessageContentView.scrollTo(0, 0);
}
updateDecryptLayout();
} }
}); });
// If the message contains external pictures and the "Show pictures" // If the message contains external pictures and the "Show pictures"
// button wasn't already pressed, see if the user's preferences has us // button wasn't already pressed, see if the user's preferences has us
// showing them anyway. // showing them anyway.
if (Utility.hasExternalImages(text) && !mShowPictures) { if (Utility.hasExternalImages(text) && !mMessageView.showPictures()) {
if ((account.getShowPictures() == Account.ShowPictures.ALWAYS) || if ((account.getShowPictures() == Account.ShowPictures.ALWAYS) ||
((account.getShowPictures() == Account.ShowPictures.ONLY_FROM_CONTACTS) && ((account.getShowPictures() == Account.ShowPictures.ONLY_FROM_CONTACTS) &&
mContacts.isInContacts(message.getFrom()[0].getAddress()))) { mContacts.isInContacts(message.getFrom()[0].getAddress()))) {
onShowPictures(); setLoadPictures(true);
} else { } else {
mHandler.showShowPictures(true); mMessageView.showShowPicturesSection(true);
} }
} }
} else { } else {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
public void run() { public void run() {
mMessageContentView.loadUrl("file:///android_asset/empty.html"); mMessageView.loadBodyFromUrl("file:///android_asset/empty.html");
updateDecryptLayout();
} }
}); });
} }
renderAttachments(mMessage, 0); mMessageView.renderAttachments(mMessage, 0, mMessage, mAccount, mController, mListener);
} catch (Exception e) { } catch (Exception e) {
if (Config.LOGV) { if (Config.LOGV) {
Log.v(K9.LOG_TAG, "loadMessageForViewBodyAvailable", e); Log.v(K9.LOG_TAG, "loadMessageForViewBodyAvailable", e);
@ -1260,53 +1120,31 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
} }
private void renderAttachments(Part part, int depth) throws MessagingException {
if (part.getBody() instanceof Multipart) {
Multipart mp = (Multipart) part.getBody();
for (int i = 0; i < mp.getCount(); i++) {
renderAttachments(mp.getBodyPart(i), depth + 1);
}
} else if (part instanceof LocalAttachmentBodyPart) {
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
// Inline parts with a content-id are almost certainly components of an HTML message
// not attachments. Don't show attachment download buttons for them.
if (contentDisposition != null &&
MimeUtility.getHeaderParameter(contentDisposition, null).matches("^(?i:inline)")
&& part.getHeader("Content-ID") != null) {
return;
}
renderPartAsAttachment(part);
}
}
private void renderPartAsAttachment(Part part) throws MessagingException {
LayoutInflater inflater = getLayoutInflater();
AttachmentView view = (AttachmentView)inflater.inflate(R.layout.message_view_attachment, null);
if (view.populateFromPart(part, mMessage, mAccount, mController, mListener)) {
mHandler.addAttachment(view);
}
return;
}
class Listener extends MessagingListener { class Listener extends MessagingListener {
@Override @Override
public void loadMessageForViewHeadersAvailable(Account account, String folder, String uid, public void loadMessageForViewHeadersAvailable(final Account account, String folder, String uid,
final Message message) { final Message message) {
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder) if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|| !mMessageReference.accountUuid.equals(account.getUuid())) { || !mMessageReference.accountUuid.equals(account.getUuid())) {
return; return;
} }
MessageView.this.mMessage = message; MessageView.this.mMessage = message;
if (!message.isSet(Flag.X_DOWNLOADED_FULL) runOnUiThread(
&& !message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
mHandler.post(new Runnable() { new Runnable() {
public void run() { public void run() {
mMessageContentView.loadUrl("file:///android_asset/downloading.html"); if (!message.isSet(Flag.X_DOWNLOADED_FULL) && !message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
updateDecryptLayout(); mMessageView.loadBodyFromUrl("file:///android_asset/downloading.html");
} }
}); mMessageView.setHeaders(message, account);
} mMessageView.setOnFlagListener(new OnClickListener() {
mHandler.setHeaders(message); @Override
public void onClick(View v) {
onFlag();
}
});
}
});
} }
@Override @Override
@ -1339,8 +1177,7 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
if ((MessageView.this.mMessage == null) || if ((MessageView.this.mMessage == null) ||
!MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)) { !MessageView.this.mMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
mMessageContentView.loadUrl("file:///android_asset/empty.html"); mMessageView.loadBodyFromUrl("file:///android_asset/empty.html");
updateDecryptLayout();
} }
} }
}); });
@ -1348,7 +1185,7 @@ public class MessageView extends K9Activity implements OnClickListener {
@Override @Override
public void loadMessageForViewFinished(Account account, String folder, String uid, public void loadMessageForViewFinished(Account account, String folder, String uid,
Message message) { final Message message) {
if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder) if (!mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder)
|| !mMessageReference.accountUuid.equals(account.getUuid())) { || !mMessageReference.accountUuid.equals(account.getUuid())) {
return; return;
@ -1356,6 +1193,7 @@ public class MessageView extends K9Activity implements OnClickListener {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
public void run() { public void run() {
setProgressBarIndeterminateVisibility(false); setProgressBarIndeterminateVisibility(false);
mMessageView.setShowDownloadButton(message);
} }
}); });
} }
@ -1368,7 +1206,6 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
public void run() { public void run() {
updateDecryptLayout();
setProgressBarIndeterminateVisibility(true); setProgressBarIndeterminateVisibility(true);
} }
}); });
@ -1380,7 +1217,7 @@ public class MessageView extends K9Activity implements OnClickListener {
if (mMessage != message) { if (mMessage != message) {
return; return;
} }
mHandler.setAttachmentsEnabled(false); mMessageView.setAttachmentsEnabled(false);
mHandler.progress(true); mHandler.progress(true);
if (requiresDownload) { if (requiresDownload) {
mHandler.fetchingAttachment(); mHandler.fetchingAttachment();
@ -1388,12 +1225,11 @@ public class MessageView extends K9Activity implements OnClickListener {
} }
@Override @Override
public void loadAttachmentFinished(Account account, Message message, public void loadAttachmentFinished(Account account, Message message, Part part, Object tag) {
Part part, Object tag) {
if (mMessage != message) { if (mMessage != message) {
return; return;
} }
mHandler.setAttachmentsEnabled(true); mMessageView.setAttachmentsEnabled(true);
mHandler.progress(false); mHandler.progress(false);
Object[] params = (Object[]) tag; Object[] params = (Object[]) tag;
boolean download = (Boolean) params[0]; boolean download = (Boolean) params[0];
@ -1412,93 +1248,22 @@ public class MessageView extends K9Activity implements OnClickListener {
if (mMessage != message) { if (mMessage != message) {
return; return;
} }
mHandler.setAttachmentsEnabled(true); mMessageView.setAttachmentsEnabled(true);
mHandler.progress(false); mHandler.progress(false);
mHandler.networkError(); mHandler.networkError();
} }
} }
private void initializeCrypto(PgpData data) { public PgpData getPgpData() {
if (data == null) { return mPgpData;
if (mAccount == null) {
mAccount = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
}
mPgpData = new PgpData();
} else {
mPgpData = data;
}
} }
/** // This REALLY should be in MessageCryptoView
* Fill the decrypt layout with signature data, if known, make controls visible, if public void onDecryptDone(PgpData pgpData) {
* they should be visible.
*/
public void updateDecryptLayout() {
if (mPgpData.getSignatureKeyId() != 0) {
mCryptoSignatureUserIdRest.setText(
getString(R.string.key_id, Long.toHexString(mPgpData.getSignatureKeyId() & 0xffffffffL)));
String userId = mPgpData.getSignatureUserId();
if (userId == null) {
userId = getString(R.string.unknown_crypto_signature_user_id);
}
String chunks[] = userId.split(" <", 2);
String name = chunks[0];
if (chunks.length > 1) {
mCryptoSignatureUserIdRest.setText("<" + chunks[1]);
}
mCryptoSignatureUserId.setText(name);
if (mPgpData.getSignatureSuccess()) {
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
} else if (mPgpData.getSignatureUnknown()) {
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} else {
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
mCryptoSignatureLayout.setVisibility(View.VISIBLE);
mDecryptLayout.setVisibility(View.VISIBLE);
} else {
mCryptoSignatureLayout.setVisibility(View.INVISIBLE);
}
if (false || ((mMessage == null) && (mPgpData.getDecryptedData() == null))) {
mDecryptLayout.setVisibility(View.GONE);
return;
}
if (mPgpData.getDecryptedData() != null) {
if (mPgpData.getSignatureKeyId() == 0) {
mDecryptLayout.setVisibility(View.GONE);
} else {
// no need to show this after decryption/verification
mDecryptButton.setVisibility(View.GONE);
}
return;
}
mDecryptButton.setVisibility(View.VISIBLE);
CryptoProvider crypto = mAccount.getCryptoProvider();
if (crypto.isEncrypted(mMessage)) {
mDecryptButton.setText(R.string.btn_decrypt);
mDecryptLayout.setVisibility(View.VISIBLE);
} else if (crypto.isSigned(mMessage)) {
mDecryptButton.setText(R.string.btn_verify);
mDecryptLayout.setVisibility(View.VISIBLE);
} else {
mDecryptLayout.setVisibility(View.GONE);
try {
// check for PGP/MIME encryption
Part pgp = MimeUtility.findFirstPartByMimeType(mMessage, "application/pgp-encrypted");
if (pgp != null) {
Toast.makeText(this, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG).show();
}
} catch (MessagingException e) {
// nothing to do...
}
}
}
public void onDecryptDone() {
// TODO: this might not be enough if the orientation was changed while in APG, // TODO: this might not be enough if the orientation was changed while in APG,
// sometimes shows the original encrypted content // sometimes shows the original encrypted content
mMessageContentView.loadDataWithBaseURL("email://", mPgpData.getDecryptedData(), "text/plain", "utf-8", null); mMessageView.loadBodyFromText(mAccount.getCryptoProvider(), mPgpData, mMessage, mPgpData.getDecryptedData(), "text/plain");
updateDecryptLayout();
} }
} }

View File

@ -46,6 +46,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Folder.FolderType; import com.fsck.k9.mail.Folder.FolderType;
import com.fsck.k9.mail.Folder.OpenMode; import com.fsck.k9.mail.Folder.OpenMode;
import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part; import com.fsck.k9.mail.Part;
@ -1004,13 +1005,19 @@ public class MessagingController implements Runnable {
* Remove any messages that are in the local store but no longer on the remote store or are too old * Remove any messages that are in the local store but no longer on the remote store or are too old
*/ */
if (account.syncRemoteDeletions()) { if (account.syncRemoteDeletions()) {
ArrayList<Message> destroyMessages = new ArrayList<Message>();
for (Message localMessage : localMessages) { for (Message localMessage : localMessages) {
if (remoteUidMap.get(localMessage.getUid()) == null) { if (remoteUidMap.get(localMessage.getUid()) == null) {
localMessage.destroy(); destroyMessages.add(localMessage);
}
}
for (MessagingListener l : getListeners(listener)) {
l.synchronizeMailboxRemovedMessage(account, folder, localMessage); localFolder.destroyMessages(destroyMessages.toArray(EMPTY_MESSAGE_ARRAY));
}
for (Message destroyMessage : destroyMessages) {
for (MessagingListener l : getListeners(listener)) {
l.synchronizeMailboxRemovedMessage(account, folder, destroyMessage);
} }
} }
} }
@ -3469,7 +3476,22 @@ public class MessagingController implements Runnable {
if (quotedText != null) { if (quotedText != null) {
msg.putExtra(Intent.EXTRA_TEXT, quotedText); msg.putExtra(Intent.EXTRA_TEXT, quotedText);
} }
msg.putExtra(Intent.EXTRA_SUBJECT, "Fwd: " + message.getSubject()); msg.putExtra(Intent.EXTRA_SUBJECT, message.getSubject());
Address[] to = message.getRecipients(RecipientType.TO);
String[] recipientsTo = new String[to.length];
for (int i = 0; i < to.length; i++) {
recipientsTo[i] = to[i].toString();
}
msg.putExtra(Intent.EXTRA_EMAIL, recipientsTo);
Address[] cc = message.getRecipients(RecipientType.CC);
String[] recipientsCc = new String[cc.length];
for (int i = 0; i < cc.length; i++) {
recipientsCc[i] = cc[i].toString();
}
msg.putExtra(Intent.EXTRA_CC, recipientsCc);
msg.setType("text/plain"); msg.setType("text/plain");
context.startActivity(Intent.createChooser(msg, context.getString(R.string.send_alternate_chooser_title))); context.startActivity(Intent.createChooser(msg, context.getString(R.string.send_alternate_chooser_title)));
} catch (MessagingException me) { } catch (MessagingException me) {

View File

@ -327,7 +327,7 @@ public class Apg extends CryptoProvider {
pgpData.setSignatureUnknown(data.getBooleanExtra(Apg.EXTRA_SIGNATURE_UNKNOWN, false)); pgpData.setSignatureUnknown(data.getBooleanExtra(Apg.EXTRA_SIGNATURE_UNKNOWN, false));
pgpData.setDecryptedData(data.getStringExtra(Apg.EXTRA_DECRYPTED_MESSAGE)); pgpData.setDecryptedData(data.getStringExtra(Apg.EXTRA_DECRYPTED_MESSAGE));
((MessageView) activity).onDecryptDone(); ((MessageView) activity).onDecryptDone(pgpData);
break; break;
@ -386,9 +386,7 @@ public class Apg extends CryptoProvider {
activity.startActivityForResult(intent, Apg.DECRYPT_MESSAGE); activity.startActivityForResult(intent, Apg.DECRYPT_MESSAGE);
return true; return true;
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
Toast.makeText(activity, Toast.makeText(activity, R.string.error_activity_not_found, Toast.LENGTH_SHORT).show();
R.string.error_activity_not_found,
Toast.LENGTH_SHORT).show();
return false; return false;
} }
} }

View File

@ -2020,6 +2020,27 @@ public class LocalStore extends Store implements Serializable {
appendMessages(messages, false); appendMessages(messages, false);
} }
public void destroyMessages(final Message[] messages) throws MessagingException {
try {
database.execute(true, new DbCallback<Void>() {
@Override
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
for (Message message : messages) {
try {
message.destroy();
} catch (MessagingException e) {
throw new WrappedException(e);
}
}
return null;
}
});
} catch (MessagingException e) {
throw new WrappedException(e);
}
}
/** /**
* The method differs slightly from the contract; If an incoming message already has a uid * The method differs slightly from the contract; If an incoming message already has a uid
* assigned and it matches the uid of an existing message then this message will replace the * assigned and it matches the uid of an existing message then this message will replace the

View File

@ -27,10 +27,8 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
import java.util.ArrayList;
import java.util.Arrays; import java.util.*;
import java.util.HashMap;
import java.util.List;
public class SmtpTransport extends Transport { public class SmtpTransport extends Transport {
public static final int CONNECTION_SECURITY_NONE = 0; public static final int CONNECTION_SECURITY_NONE = 0;
@ -284,7 +282,7 @@ public class SmtpTransport extends Transport {
addressesOfCharset.add(addressString); addressesOfCharset.add(addressString);
} }
for (HashMap.Entry<String, ArrayList<String>> charsetAddressesMapEntry : for (Map.Entry<String, ArrayList<String>> charsetAddressesMapEntry :
charsetAddressesMap.entrySet()) { charsetAddressesMap.entrySet()) {
String charset = charsetAddressesMapEntry.getKey(); String charset = charsetAddressesMapEntry.getKey();
ArrayList<String> addressesOfCharset = charsetAddressesMapEntry.getValue(); ArrayList<String> addressesOfCharset = charsetAddressesMapEntry.getValue();

View File

@ -0,0 +1,143 @@
package com.fsck.k9.view;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.*;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.crypto.CryptoProvider;
import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MimeUtility;
public class MessageCryptoView extends LinearLayout {
private Context mContext;
private Activity mActivity;
private Button mDecryptButton;
private LinearLayout mCryptoSignatureLayout = null;
private ImageView mCryptoSignatureStatusImage = null;
private TextView mCryptoSignatureUserId = null;
private TextView mCryptoSignatureUserIdRest = null;
public MessageCryptoView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public void setupChildViews() {
mCryptoSignatureLayout = (LinearLayout) findViewById(R.id.crypto_signature);
mCryptoSignatureStatusImage = (ImageView) findViewById(R.id.ic_crypto_signature_status);
mCryptoSignatureUserId = (TextView) findViewById(R.id.userId);
mCryptoSignatureUserIdRest = (TextView) findViewById(R.id.userIdRest);
mCryptoSignatureLayout.setVisibility(View.INVISIBLE);
mDecryptButton = (Button) findViewById(R.id.btn_decrypt);
}
public void setActivity(Activity activity) {
mActivity = activity;
}
public void hide () {
this.setVisibility(View.GONE);
}
/**
* Fill the decrypt layout with signature data, if known, make controls visible, if
* they should be visible.
*/
public void updateLayout(final CryptoProvider cryptoProvider, final PgpData pgpData, final Message message) {
if (pgpData.getSignatureKeyId() != 0) {
mCryptoSignatureUserIdRest.setText(
mContext.getString(R.string.key_id, Long.toHexString(pgpData.getSignatureKeyId() & 0xffffffffL)));
String userId = pgpData.getSignatureUserId();
if (userId == null) {
userId = mContext.getString(R.string.unknown_crypto_signature_user_id);
}
String chunks[] = userId.split(" <", 2);
String name = chunks[0];
if (chunks.length > 1) {
mCryptoSignatureUserIdRest.setText("<" + chunks[1]);
}
mCryptoSignatureUserId.setText(name);
if (pgpData.getSignatureSuccess()) {
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
} else if (pgpData.getSignatureUnknown()) {
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} else {
mCryptoSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
mCryptoSignatureLayout.setVisibility(View.VISIBLE);
this.setVisibility(View.VISIBLE);
} else {
mCryptoSignatureLayout.setVisibility(View.INVISIBLE);
}
if ((message == null) && (pgpData.getDecryptedData() == null)) {
this.setVisibility(View.GONE);
return;
}
if (pgpData.getDecryptedData() != null) {
if (pgpData.getSignatureKeyId() == 0) {
this.setVisibility(View.GONE);
} else {
// no need to show this after decryption/verification
mDecryptButton.setVisibility(View.GONE);
}
return;
}
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
String data = null;
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
if (part == null) {
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
}
if (part != null) {
data = MimeUtility.getTextFromPart(part);
}
cryptoProvider.decrypt(mActivity, data, pgpData);
} catch (MessagingException me) {
Log.e(K9.LOG_TAG, "Unable to decrypt email.", me);
}
}
});
mDecryptButton.setVisibility(View.VISIBLE);
if (cryptoProvider.isEncrypted(message)) {
mDecryptButton.setText(R.string.btn_decrypt);
this.setVisibility(View.VISIBLE);
} else if (cryptoProvider.isSigned(message)) {
mDecryptButton.setText(R.string.btn_verify);
this.setVisibility(View.VISIBLE);
} else {
this.setVisibility(View.GONE);
try {
// check for PGP/MIME encryption
Part pgp = MimeUtility.findFirstPartByMimeType(message, "application/pgp-encrypted");
if (pgp != null) {
Toast.makeText(mContext, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG).show();
}
} catch (MessagingException e) {
// nothing to do...
}
}
}
}

View File

@ -0,0 +1,211 @@
package com.fsck.k9.view;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.crypto.CryptoProvider;
import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.mail.*;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.LocalStore;
/**
*/
public class SingleMessageView extends LinearLayout {
private boolean mScreenReaderEnabled;
private MessageCryptoView mCryptoView;
private MessageWebView mMessageContentView;
private AccessibleWebView mAccessibleMessageContentView;
private MessageHeader mHeaderContainer;
private LinearLayout mAttachments;
private View mShowPicturesSection;
private boolean mShowPictures;
private Button mDownloadRemainder;
private LayoutInflater mInflater;
public void initialize(Activity activity, Boolean isScreenReaderActive) {
mMessageContentView = (MessageWebView) findViewById(R.id.message_content);
mAccessibleMessageContentView = (AccessibleWebView) findViewById(R.id.accessible_message_content);
mAttachments = (LinearLayout) findViewById(R.id.attachments);
mHeaderContainer = (MessageHeader) findViewById(R.id.header_container);
mCryptoView = (MessageCryptoView) findViewById(R.id.layout_decrypt);
mCryptoView.setActivity(activity);
mCryptoView.setupChildViews();
mShowPicturesSection = findViewById(R.id.show_pictures_section);
mShowPictures = false;
mInflater = activity.getLayoutInflater();
mDownloadRemainder = (Button) findViewById(R.id.download_remainder);
mMessageContentView.configure();
mAttachments.setVisibility(View.GONE);
if (isScreenReaderActive) {
mAccessibleMessageContentView.setVisibility(View.VISIBLE);
mMessageContentView.setVisibility(View.GONE);
} else {
mAccessibleMessageContentView.setVisibility(View.GONE);
mMessageContentView.setVisibility(View.VISIBLE);
}
mScreenReaderEnabled = isScreenReaderActive;
}
public SingleMessageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean showPictures() {
return mShowPictures;
}
public void setShowPictures(Boolean show) {
mShowPictures = show;
}
public void blockNetworkData(Boolean block) {
mMessageContentView.blockNetworkData(block);
}
public Button downloadRemainderButton() {
return mDownloadRemainder;
}
public void showShowPicturesSection(boolean show) {
mShowPicturesSection.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void setHeaders(final Message message, Account account) {
try {
mHeaderContainer.populate(message, account);
} catch (Exception me) {
Log.e(K9.LOG_TAG, "setHeaders - error", me);
}
}
public void setShowDownloadButton(Message message) {
if (message.isSet(Flag.X_DOWNLOADED_FULL)) {
mDownloadRemainder.setVisibility(View.GONE);
} else {
mDownloadRemainder.setEnabled(true);
mDownloadRemainder.setVisibility(View.VISIBLE);
}
}
public void setOnFlagListener(OnClickListener listener) {
mHeaderContainer.setOnFlagListener(listener);
}
public void showAllHeaders() {
mHeaderContainer.onShowAdditionalHeaders();
}
public boolean additionalHeadersVisible() {
return mHeaderContainer.additionalHeadersVisible();
}
public void loadBodyFromUrl(String url) {
mMessageContentView.loadUrl(url);
mCryptoView.hide();
}
public void loadBodyFromText(CryptoProvider cryptoProvider, PgpData pgpData, Message message, String emailText, String contentType) {
if (mScreenReaderEnabled) {
mAccessibleMessageContentView.loadDataWithBaseURL("http://", emailText, contentType, "utf-8", null);
} else {
mMessageContentView.loadDataWithBaseURL("http://", emailText, contentType, "utf-8", null);
mMessageContentView.scrollTo(0, 0);
}
updateCryptoLayout(cryptoProvider, pgpData, message);
}
public void updateCryptoLayout(CryptoProvider cp, PgpData pgpData, Message message) {
mCryptoView.updateLayout(cp, pgpData, message);
}
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);
}
}
public void removeAllAttachments() {
for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) {
mAttachments.removeView(mAttachments.getChildAt(i));
}
}
public void renderAttachments(Part part, int depth,
Message message, Account account, MessagingController controller, MessagingListener listener) throws MessagingException {
if (part.getBody() instanceof Multipart) {
Multipart mp = (Multipart) part.getBody();
for (int i = 0; i < mp.getCount(); i++) {
renderAttachments(mp.getBodyPart(i), depth + 1, message, account, controller, listener);
}
} else if (part instanceof LocalStore.LocalAttachmentBodyPart) {
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
// Inline parts with a content-id are almost certainly components of an HTML message
// not attachments. Don't show attachment download buttons for them.
if (contentDisposition != null &&
MimeUtility.getHeaderParameter(contentDisposition, null).matches("^(?i:inline)")
&& part.getHeader("Content-ID") != null) {
return;
}
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
if (view.populateFromPart(part, message, account, controller, listener)) {
addAttachment(view);
}
}
}
public void addAttachment(View attachmentView) {
mAttachments.addView(attachmentView);
mAttachments.setVisibility(View.VISIBLE);
}
public void zoom(KeyEvent event) {
if (mScreenReaderEnabled) {
mAccessibleMessageContentView.zoomIn();
} else {
if (event.isShiftPressed()) {
mMessageContentView.zoomIn();
} else {
mMessageContentView.zoomOut();
}
}
}
public void beginSelectingText() {
mMessageContentView.emulateShiftHeld();
}
public void resetView() {
mMessageContentView.scrollTo(0, 0);
mHeaderContainer.setVisibility(View.GONE);
mMessageContentView.clearView();
mAttachments.removeAllViews();
}
}