2011-02-10 19:29:46 -05:00
|
|
|
package com.fsck.k9.view;
|
|
|
|
|
2011-02-11 10:09:15 -05:00
|
|
|
import android.app.Activity;
|
2011-02-14 11:54:01 -05:00
|
|
|
import android.content.ContentResolver;
|
2011-02-10 19:29:46 -05:00
|
|
|
import android.content.Context;
|
2011-02-14 11:54:01 -05:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.pm.ResolveInfo;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.net.Uri;
|
2011-02-10 19:29:46 -05:00
|
|
|
import android.util.AttributeSet;
|
2011-02-11 10:09:15 -05:00
|
|
|
import android.util.Log;
|
|
|
|
import android.view.KeyEvent;
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.View;
|
|
|
|
import android.widget.Button;
|
2011-02-10 19:29:46 -05:00
|
|
|
import android.widget.LinearLayout;
|
2011-02-11 10:09:15 -05:00
|
|
|
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;
|
2011-02-14 12:27:40 -05:00
|
|
|
import com.fsck.k9.helper.Contacts;
|
|
|
|
import com.fsck.k9.helper.Utility;
|
2011-02-11 10:09:15 -05:00
|
|
|
import com.fsck.k9.mail.*;
|
|
|
|
import com.fsck.k9.mail.internet.MimeUtility;
|
|
|
|
import com.fsck.k9.mail.store.LocalStore;
|
|
|
|
|
2011-02-14 11:54:01 -05:00
|
|
|
import java.util.List;
|
|
|
|
|
2011-02-10 19:29:46 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
2011-02-11 10:09:15 -05:00
|
|
|
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;
|
2011-02-11 12:13:18 -05:00
|
|
|
private LayoutInflater mInflater;
|
2011-02-14 12:27:40 -05:00
|
|
|
private Contacts mContacts;
|
2011-04-24 00:00:10 -04:00
|
|
|
private AttachmentView.AttachmentFileDownloadCallback attachmentCallback;
|
2011-02-11 10:09:15 -05:00
|
|
|
|
2011-02-14 11:54:01 -05:00
|
|
|
public void initialize(Activity activity) {
|
2011-02-11 10:09:15 -05:00
|
|
|
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;
|
|
|
|
|
2011-02-14 12:27:40 -05:00
|
|
|
mContacts = Contacts.getInstance(activity);
|
|
|
|
|
2011-02-11 12:13:18 -05:00
|
|
|
mInflater = activity.getLayoutInflater();
|
2011-02-11 10:09:15 -05:00
|
|
|
mDownloadRemainder = (Button) findViewById(R.id.download_remainder);
|
|
|
|
mMessageContentView.configure();
|
|
|
|
|
|
|
|
|
|
|
|
mAttachments.setVisibility(View.GONE);
|
2011-02-14 11:54:01 -05:00
|
|
|
if (isScreenReaderActive(activity)) {
|
2011-02-11 10:09:15 -05:00
|
|
|
mAccessibleMessageContentView.setVisibility(View.VISIBLE);
|
|
|
|
mMessageContentView.setVisibility(View.GONE);
|
2011-02-14 11:54:01 -05:00
|
|
|
mScreenReaderEnabled = true;
|
2011-02-11 10:09:15 -05:00
|
|
|
} else {
|
|
|
|
mAccessibleMessageContentView.setVisibility(View.GONE);
|
|
|
|
mMessageContentView.setVisibility(View.VISIBLE);
|
2011-02-14 11:54:01 -05:00
|
|
|
mScreenReaderEnabled = false;
|
2011-02-11 10:09:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-02-10 19:29:46 -05:00
|
|
|
public SingleMessageView(Context context, AttributeSet attrs) {
|
|
|
|
super(context, attrs);
|
|
|
|
}
|
2011-02-11 10:09:15 -05:00
|
|
|
|
2011-02-14 11:54:01 -05:00
|
|
|
|
2011-02-14 12:27:40 -05:00
|
|
|
private boolean isScreenReaderActive(Activity activity) {
|
2011-02-14 11:54:01 -05:00
|
|
|
final String SCREENREADER_INTENT_ACTION = "android.accessibilityservice.AccessibilityService";
|
|
|
|
final String SCREENREADER_INTENT_CATEGORY = "android.accessibilityservice.category.FEEDBACK_SPOKEN";
|
|
|
|
// Restrict the set of intents to only accessibility services that have
|
|
|
|
// the category FEEDBACK_SPOKEN (aka, screen readers).
|
|
|
|
Intent screenReaderIntent = new Intent(SCREENREADER_INTENT_ACTION);
|
|
|
|
screenReaderIntent.addCategory(SCREENREADER_INTENT_CATEGORY);
|
|
|
|
List<ResolveInfo> screenReaders = activity.getPackageManager().queryIntentServices(
|
2011-02-14 12:27:40 -05:00
|
|
|
screenReaderIntent, 0);
|
2011-02-14 11:54:01 -05:00
|
|
|
ContentResolver cr = activity.getContentResolver();
|
|
|
|
Cursor cursor = null;
|
|
|
|
int status = 0;
|
|
|
|
for (ResolveInfo screenReader : screenReaders) {
|
|
|
|
// All screen readers are expected to implement a content provider
|
|
|
|
// that responds to
|
|
|
|
// content://<nameofpackage>.providers.StatusProvider
|
|
|
|
cursor = cr.query(Uri.parse("content://" + screenReader.serviceInfo.packageName
|
2011-02-14 12:27:40 -05:00
|
|
|
+ ".providers.StatusProvider"), null, null, null, null);
|
2011-02-14 11:54:01 -05:00
|
|
|
if (cursor != null) {
|
|
|
|
cursor.moveToFirst();
|
|
|
|
// These content providers use a special cursor that only has
|
|
|
|
// one element,
|
|
|
|
// an integer that is 1 if the screen reader is running.
|
|
|
|
status = cursor.getInt(0);
|
|
|
|
cursor.close();
|
|
|
|
if (status == 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-02-11 10:09:15 -05:00
|
|
|
public boolean showPictures() {
|
|
|
|
return mShowPictures;
|
|
|
|
}
|
|
|
|
public void setShowPictures(Boolean show) {
|
|
|
|
mShowPictures = show;
|
|
|
|
}
|
|
|
|
|
2011-02-14 11:40:52 -05:00
|
|
|
/**
|
|
|
|
* Enable/disable image loading of the WebView. But always hide the
|
|
|
|
* "Show pictures" button!
|
|
|
|
*
|
|
|
|
* @param enable true, if (network) images should be loaded.
|
|
|
|
* false, otherwise.
|
|
|
|
*/
|
2011-02-14 12:27:40 -05:00
|
|
|
public void setLoadPictures(boolean enable) {
|
2011-02-14 11:40:52 -05:00
|
|
|
mMessageContentView.blockNetworkData(!enable);
|
|
|
|
setShowPictures(enable);
|
|
|
|
showShowPicturesSection(false);
|
2011-02-11 10:09:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2011-02-11 12:10:45 -05:00
|
|
|
|
|
|
|
|
2011-02-11 10:09:15 -05:00
|
|
|
} catch (Exception me) {
|
|
|
|
Log.e(K9.LOG_TAG, "setHeaders - error", me);
|
|
|
|
}
|
2011-02-11 12:11:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setShowDownloadButton(Message message) {
|
2011-02-11 10:09:15 -05:00
|
|
|
if (message.isSet(Flag.X_DOWNLOADED_FULL)) {
|
|
|
|
mDownloadRemainder.setVisibility(View.GONE);
|
|
|
|
} else {
|
|
|
|
mDownloadRemainder.setEnabled(true);
|
|
|
|
mDownloadRemainder.setVisibility(View.VISIBLE);
|
|
|
|
}
|
|
|
|
}
|
2011-02-11 12:10:45 -05:00
|
|
|
|
|
|
|
public void setOnFlagListener(OnClickListener listener) {
|
|
|
|
mHeaderContainer.setOnFlagListener(listener);
|
|
|
|
}
|
|
|
|
|
2011-02-11 10:09:15 -05:00
|
|
|
public void showAllHeaders() {
|
|
|
|
mHeaderContainer.onShowAdditionalHeaders();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean additionalHeadersVisible() {
|
|
|
|
return mHeaderContainer.additionalHeadersVisible();
|
|
|
|
}
|
|
|
|
|
2011-02-14 12:27:40 -05:00
|
|
|
public void displayMessageBody(Account account, String folder, String uid, Message message, PgpData pgpData) throws MessagingException {
|
|
|
|
// TODO - really this code path? this is an odd place to put it
|
|
|
|
removeAllAttachments();
|
|
|
|
|
|
|
|
String type;
|
|
|
|
String text = pgpData.getDecryptedData();
|
|
|
|
if (text != null) {
|
|
|
|
type = "text/plain";
|
|
|
|
} else {
|
|
|
|
// getTextForDisplay() always returns HTML-ified content.
|
|
|
|
text = ((LocalStore.LocalMessage) message).getTextForDisplay();
|
|
|
|
type = "text/html";
|
|
|
|
}
|
|
|
|
if (text != null) {
|
|
|
|
final String emailText = text;
|
|
|
|
final String contentType = type;
|
|
|
|
loadBodyFromText(account.getCryptoProvider(), pgpData, message, emailText, contentType);
|
|
|
|
// If the message contains external pictures and the "Show pictures"
|
|
|
|
// button wasn't already pressed, see if the user's preferences has us
|
|
|
|
// showing them anyway.
|
|
|
|
if (Utility.hasExternalImages(text) && !showPictures()) {
|
2011-10-01 14:30:22 -04:00
|
|
|
Address[] from = message.getFrom();
|
2011-02-14 12:27:40 -05:00
|
|
|
if ((account.getShowPictures() == Account.ShowPictures.ALWAYS) ||
|
|
|
|
((account.getShowPictures() == Account.ShowPictures.ONLY_FROM_CONTACTS) &&
|
2011-10-01 14:30:22 -04:00
|
|
|
// Make sure we have at least one from address
|
|
|
|
(from != null && from.length > 0) &&
|
|
|
|
mContacts.isInContacts(from[0].getAddress()))) {
|
2011-02-14 12:27:40 -05:00
|
|
|
setLoadPictures(true);
|
|
|
|
} else {
|
|
|
|
showShowPicturesSection(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
loadBodyFromUrl("file:///android_asset/empty.html");
|
|
|
|
}
|
|
|
|
}
|
2011-02-11 10:09:15 -05:00
|
|
|
|
2011-02-11 12:09:47 -05:00
|
|
|
public void loadBodyFromUrl(String url) {
|
2011-02-11 10:09:15 -05:00
|
|
|
mMessageContentView.loadUrl(url);
|
2011-02-11 12:12:09 -05:00
|
|
|
mCryptoView.hide();
|
2011-02-11 10:09:15 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2011-02-11 12:13:18 -05:00
|
|
|
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
|
2011-04-24 00:00:10 -04:00
|
|
|
view.setCallback(attachmentCallback);
|
2011-02-11 10:09:15 -05:00
|
|
|
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() {
|
2011-02-14 11:47:39 -05:00
|
|
|
setLoadPictures(false);
|
2011-02-11 10:09:15 -05:00
|
|
|
mMessageContentView.scrollTo(0, 0);
|
|
|
|
mHeaderContainer.setVisibility(View.GONE);
|
|
|
|
mMessageContentView.clearView();
|
|
|
|
mAttachments.removeAllViews();
|
|
|
|
}
|
2011-04-24 00:00:10 -04:00
|
|
|
|
|
|
|
public AttachmentView.AttachmentFileDownloadCallback getAttachmentCallback() {
|
|
|
|
return attachmentCallback;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setAttachmentCallback(
|
|
|
|
AttachmentView.AttachmentFileDownloadCallback attachmentCallback) {
|
|
|
|
this.attachmentCallback = attachmentCallback;
|
|
|
|
}
|
|
|
|
|
2011-02-10 19:29:46 -05:00
|
|
|
}
|