1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-08-13 17:03:48 -04:00
k-9/src/com/fsck/k9/view/SingleMessageView.java
m0viefreak f1baa8f461 invalidate message content WebView when the message header changes
This fixes redraw issues where text appears doubled or is cut off
2012-03-02 05:16:58 +01:00

559 lines
20 KiB
Java

package com.fsck.k9.view;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
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.activity.K9Activity;
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.helper.Contacts;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.*;
import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import java.util.List;
public class SingleMessageView extends LinearLayout implements OnClickListener,
MessageHeader.OnLayoutChangedListener {
private boolean mScreenReaderEnabled;
private MessageCryptoView mCryptoView;
private MessageWebView mMessageContentView;
private AccessibleWebView mAccessibleMessageContentView;
private MessageHeader mHeaderContainer;
private LinearLayout mAttachments;
private Button mShowHiddenAttachments;
private LinearLayout mHiddenAttachments;
private View mShowPicturesAction;
private View mShowMessageAction;
private View mShowAttachmentsAction;
private boolean mShowPictures;
private boolean mHasAttachments;
private Button mDownloadRemainder;
private LayoutInflater mInflater;
private Contacts mContacts;
private AttachmentView.AttachmentFileDownloadCallback attachmentCallback;
private LinearLayout mHeaderPlaceHolder;
private LinearLayout mTitleBarHeaderContainer;
private View mAttachmentsContainer;
private LinearLayout mInsideAttachmentsContainer;
private SavedState mSavedState;
public void initialize(Activity activity) {
mMessageContentView = (MessageWebView) findViewById(R.id.message_content);
mAccessibleMessageContentView = (AccessibleWebView) findViewById(R.id.accessible_message_content);
mMessageContentView.configure();
mHeaderPlaceHolder = (LinearLayout) findViewById(R.id.message_view_header_container);
mHeaderContainer = (MessageHeader) findViewById(R.id.header_container);
mHeaderContainer.setOnLayoutChangedListener(this);
mAttachmentsContainer = findViewById(R.id.attachments_container);
mInsideAttachmentsContainer = (LinearLayout) findViewById(R.id.inside_attachments_container);
mAttachments = (LinearLayout) findViewById(R.id.attachments);
mHiddenAttachments = (LinearLayout) findViewById(R.id.hidden_attachments);
mHiddenAttachments.setVisibility(View.GONE);
mShowHiddenAttachments = (Button) findViewById(R.id.show_hidden_attachments);
mShowHiddenAttachments.setVisibility(View.GONE);
mCryptoView = (MessageCryptoView) findViewById(R.id.layout_decrypt);
mCryptoView.setActivity(activity);
mCryptoView.setupChildViews();
mShowPicturesAction = findViewById(R.id.show_pictures);
mShowMessageAction = findViewById(R.id.show_message);
mShowAttachmentsAction = findViewById(R.id.show_attachments);
mShowPictures = false;
mContacts = Contacts.getInstance(activity);
mInflater = activity.getLayoutInflater();
mDownloadRemainder = (Button) findViewById(R.id.download_remainder);
mDownloadRemainder.setVisibility(View.GONE);
mAttachmentsContainer.setVisibility(View.GONE);
if (isScreenReaderActive(activity)) {
mAccessibleMessageContentView.setVisibility(View.VISIBLE);
mMessageContentView.setVisibility(View.GONE);
mScreenReaderEnabled = true;
} else {
mAccessibleMessageContentView.setVisibility(View.GONE);
mMessageContentView.setVisibility(View.VISIBLE);
mScreenReaderEnabled = false;
mHeaderPlaceHolder.removeView(mHeaderContainer);
// the HTC version of WebView tries to force the background of the
// titlebar, which is really unfair.
mHeaderContainer.setBackgroundColor(((K9Activity)activity).getThemeBackgroundColor());
mTitleBarHeaderContainer = new LinearLayout(activity);
mTitleBarHeaderContainer.addView(mHeaderContainer);
mMessageContentView.wrapSetTitleBar(mTitleBarHeaderContainer);
}
mShowHiddenAttachments.setOnClickListener(this);
mShowMessageAction.setOnClickListener(this);
mShowAttachmentsAction.setOnClickListener(this);
mShowPicturesAction.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.show_hidden_attachments: {
onShowHiddenAttachments();
break;
}
case R.id.show_message: {
onShowMessage();
break;
}
case R.id.show_attachments: {
onShowAttachments();
break;
}
case R.id.show_pictures: {
setLoadPictures(true);
break;
}
}
}
private void onShowHiddenAttachments() {
mShowHiddenAttachments.setVisibility(View.GONE);
mHiddenAttachments.setVisibility(View.VISIBLE);
}
public void onShowMessage() {
showShowMessageAction(false);
showAttachments(false);
showShowAttachmentsAction(mHasAttachments);
showMessageWebView(true);
}
public void onShowAttachments() {
showMessageWebView(false);
showShowAttachmentsAction(false);
showShowMessageAction(true);
showAttachments(true);
}
public SingleMessageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private boolean isScreenReaderActive(Activity activity) {
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(
screenReaderIntent, 0);
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
+ ".providers.StatusProvider"), null, null, null, null);
try {
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);
if (status == 1) {
return true;
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
return false;
}
public boolean showPictures() {
return mShowPictures;
}
public void setShowPictures(Boolean show) {
mShowPictures = show;
}
/**
* 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.
*/
public void setLoadPictures(boolean enable) {
mMessageContentView.blockNetworkData(!enable);
setShowPictures(enable);
showShowPicturesAction(false);
}
public Button downloadRemainderButton() {
return mDownloadRemainder;
}
public void showShowPicturesAction(boolean show) {
mShowPicturesAction.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void showShowMessageAction(boolean show) {
mShowMessageAction.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void showShowAttachmentsAction(boolean show) {
mShowAttachmentsAction.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void setHeaders(final Message message, Account account) {
try {
mHeaderContainer.populate(message, account);
mHeaderContainer.setVisibility(View.VISIBLE);
} 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 setMessage(Account account, LocalMessage message, PgpData pgpData,
MessagingController controller, MessagingListener listener) throws MessagingException {
resetView();
String type;
String text = pgpData.getDecryptedData();
if (text != null) {
type = "text/plain";
} else {
// getTextForDisplay() always returns HTML-ified content.
text = message.getTextForDisplay();
type = "text/html";
}
if (text != null) {
final String emailText = text;
final String contentType = type;
loadBodyFromText(emailText, contentType);
updateCryptoLayout(account.getCryptoProvider(), pgpData, message);
} else {
loadBodyFromUrl("file:///android_asset/empty.html");
}
mHasAttachments = message.hasAttachments();
if (mHasAttachments) {
renderAttachments(message, 0, message, account, controller, listener);
}
mHiddenAttachments.setVisibility(View.GONE);
boolean lookForImages = true;
if (mSavedState != null) {
if (mSavedState.showPictures) {
setLoadPictures(true);
lookForImages = false;
}
if (mSavedState.attachmentViewVisible) {
onShowAttachments();
} else {
onShowMessage();
}
if (mSavedState.hiddenAttachmentsVisible) {
onShowHiddenAttachments();
}
mSavedState = null;
} else {
onShowMessage();
}
if (text != null && lookForImages) {
// 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()) {
Address[] from = message.getFrom();
if ((account.getShowPictures() == Account.ShowPictures.ALWAYS) ||
((account.getShowPictures() == Account.ShowPictures.ONLY_FROM_CONTACTS) &&
// Make sure we have at least one from address
(from != null && from.length > 0) &&
mContacts.isInContacts(from[0].getAddress()))) {
setLoadPictures(true);
} else {
showShowPicturesAction(true);
}
}
}
}
public void loadBodyFromUrl(String url) {
mMessageContentView.loadUrl(url);
mCryptoView.hide();
}
private void loadBodyFromText(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);
}
}
public void updateCryptoLayout(CryptoProvider cp, PgpData pgpData, Message message) {
mCryptoView.updateLayout(cp, pgpData, message);
}
public void showAttachments(boolean show) {
mAttachmentsContainer.setVisibility(show ? View.VISIBLE : View.GONE);
boolean showHidden = (show && mHiddenAttachments.getVisibility() == View.GONE &&
mHiddenAttachments.getChildCount() > 0);
mShowHiddenAttachments.setVisibility(showHidden ? View.VISIBLE : View.GONE);
if (show) {
moveHeaderToLayout();
} else {
moveHeaderToWebViewTitleBar();
}
}
public void showMessageWebView(boolean show) {
mMessageContentView.setVisibility(show ? View.VISIBLE : View.GONE);
}
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) {
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
view.setCallback(attachmentCallback);
try {
if (view.populateFromPart(part, message, account, controller, listener)) {
addAttachment(view);
} else {
addHiddenAttachment(view);
}
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Error adding attachment view", e);
}
}
}
public void addAttachment(View attachmentView) {
mAttachments.addView(attachmentView);
}
public void addHiddenAttachment(View attachmentView) {
mHiddenAttachments.addView(attachmentView);
}
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() {
mDownloadRemainder.setVisibility(View.GONE);
setLoadPictures(false);
showShowAttachmentsAction(false);
showShowMessageAction(false);
showShowPicturesAction(false);
mAttachments.removeAllViews();
mHiddenAttachments.removeAllViews();
/*
* Clear the WebView content
*
* For some reason WebView.clearView() doesn't clear the contents when the WebView changes
* its size because the button to download the complete message was previously shown and
* is now hidden.
*/
loadBodyFromText("", "text/plain");
}
public void resetHeaderView() {
mHeaderContainer.setVisibility(View.GONE);
}
public AttachmentView.AttachmentFileDownloadCallback getAttachmentCallback() {
return attachmentCallback;
}
public void setAttachmentCallback(
AttachmentView.AttachmentFileDownloadCallback attachmentCallback) {
this.attachmentCallback = attachmentCallback;
}
private void moveHeaderToLayout() {
if (mTitleBarHeaderContainer != null && mTitleBarHeaderContainer.getChildCount() != 0) {
mTitleBarHeaderContainer.removeView(mHeaderContainer);
mInsideAttachmentsContainer.addView(mHeaderContainer, 0);
}
}
private void moveHeaderToWebViewTitleBar() {
if (mTitleBarHeaderContainer != null && mTitleBarHeaderContainer.getChildCount() == 0) {
mInsideAttachmentsContainer.removeView(mHeaderContainer);
mTitleBarHeaderContainer.addView(mHeaderContainer);
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.attachmentViewVisible = (mAttachmentsContainer != null &&
mAttachmentsContainer.getVisibility() == View.VISIBLE);
savedState.hiddenAttachmentsVisible = (mHiddenAttachments != null &&
mHiddenAttachments.getVisibility() == View.VISIBLE);
savedState.showPictures = mShowPictures;
return savedState;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if(!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState savedState = (SavedState)state;
super.onRestoreInstanceState(savedState.getSuperState());
mSavedState = savedState;
}
public void onLayoutChanged() {
if (mMessageContentView != null) {
mMessageContentView.invalidate();
}
}
static class SavedState extends BaseSavedState {
boolean attachmentViewVisible;
boolean hiddenAttachmentsVisible;
boolean showPictures;
@SuppressWarnings("hiding")
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.attachmentViewVisible = (in.readInt() != 0);
this.hiddenAttachmentsVisible = (in.readInt() != 0);
this.showPictures = (in.readInt() != 0);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt((this.attachmentViewVisible) ? 1 : 0);
out.writeInt((this.hiddenAttachmentsVisible) ? 1 : 0);
out.writeInt((this.showPictures) ? 1 : 0);
}
}
}