<commit_before>
package com.android.email.activity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Map;

import org.apache.commons.io.IOUtils;

import com.android.email.K9Activity;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;


import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.Contacts;
import android.provider.Contacts.Intents;
import android.util.Config;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.MotionEvent;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.CacheManager;
import android.webkit.UrlInterceptHandler;
import android.webkit.WebView;
import android.webkit.CacheManager.CacheResult;
import android.webkit.PluginData;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.android.email.Account;
import com.android.email.Email;
import com.android.email.MessagingController;
import com.android.email.MessagingListener;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.mail.Flag;
import com.android.email.mail.Message;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Multipart;
import com.android.email.mail.Part;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.mail.store.LocalStore.LocalAttachmentBodyPart;
import com.android.email.mail.store.LocalStore.LocalMessage;
import com.android.email.mail.store.LocalStore.LocalTextBody;
import com.android.email.provider.AttachmentProvider;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.List;

public class MessageView extends K9Activity
            implements UrlInterceptHandler, OnClickListener
{
    private static final String EXTRA_ACCOUNT = "com.android.email.MessageView_account";
    private static final String EXTRA_FOLDER = "com.android.email.MessageView_folder";
    private static final String EXTRA_MESSAGE = "com.android.email.MessageView_message";
    private static final String EXTRA_MESSAGE_UIDS = "com.android.email.MessageView_messageUids";
    private static final String EXTRA_NEXT = "com.android.email.MessageView_next";

    private static final String CID_PREFIX  = "http://cid/";

    private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;

    private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;

    private java.text.DateFormat mDateFormat;

    private java.text.DateFormat mTimeFormat;


    private TextView mFromView;
    private TextView mDateView;
    private TextView mTimeView;
    private TextView mToView;
    private TextView mCcView;
    private TextView mSubjectView;
    private CheckBox mFlagged;
    private int defaultSubjectColor;
    private ScrollView mTopView;
    private WebView mMessageContentView;
    private LinearLayout mAttachments;
    private View mAttachmentIcon;
    private View mShowPicturesSection;
    View next;
    View next_scrolling;
    View previous;
    View previous_scrolling;

    private Account mAccount;
    private String mFolder;
    private String mMessageUid;
    private ArrayList<String> mMessageUids;

    private Message mMessage;
    private String mNextMessageUid = null;
    private String mPreviousMessageUid = null;

    private static final float SWIPE_MIN_DISTANCE_DIP = 100.0f;
    private static final float SWIPE_MAX_OFF_PATH_DIP = 250f;
    private static final float SWIPE_THRESHOLD_VELOCITY_DIP = 200f;

    private GestureDetector gestureDetector;

    private Menu optionsMenu = null;

    private Listener mListener = new Listener();
    private MessageViewHandler mHandler = new MessageViewHandler();


    @Override
    public boolean dispatchKeyEvent(KeyEvent event)
    {
        boolean ret = false;

        if (KeyEvent.ACTION_DOWN == event.getAction())
        {
            ret = onKeyDown(event.getKeyCode(), event);
        }
        if (ret == false)
        {
            ret = super.dispatchKeyEvent(event);
        }
        return ret;
    }

    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        switch (keyCode)
        {
            case KeyEvent.KEYCODE_DEL:
            {
                onDelete();
                return true;
            }
            case KeyEvent.KEYCODE_D:
            {
                onDelete();
                return true;
            }
            case KeyEvent.KEYCODE_F:
            {
                onForward();
                return true;
            }
            case KeyEvent.KEYCODE_A:
            {
                onReplyAll();
                return true;
            }
            case KeyEvent.KEYCODE_R:
            {
                onReply();
                return true;
            }
            case KeyEvent.KEYCODE_G:
            {
                onFlag();
                return true;
            }

            case KeyEvent.KEYCODE_M:
            {
                onMove();
                return true;
            }
            case KeyEvent.KEYCODE_Y:
            {
                onCopy();
                return true;
            }
            case KeyEvent.KEYCODE_J:
            case KeyEvent.KEYCODE_P:
            {
                onPrevious();
                return true;
            }
            case KeyEvent.KEYCODE_N:
            case KeyEvent.KEYCODE_K:
            {
                onNext();
                return true;
            }
            case KeyEvent.KEYCODE_Z:
            {
                if (event.isShiftPressed())
                {
                    mHandler.post(new Runnable()
                    {
                        public void run()
                        {
                            mMessageContentView.zoomIn();
                        }
                    });
                }
                else
                {
                    mHandler.post(new Runnable()
                    {
                        public void run()
                        {
                            mMessageContentView.zoomOut();
                        }
                    });
                }
                return true;
            }
            case KeyEvent.KEYCODE_H:
            {
                Toast toast = Toast.makeText(this, R.string.message_help_key, Toast.LENGTH_LONG);
                toast.show();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    class MessageViewHandler extends Handler
    {
        private static final int MSG_PROGRESS = 2;
        private static final int MSG_ADD_ATTACHMENT = 3;
        private static final int MSG_SET_ATTACHMENTS_ENABLED = 4;
        private static final int MSG_SET_HEADERS = 5;
        private static final int MSG_NETWORK_ERROR = 6;
        private static final int MSG_ATTACHMENT_SAVED = 7;
        private static final int MSG_ATTACHMENT_NOT_SAVED = 8;
        private static final int MSG_SHOW_SHOW_PICTURES = 9;
        private static final int MSG_FETCHING_ATTACHMENT = 10;
        private static final int MSG_INVALID_ID_ERROR = 11;

        private static final int FLAG_FLAGGED = 1;
        private static final int FLAG_ANSWERED = 2;

        @Override
        public void handleMessage(android.os.Message msg)
        {
            switch (msg.what)
            {
                case MSG_PROGRESS:
                    setProgressBarIndeterminateVisibility(msg.arg1 != 0);
                    break;
                case MSG_ADD_ATTACHMENT:
                    mAttachments.addView((View) msg.obj);
                    mAttachments.setVisibility(View.VISIBLE);
                    break;
                case MSG_SET_ATTACHMENTS_ENABLED:
                    for (int i = 0, count = mAttachments.getChildCount(); i < count; i++)
                    {
                        Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
                        attachment.viewButton.setEnabled(msg.arg1 == 1);
                        attachment.downloadButton.setEnabled(msg.arg1 == 1);
                    }
                    break;
                case MSG_SET_HEADERS:
                    String[] values = (String[]) msg.obj;
                    setTitle(values[0]);
                    mSubjectView.setText(values[0]);
                    mFromView.setText(values[1]);
                    if (values[2]!=null)
                    {
                        mDateView.setText(values[2]);
                        mDateView.setVisibility(View.VISIBLE);
                    }
                    else
                    {
                        mDateView.setVisibility(View.GONE);
                    }
                    mTimeView.setText(values[3]);
                    mToView.setText(values[4]);
                    mCcView.setText(values[5]);
                    mAttachmentIcon.setVisibility(msg.arg1 == 1 ? View.VISIBLE : View.GONE);
                    if ((msg.arg2 & FLAG_FLAGGED) != 0)
                    {
                        mFlagged.setChecked(true);
                    }
                    else
                    {
                        mFlagged.setChecked(false);
                    }
                    mSubjectView.setTextColor(0xff000000 | defaultSubjectColor);


                    if ((msg.arg2 & FLAG_ANSWERED) != 0)
                    {
                        Drawable answeredIcon = getResources().getDrawable(
                                                    R.drawable.ic_mms_answered_small);
                        mSubjectView.setCompoundDrawablesWithIntrinsicBounds(
                            answeredIcon, // left
                            null, // top
                            null, // right
                            null); // bottom
                    }
                    else
                    {
                        mSubjectView.setCompoundDrawablesWithIntrinsicBounds(
                            null, // left
                            null, // top
                            null, // right
                            null); // bottom
                    }

                    break;
                case MSG_NETWORK_ERROR:
                    Toast.makeText(MessageView.this,
                                   R.string.status_network_error, Toast.LENGTH_LONG).show();
                    break;
                case MSG_INVALID_ID_ERROR:
                    Toast.makeText(MessageView.this,
                                   R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
                    break;
                case MSG_ATTACHMENT_SAVED:
                    Toast.makeText(MessageView.this, String.format(
                                       getString(R.string.message_view_status_attachment_saved), msg.obj),
                                   Toast.LENGTH_LONG).show();
                    break;
                case MSG_ATTACHMENT_NOT_SAVED:
                    Toast.makeText(MessageView.this,
                                   getString(R.string.message_view_status_attachment_not_saved),
                                   Toast.LENGTH_LONG).show();
                    break;
                case MSG_SHOW_SHOW_PICTURES:
                    mShowPicturesSection.setVisibility(msg.arg1 == 1 ? View.VISIBLE : View.GONE);
                    break;
                case MSG_FETCHING_ATTACHMENT:
                    Toast.makeText(MessageView.this,
                                   getString(R.string.message_view_fetching_attachment_toast),
                                   Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

        public void progress(boolean progress)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_PROGRESS;
            msg.arg1 = progress ? 1 : 0;
            sendMessage(msg);
        }

        public void addAttachment(View attachmentView)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_ADD_ATTACHMENT;
            msg.obj = attachmentView;
            sendMessage(msg);
        }

        public void setAttachmentsEnabled(boolean enabled)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_SET_ATTACHMENTS_ENABLED;
            msg.arg1 = enabled ? 1 : 0;
            sendMessage(msg);
        }

        public void setHeaders(
            String subject,
            String from,
            String date,
            String time,
            String to,
            String cc,
            boolean hasAttachments,
            boolean flagged,
            boolean seen)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_SET_HEADERS;
            msg.arg1 = hasAttachments ? 1 : 0;
            msg.arg2 += (flagged ? FLAG_FLAGGED : 0);
            msg.arg2 += (seen ? FLAG_ANSWERED : 0);

            msg.obj = new String[] { subject, from, date, time, to, cc };
            sendMessage(msg);
        }

        public void networkError()
        {
            sendEmptyMessage(MSG_NETWORK_ERROR);
        }

        public void invalidIdError()
        {
            sendEmptyMessage(MSG_INVALID_ID_ERROR);
        }

        public void attachmentSaved(String filename)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_ATTACHMENT_SAVED;
            msg.obj = filename;
            sendMessage(msg);
        }

        public void attachmentNotSaved()
        {
            sendEmptyMessage(MSG_ATTACHMENT_NOT_SAVED);
        }

        public void fetchingAttachment()
        {
            sendEmptyMessage(MSG_FETCHING_ATTACHMENT);
        }

        public void showShowPictures(boolean show)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_SHOW_SHOW_PICTURES;
            msg.arg1 = show ? 1 : 0;
            sendMessage(msg);
        }



    }

    class Attachment
    {
        public String name;
        public String contentType;
        public long size;
        public LocalAttachmentBodyPart part;
        public Button viewButton;
        public Button downloadButton;
        public ImageView iconView;
    }

    public static void actionView(Context context, Account account, String folder, String messageUid, ArrayList<String> folderUids)
    {
        actionView(context, account, folder, messageUid, folderUids, null);
    }

    public static void actionView(Context context, Account account, String folder, String messageUid, ArrayList<String> folderUids, Bundle extras)
    {
        Intent i = new Intent(context, MessageView.class);
        i.putExtra(EXTRA_ACCOUNT, account);
        i.putExtra(EXTRA_FOLDER, folder);
        i.putExtra(EXTRA_MESSAGE, messageUid);
        i.putExtra(EXTRA_MESSAGE_UIDS, folderUids);
        if (extras != null)
        {
            i.putExtras(extras);
        }
        context.startActivity(i);
    }

    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);


        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.message_view);


        mFromView = (TextView)findViewById(R.id.from);
        mToView = (TextView)findViewById(R.id.to);
        mCcView = (TextView)findViewById(R.id.cc);
        mSubjectView = (TextView)findViewById(R.id.subject);
        defaultSubjectColor = mSubjectView.getCurrentTextColor();


        mDateView = (TextView)findViewById(R.id.date);
        mTimeView = (TextView)findViewById(R.id.time);
        mTopView = (ScrollView)findViewById(R.id.top_view);
        mMessageContentView = (WebView)findViewById(R.id.message_content);

        mAttachments = (LinearLayout)findViewById(R.id.attachments);
        mAttachmentIcon = findViewById(R.id.attachment);
        mShowPicturesSection = findViewById(R.id.show_pictures_section);


        mFlagged = (CheckBox)findViewById(R.id.flagged);
        mFlagged.setOnClickListener(new OnClickListener()
        {
            public void onClick(View v)
            {
                onFlag();
            }
        });

        mMessageContentView.setVerticalScrollBarEnabled(true);
        mMessageContentView.setVerticalScrollbarOverlay(true);
        mMessageContentView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

        final WebSettings webSettings = mMessageContentView.getSettings();

        webSettings.setSupportZoom(true);
        webSettings.setLoadsImagesAutomatically(true);
        //webSettings.setBuiltInZoomControls(true);
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);

        mAttachments.setVisibility(View.GONE);
        mAttachmentIcon.setVisibility(View.GONE);

        setOnClickListener(R.id.from);
        setOnClickListener(R.id.reply);
        setOnClickListener(R.id.reply_all);
        setOnClickListener(R.id.delete);
        setOnClickListener(R.id.forward);
        setOnClickListener(R.id.next);
        setOnClickListener(R.id.previous);

        setOnClickListener(R.id.reply_scrolling);
//       setOnClickListener(R.id.reply_all_scrolling);
        setOnClickListener(R.id.delete_scrolling);
        setOnClickListener(R.id.forward_scrolling);
        setOnClickListener(R.id.next_scrolling);
        setOnClickListener(R.id.previous_scrolling);

        setOnClickListener(R.id.show_pictures);


        setTitle("");

        Intent intent = getIntent();
        Uri uri = intent.getData();

        if (icicle!=null)
        {
            mAccount = (Account) icicle.getSerializable(EXTRA_ACCOUNT);
            mFolder = icicle.getString(EXTRA_FOLDER);
            mMessageUid = icicle.getString(EXTRA_MESSAGE);
            mMessageUids = icicle.getStringArrayList(EXTRA_MESSAGE_UIDS);
        }
        else
        {
            if (uri==null)
            {
                mAccount = (Account) intent.getSerializableExtra(EXTRA_ACCOUNT);
                mFolder = intent.getStringExtra(EXTRA_FOLDER);
                mMessageUid = intent.getStringExtra(EXTRA_MESSAGE);
                mMessageUids = intent.getStringArrayListExtra(EXTRA_MESSAGE_UIDS);

                Log.v(Email.LOG_TAG, "mAccount number: " + mAccount.getAccountNumber());
                Log.v(Email.LOG_TAG, "mFolder: " + mFolder);
                Log.v(Email.LOG_TAG, "mMessageUid: " + mMessageUid);
            }
            else
            {
                Log.v(Email.LOG_TAG, "uri: " + uri.toString());
                List<String> segmentList = uri.getPathSegments();
                Log.v(Email.LOG_TAG, "segmentList size: " + segmentList.size());
                if (segmentList.size()==3)
                {
                    String accountId = segmentList.get(0);
                    Account[] accounts = Preferences.getPreferences(this).getAccounts();
                    Log.v(Email.LOG_TAG, "account.length: " + accounts.length);
                    boolean found = false;
                    for (Account account : accounts)
                    {
                        Log.v(Email.LOG_TAG, "account: name=" + account.getDescription() + " number=" + account.getAccountNumber());
                        if (String.valueOf(account.getAccountNumber()).equals(accountId))
                        {
                            mAccount = account;
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        //TODO: Use ressource to externalize message
                        Toast.makeText(this, "Invalid account id: " + accountId, Toast.LENGTH_LONG).show();
                        return;
                    }
                    mFolder = segmentList.get(1);
                    mMessageUid = segmentList.get(2);
                    mMessageUids = new ArrayList<String>();
                }
                else
                {
                    for (String segment : segmentList)
                    {
                        Log.v(Email.LOG_TAG, "segment: " + segment);
                    }
                    //TODO: Use ressource to externalize message
                    Toast.makeText(this, "Invalid intent uri: " + uri.toString(), Toast.LENGTH_LONG).show();
                    return;
                }
            }
        }

        next = findViewById(R.id.next);
        previous = findViewById(R.id.previous);

        setOnClickListener(R.id.next);
        setOnClickListener(R.id.previous);

        next_scrolling = findViewById(R.id.next_scrolling);
        previous_scrolling = findViewById(R.id.previous_scrolling);


        // Gesture detection
        gestureDetector = new GestureDetector(new MyGestureDetector());

        boolean goNext = intent.getBooleanExtra(EXTRA_NEXT, false);
        if (goNext)
        {
            next.requestFocus();
        }

        Account.HideButtons hideButtons = mAccount.getHideMessageViewButtons();

        //MessagingController.getInstance(getApplication()).addListener(mListener);
        if (Account.HideButtons.ALWAYS == hideButtons)
        {
            hideButtons();
        }
        else if (Account.HideButtons.NEVER == hideButtons)
        {
            showButtons();
        }
        else   // Account.HideButtons.KEYBOARD_AVAIL
        {
            final Configuration config = this.getResources().getConfiguration();
            if (config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO)
            {
                hideButtons();
            }
            else
            {
                showButtons();
            }
        }
        displayMessage(mMessageUid);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        outState.putSerializable(EXTRA_ACCOUNT, mAccount);
        outState.putString(EXTRA_FOLDER, mFolder);
        outState.putString(EXTRA_MESSAGE, mMessageUid);
        outState.putStringArrayList(EXTRA_MESSAGE_UIDS, mMessageUids);
    }

    private void displayMessage(String uid)
    {
        mMessageUid = uid;
        mMessageContentView.getSettings().setBlockNetworkImage(true);

        mAttachments.removeAllViews();
        findSurroundingMessagesUid();

        next.setEnabled(mNextMessageUid != null);
        previous.setEnabled(mPreviousMessageUid != null);

        if (next_scrolling != null)
            next_scrolling.setEnabled(mNextMessageUid != null);
        if (previous_scrolling != null)
            previous_scrolling.setEnabled(mPreviousMessageUid != null);

        MessagingController.getInstance(getApplication()).loadMessageForView(
            mAccount,
            mFolder,
            mMessageUid,
            mListener);

        mTopView.scrollTo(0, 0);
        mMessageContentView.scrollTo(0, 0);
    }


    private void showButtons()
    {
        View buttons = findViewById(R.id.scrolling_buttons);
        if (buttons != null)
        {
            buttons.setVisibility(View.GONE);
        }
    }

    private void hideButtons()
    {
        View buttons = findViewById(R.id.bottom_buttons);
        if (buttons != null)
        {
            buttons.setVisibility(View.GONE);
        }
    }

    private void setOnClickListener(int viewCode)
    {
        View thisView = findViewById(viewCode);
        if (thisView != null)
        {
            thisView.setOnClickListener(this);
        }
    }

    private void findSurroundingMessagesUid()
    {
        mNextMessageUid = mPreviousMessageUid = null;
        int i = mMessageUids.indexOf(mMessageUid);
        if (i < 0)
            return;
        if (i != 0)
            mNextMessageUid = mMessageUids.get(i - 1);
        if (i != (mMessageUids.size() - 1))
            mPreviousMessageUid = mMessageUids.get(i + 1);
    }

    public void onResume()
    {
        super.onResume();

        mDateFormat = DateFormatter.getDateFormat(this);
        mTimeFormat = android.text.format.DateFormat.getTimeFormat(this);   // 12/24 date format


    }

    private void onDelete()
    {
        if (mMessage != null)
        {
            Message messageToDelete = mMessage;
            String folderForDelete = mFolder;
            Account accountForDelete = mAccount;

            findSurroundingMessagesUid();

            // Remove this message's Uid locally
            mMessageUids.remove(messageToDelete.getUid());

            MessagingController.getInstance(getApplication()).deleteMessages(
                accountForDelete,
                folderForDelete,
                new Message[] { messageToDelete },
                null);
            if (mNextMessageUid != null)
            {
                onNext();
            }
            else if (mPreviousMessageUid != null)
            {
                onPrevious();
            }
            else
            {
                finish();
            }
        }
    }

    private void onClickSender()
    {
        if (mMessage != null)
        {
            try
            {
                Address senderEmail = mMessage.getFrom()[0];
                Uri contactUri = Uri.fromParts("mailto", senderEmail.getAddress(), null);

                Intent contactIntent = new Intent(Contacts.Intents.SHOW_OR_CREATE_CONTACT);
                contactIntent.setData(contactUri);

                // Pass along full E-mail string for possible create dialog
                contactIntent.putExtra(Contacts.Intents.EXTRA_CREATE_DESCRIPTION,
                                       senderEmail.toString());

                // Only provide personal name hint if we have one
                String senderPersonal = senderEmail.getPersonal();
                if (senderPersonal != null)
                {
                    contactIntent.putExtra(Intents.Insert.NAME, senderPersonal);
                }

                startActivity(contactIntent);
            }
            catch (MessagingException me)
            {
                if (Config.LOGV)
                {
                    Log.v(Email.LOG_TAG, "loadMessageForViewHeadersAvailable", me);
                }
            }
        }
    }

    private void onReply()
    {
        if (mMessage != null)
        {
            MessageCompose.actionReply(this, mAccount, mMessage, false);
            finish();
        }
    }

    private void onReplyAll()
    {
        if (mMessage != null)
        {
            MessageCompose.actionReply(this, mAccount, mMessage, true);
            finish();
        }
    }

    private void onForward()
    {
        if (mMessage != null)
        {
            MessageCompose.actionForward(this, mAccount, mMessage);
            finish();
        }
    }

    private void onFlag()
    {
        if (mMessage != null)
        {
            MessagingController.getInstance(getApplication()).setFlag(mAccount,
                    mMessage.getFolder().getName(), new String[] { mMessage.getUid() }, Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
            try
            {
                mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
                setHeaders(mAccount, mMessage.getFolder().getName(), mMessage.getUid(), mMessage);
                setMenuFlag();
            }
            catch (MessagingException me)
            {
                Log.e(Email.LOG_TAG, "Could not set flag on local message", me);
            }
        }
    }

    private void onMove()
    {
        if (MessagingController.getInstance(getApplication()).isMoveCapable(mAccount) == false)
        {
            return;
        }
        if (MessagingController.getInstance(getApplication()).isMoveCapable(mMessage) == false)
        {
            Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
            toast.show();
            return;
        }
        Intent intent = new Intent(this, ChooseFolder.class);
        intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
        intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, mFolder);
        intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, mMessageUid);
        startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE);
    }

    private void onCopy()
    {
        if (MessagingController.getInstance(getApplication()).isCopyCapable(mAccount) == false)
        {
            return;
        }
        if (MessagingController.getInstance(getApplication()).isCopyCapable(mMessage) == false)
        {
            Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
            toast.show();
            return;
        }
        Intent intent = new Intent(this, ChooseFolder.class);

        intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
        intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, mFolder);
        intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, mMessageUid);
        startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (resultCode != RESULT_OK)
            return;

        switch (requestCode)
        {
            case ACTIVITY_CHOOSE_FOLDER_MOVE:
            case ACTIVITY_CHOOSE_FOLDER_COPY:
                if (data == null)
                    return;
                String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
                String srcFolderName = data.getStringExtra(ChooseFolder.EXTRA_CUR_FOLDER);
                String uid = data.getStringExtra(ChooseFolder.EXTRA_MESSAGE_UID);

                if (uid.equals(mMessageUid) && srcFolderName.equals(mFolder))
                {

                    switch (requestCode)
                    {
                        case ACTIVITY_CHOOSE_FOLDER_MOVE:
                            MessagingController.getInstance(getApplication()).moveMessage(mAccount,
                                    srcFolderName, mMessage, destFolderName, null);
                            break;
                        case ACTIVITY_CHOOSE_FOLDER_COPY:
                            MessagingController.getInstance(getApplication()).copyMessage(mAccount,
                                    srcFolderName, mMessage, destFolderName, null);
                            break;
                    }
                }
        }
    }


    private void onSendAlternate()
    {
        if (mMessage != null)
        {
            MessagingController.getInstance(getApplication()).sendAlternate(this, mAccount, mMessage);

        }
    }

    private void onNext()
    {
        if (mNextMessageUid == null)
        {
            Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
            return;
        }
        displayMessage(mNextMessageUid);
        mTopView.startAnimation(outToLeftAnimation());
        next.requestFocus();
    }

    private void onPrevious()
    {
        if (mPreviousMessageUid == null)
        {
            Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
            return;
        }
        displayMessage(mPreviousMessageUid);
        mTopView.startAnimation(inFromRightAnimation());
        previous.requestFocus();
    }

    private void onMarkAsUnread()
    {
        if (mMessage != null)
        {
            MessagingController.getInstance(getApplication()).setFlag(
                mAccount,
                mFolder,
                new String[] { mMessage.getUid() },
                Flag.SEEN,
                false);
        }
    }

    /**
     * Creates a unique file in the given directory by appending a hyphen
     * and a number to the given filename.
     * @param directory<commit_msg>deleting messages in messageView now preserves the direction the user was "travelling" in before the delete<commit_after>
package com.android.email.activity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Map;

import org.apache.commons.io.IOUtils;

import com.android.email.K9Activity;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;


import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.Contacts;
import android.provider.Contacts.Intents;
import android.util.Config;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.MotionEvent;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.CacheManager;
import android.webkit.UrlInterceptHandler;
import android.webkit.WebView;
import android.webkit.CacheManager.CacheResult;
import android.webkit.PluginData;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.android.email.Account;
import com.android.email.Email;
import com.android.email.MessagingController;
import com.android.email.MessagingListener;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.mail.Flag;
import com.android.email.mail.Message;
import com.android.email.mail.MessagingException;
import com.android.email.mail.Multipart;
import com.android.email.mail.Part;
import com.android.email.mail.Message.RecipientType;
import com.android.email.mail.internet.MimeUtility;
import com.android.email.mail.store.LocalStore.LocalAttachmentBodyPart;
import com.android.email.mail.store.LocalStore.LocalMessage;
import com.android.email.mail.store.LocalStore.LocalTextBody;
import com.android.email.provider.AttachmentProvider;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.List;

public class MessageView extends K9Activity
            implements UrlInterceptHandler, OnClickListener
{
    private static final String EXTRA_ACCOUNT = "com.android.email.MessageView_account";
    private static final String EXTRA_FOLDER = "com.android.email.MessageView_folder";
    private static final String EXTRA_MESSAGE = "com.android.email.MessageView_message";
    private static final String EXTRA_MESSAGE_UIDS = "com.android.email.MessageView_messageUids";
    private static final String EXTRA_NEXT = "com.android.email.MessageView_next";

    private static final String CID_PREFIX  = "http://cid/";

    private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;

    private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;

    private java.text.DateFormat mDateFormat;

    private java.text.DateFormat mTimeFormat;


    private TextView mFromView;
    private TextView mDateView;
    private TextView mTimeView;
    private TextView mToView;
    private TextView mCcView;
    private TextView mSubjectView;
    private CheckBox mFlagged;
    private int defaultSubjectColor;
    private ScrollView mTopView;
    private WebView mMessageContentView;
    private LinearLayout mAttachments;
    private View mAttachmentIcon;
    private View mShowPicturesSection;
    View next;
    View next_scrolling;
    View previous;
    View previous_scrolling;

    private Account mAccount;
    private String mFolder;
    private String mMessageUid;
    private ArrayList<String> mMessageUids;

    private Message mMessage;

    private static final int PREVIOUS = 1;
    private static final int NEXT = 1;

    private int mLastDirection = PREVIOUS;


    private String mNextMessageUid = null;
    private String mPreviousMessageUid = null;

    private static final float SWIPE_MIN_DISTANCE_DIP = 100.0f;
    private static final float SWIPE_MAX_OFF_PATH_DIP = 250f;
    private static final float SWIPE_THRESHOLD_VELOCITY_DIP = 200f;

    private GestureDetector gestureDetector;

    private Menu optionsMenu = null;

    private Listener mListener = new Listener();
    private MessageViewHandler mHandler = new MessageViewHandler();


    @Override
    public boolean dispatchKeyEvent(KeyEvent event)
    {
        boolean ret = false;

        if (KeyEvent.ACTION_DOWN == event.getAction())
        {
            ret = onKeyDown(event.getKeyCode(), event);
        }
        if (ret == false)
        {
            ret = super.dispatchKeyEvent(event);
        }
        return ret;
    }

    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        switch (keyCode)
        {
            case KeyEvent.KEYCODE_DEL:
            {
                onDelete();
                return true;
            }
            case KeyEvent.KEYCODE_D:
            {
                onDelete();
                return true;
            }
            case KeyEvent.KEYCODE_F:
            {
                onForward();
                return true;
            }
            case KeyEvent.KEYCODE_A:
            {
                onReplyAll();
                return true;
            }
            case KeyEvent.KEYCODE_R:
            {
                onReply();
                return true;
            }
            case KeyEvent.KEYCODE_G:
            {
                onFlag();
                return true;
            }

            case KeyEvent.KEYCODE_M:
            {
                onMove();
                return true;
            }
            case KeyEvent.KEYCODE_Y:
            {
                onCopy();
                return true;
            }
            case KeyEvent.KEYCODE_J:
            case KeyEvent.KEYCODE_P:
            {
                onPrevious();
                return true;
            }
            case KeyEvent.KEYCODE_N:
            case KeyEvent.KEYCODE_K:
            {
                onNext();
                return true;
            }
            case KeyEvent.KEYCODE_Z:
            {
                if (event.isShiftPressed())
                {
                    mHandler.post(new Runnable()
                    {
                        public void run()
                        {
                            mMessageContentView.zoomIn();
                        }
                    });
                }
                else
                {
                    mHandler.post(new Runnable()
                    {
                        public void run()
                        {
                            mMessageContentView.zoomOut();
                        }
                    });
                }
                return true;
            }
            case KeyEvent.KEYCODE_H:
            {
                Toast toast = Toast.makeText(this, R.string.message_help_key, Toast.LENGTH_LONG);
                toast.show();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    class MessageViewHandler extends Handler
    {
        private static final int MSG_PROGRESS = 2;
        private static final int MSG_ADD_ATTACHMENT = 3;
        private static final int MSG_SET_ATTACHMENTS_ENABLED = 4;
        private static final int MSG_SET_HEADERS = 5;
        private static final int MSG_NETWORK_ERROR = 6;
        private static final int MSG_ATTACHMENT_SAVED = 7;
        private static final int MSG_ATTACHMENT_NOT_SAVED = 8;
        private static final int MSG_SHOW_SHOW_PICTURES = 9;
        private static final int MSG_FETCHING_ATTACHMENT = 10;
        private static final int MSG_INVALID_ID_ERROR = 11;

        private static final int FLAG_FLAGGED = 1;
        private static final int FLAG_ANSWERED = 2;

        @Override
        public void handleMessage(android.os.Message msg)
        {
            switch (msg.what)
            {
                case MSG_PROGRESS:
                    setProgressBarIndeterminateVisibility(msg.arg1 != 0);
                    break;
                case MSG_ADD_ATTACHMENT:
                    mAttachments.addView((View) msg.obj);
                    mAttachments.setVisibility(View.VISIBLE);
                    break;
                case MSG_SET_ATTACHMENTS_ENABLED:
                    for (int i = 0, count = mAttachments.getChildCount(); i < count; i++)
                    {
                        Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag();
                        attachment.viewButton.setEnabled(msg.arg1 == 1);
                        attachment.downloadButton.setEnabled(msg.arg1 == 1);
                    }
                    break;
                case MSG_SET_HEADERS:
                    String[] values = (String[]) msg.obj;
                    setTitle(values[0]);
                    mSubjectView.setText(values[0]);
                    mFromView.setText(values[1]);
                    if (values[2]!=null)
                    {
                        mDateView.setText(values[2]);
                        mDateView.setVisibility(View.VISIBLE);
                    }
                    else
                    {
                        mDateView.setVisibility(View.GONE);
                    }
                    mTimeView.setText(values[3]);
                    mToView.setText(values[4]);
                    mCcView.setText(values[5]);
                    mAttachmentIcon.setVisibility(msg.arg1 == 1 ? View.VISIBLE : View.GONE);
                    if ((msg.arg2 & FLAG_FLAGGED) != 0)
                    {
                        mFlagged.setChecked(true);
                    }
                    else
                    {
                        mFlagged.setChecked(false);
                    }
                    mSubjectView.setTextColor(0xff000000 | defaultSubjectColor);


                    if ((msg.arg2 & FLAG_ANSWERED) != 0)
                    {
                        Drawable answeredIcon = getResources().getDrawable(
                                                    R.drawable.ic_mms_answered_small);
                        mSubjectView.setCompoundDrawablesWithIntrinsicBounds(
                            answeredIcon, // left
                            null, // top
                            null, // right
                            null); // bottom
                    }
                    else
                    {
                        mSubjectView.setCompoundDrawablesWithIntrinsicBounds(
                            null, // left
                            null, // top
                            null, // right
                            null); // bottom
                    }

                    break;
                case MSG_NETWORK_ERROR:
                    Toast.makeText(MessageView.this,
                                   R.string.status_network_error, Toast.LENGTH_LONG).show();
                    break;
                case MSG_INVALID_ID_ERROR:
                    Toast.makeText(MessageView.this,
                                   R.string.status_invalid_id_error, Toast.LENGTH_LONG).show();
                    break;
                case MSG_ATTACHMENT_SAVED:
                    Toast.makeText(MessageView.this, String.format(
                                       getString(R.string.message_view_status_attachment_saved), msg.obj),
                                   Toast.LENGTH_LONG).show();
                    break;
                case MSG_ATTACHMENT_NOT_SAVED:
                    Toast.makeText(MessageView.this,
                                   getString(R.string.message_view_status_attachment_not_saved),
                                   Toast.LENGTH_LONG).show();
                    break;
                case MSG_SHOW_SHOW_PICTURES:
                    mShowPicturesSection.setVisibility(msg.arg1 == 1 ? View.VISIBLE : View.GONE);
                    break;
                case MSG_FETCHING_ATTACHMENT:
                    Toast.makeText(MessageView.this,
                                   getString(R.string.message_view_fetching_attachment_toast),
                                   Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

        public void progress(boolean progress)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_PROGRESS;
            msg.arg1 = progress ? 1 : 0;
            sendMessage(msg);
        }

        public void addAttachment(View attachmentView)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_ADD_ATTACHMENT;
            msg.obj = attachmentView;
            sendMessage(msg);
        }

        public void setAttachmentsEnabled(boolean enabled)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_SET_ATTACHMENTS_ENABLED;
            msg.arg1 = enabled ? 1 : 0;
            sendMessage(msg);
        }

        public void setHeaders(
            String subject,
            String from,
            String date,
            String time,
            String to,
            String cc,
            boolean hasAttachments,
            boolean flagged,
            boolean seen)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_SET_HEADERS;
            msg.arg1 = hasAttachments ? 1 : 0;
            msg.arg2 += (flagged ? FLAG_FLAGGED : 0);
            msg.arg2 += (seen ? FLAG_ANSWERED : 0);

            msg.obj = new String[] { subject, from, date, time, to, cc };
            sendMessage(msg);
        }

        public void networkError()
        {
            sendEmptyMessage(MSG_NETWORK_ERROR);
        }

        public void invalidIdError()
        {
            sendEmptyMessage(MSG_INVALID_ID_ERROR);
        }

        public void attachmentSaved(String filename)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_ATTACHMENT_SAVED;
            msg.obj = filename;
            sendMessage(msg);
        }

        public void attachmentNotSaved()
        {
            sendEmptyMessage(MSG_ATTACHMENT_NOT_SAVED);
        }

        public void fetchingAttachment()
        {
            sendEmptyMessage(MSG_FETCHING_ATTACHMENT);
        }

        public void showShowPictures(boolean show)
        {
            android.os.Message msg = new android.os.Message();
            msg.what = MSG_SHOW_SHOW_PICTURES;
            msg.arg1 = show ? 1 : 0;
            sendMessage(msg);
        }



    }

    class Attachment
    {
        public String name;
        public String contentType;
        public long size;
        public LocalAttachmentBodyPart part;
        public Button viewButton;
        public Button downloadButton;
        public ImageView iconView;
    }

    public static void actionView(Context context, Account account, String folder, String messageUid, ArrayList<String> folderUids)
    {
        actionView(context, account, folder, messageUid, folderUids, null);
    }

    public static void actionView(Context context, Account account, String folder, String messageUid, ArrayList<String> folderUids, Bundle extras)
    {
        Intent i = new Intent(context, MessageView.class);
        i.putExtra(EXTRA_ACCOUNT, account);
        i.putExtra(EXTRA_FOLDER, folder);
        i.putExtra(EXTRA_MESSAGE, messageUid);
        i.putExtra(EXTRA_MESSAGE_UIDS, folderUids);
        if (extras != null)
        {
            i.putExtras(extras);
        }
        context.startActivity(i);
    }

    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);


        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.message_view);


        mFromView = (TextView)findViewById(R.id.from);
        mToView = (TextView)findViewById(R.id.to);
        mCcView = (TextView)findViewById(R.id.cc);
        mSubjectView = (TextView)findViewById(R.id.subject);
        defaultSubjectColor = mSubjectView.getCurrentTextColor();


        mDateView = (TextView)findViewById(R.id.date);
        mTimeView = (TextView)findViewById(R.id.time);
        mTopView = (ScrollView)findViewById(R.id.top_view);
        mMessageContentView = (WebView)findViewById(R.id.message_content);

        mAttachments = (LinearLayout)findViewById(R.id.attachments);
        mAttachmentIcon = findViewById(R.id.attachment);
        mShowPicturesSection = findViewById(R.id.show_pictures_section);


        mFlagged = (CheckBox)findViewById(R.id.flagged);
        mFlagged.setOnClickListener(new OnClickListener()
        {
            public void onClick(View v)
            {
                onFlag();
            }
        });

        mMessageContentView.setVerticalScrollBarEnabled(true);
        mMessageContentView.setVerticalScrollbarOverlay(true);
        mMessageContentView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

        final WebSettings webSettings = mMessageContentView.getSettings();

        webSettings.setSupportZoom(true);
        webSettings.setLoadsImagesAutomatically(true);
        //webSettings.setBuiltInZoomControls(true);
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);

        mAttachments.setVisibility(View.GONE);
        mAttachmentIcon.setVisibility(View.GONE);

        setOnClickListener(R.id.from);
        setOnClickListener(R.id.reply);
        setOnClickListener(R.id.reply_all);
        setOnClickListener(R.id.delete);
        setOnClickListener(R.id.forward);
        setOnClickListener(R.id.next);
        setOnClickListener(R.id.previous);

        setOnClickListener(R.id.reply_scrolling);
//       setOnClickListener(R.id.reply_all_scrolling);
        setOnClickListener(R.id.delete_scrolling);
        setOnClickListener(R.id.forward_scrolling);
        setOnClickListener(R.id.next_scrolling);
        setOnClickListener(R.id.previous_scrolling);

        setOnClickListener(R.id.show_pictures);


        setTitle("");

        Intent intent = getIntent();
        Uri uri = intent.getData();

        if (icicle!=null)
        {
            mAccount = (Account) icicle.getSerializable(EXTRA_ACCOUNT);
            mFolder = icicle.getString(EXTRA_FOLDER);
            mMessageUid = icicle.getString(EXTRA_MESSAGE);
            mMessageUids = icicle.getStringArrayList(EXTRA_MESSAGE_UIDS);
        }
        else
        {
            if (uri==null)
            {
                mAccount = (Account) intent.getSerializableExtra(EXTRA_ACCOUNT);
                mFolder = intent.getStringExtra(EXTRA_FOLDER);
                mMessageUid = intent.getStringExtra(EXTRA_MESSAGE);
                mMessageUids = intent.getStringArrayListExtra(EXTRA_MESSAGE_UIDS);

                Log.v(Email.LOG_TAG, "mAccount number: " + mAccount.getAccountNumber());
                Log.v(Email.LOG_TAG, "mFolder: " + mFolder);
                Log.v(Email.LOG_TAG, "mMessageUid: " + mMessageUid);
            }
            else
            {
                Log.v(Email.LOG_TAG, "uri: " + uri.toString());
                List<String> segmentList = uri.getPathSegments();
                Log.v(Email.LOG_TAG, "segmentList size: " + segmentList.size());
                if (segmentList.size()==3)
                {
                    String accountId = segmentList.get(0);
                    Account[] accounts = Preferences.getPreferences(this).getAccounts();
                    Log.v(Email.LOG_TAG, "account.length: " + accounts.length);
                    boolean found = false;
                    for (Account account : accounts)
                    {
                        Log.v(Email.LOG_TAG, "account: name=" + account.getDescription() + " number=" + account.getAccountNumber());
                        if (String.valueOf(account.getAccountNumber()).equals(accountId))
                        {
                            mAccount = account;
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        //TODO: Use ressource to externalize message
                        Toast.makeText(this, "Invalid account id: " + accountId, Toast.LENGTH_LONG).show();
                        return;
                    }
                    mFolder = segmentList.get(1);
                    mMessageUid = segmentList.get(2);
                    mMessageUids = new ArrayList<String>();
                }
                else
                {
                    for (String segment : segmentList)
                    {
                        Log.v(Email.LOG_TAG, "segment: " + segment);
                    }
                    //TODO: Use ressource to externalize message
                    Toast.makeText(this, "Invalid intent uri: " + uri.toString(), Toast.LENGTH_LONG).show();
                    return;
                }
            }
        }

        next = findViewById(R.id.next);
        previous = findViewById(R.id.previous);

        setOnClickListener(R.id.next);
        setOnClickListener(R.id.previous);

        next_scrolling = findViewById(R.id.next_scrolling);
        previous_scrolling = findViewById(R.id.previous_scrolling);


        // Gesture detection
        gestureDetector = new GestureDetector(new MyGestureDetector());

        boolean goNext = intent.getBooleanExtra(EXTRA_NEXT, false);
        if (goNext)
        {
            next.requestFocus();
        }

        Account.HideButtons hideButtons = mAccount.getHideMessageViewButtons();

        //MessagingController.getInstance(getApplication()).addListener(mListener);
        if (Account.HideButtons.ALWAYS == hideButtons)
        {
            hideButtons();
        }
        else if (Account.HideButtons.NEVER == hideButtons)
        {
            showButtons();
        }
        else   // Account.HideButtons.KEYBOARD_AVAIL
        {
            final Configuration config = this.getResources().getConfiguration();
            if (config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO)
            {
                hideButtons();
            }
            else
            {
                showButtons();
            }
        }
        displayMessage(mMessageUid);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        outState.putSerializable(EXTRA_ACCOUNT, mAccount);
        outState.putString(EXTRA_FOLDER, mFolder);
        outState.putString(EXTRA_MESSAGE, mMessageUid);
        outState.putStringArrayList(EXTRA_MESSAGE_UIDS, mMessageUids);
    }

    private void displayMessage(String uid)
    {
        mMessageUid = uid;
        mMessageContentView.getSettings().setBlockNetworkImage(true);

        mAttachments.removeAllViews();
        findSurroundingMessagesUid();

        next.setEnabled(mNextMessageUid != null);
        previous.setEnabled(mPreviousMessageUid != null);

        if (next_scrolling != null)
            next_scrolling.setEnabled(mNextMessageUid != null);
        if (previous_scrolling != null)
            previous_scrolling.setEnabled(mPreviousMessageUid != null);

        MessagingController.getInstance(getApplication()).loadMessageForView(
            mAccount,
            mFolder,
            mMessageUid,
            mListener);

        mTopView.scrollTo(0, 0);
        mMessageContentView.scrollTo(0, 0);
    }


    private void showButtons()
    {
        View buttons = findViewById(R.id.scrolling_buttons);
        if (buttons != null)
        {
            buttons.setVisibility(View.GONE);
        }
    }

    private void hideButtons()
    {
        View buttons = findViewById(R.id.bottom_buttons);
        if (buttons != null)
        {
            buttons.setVisibility(View.GONE);
        }
    }

    private void setOnClickListener(int viewCode)
    {
        View thisView = findViewById(viewCode);
        if (thisView != null)
        {
            thisView.setOnClickListener(this);
        }
    }

    private void findSurroundingMessagesUid()
    {
        mNextMessageUid = mPreviousMessageUid = null;
        int i = mMessageUids.indexOf(mMessageUid);
        if (i < 0)
            return;
        if (i != 0)
            mNextMessageUid = mMessageUids.get(i - 1);
        if (i != (mMessageUids.size() - 1))
            mPreviousMessageUid = mMessageUids.get(i + 1);
    }

    public void onResume()
    {
        super.onResume();

        mDateFormat = DateFormatter.getDateFormat(this);
        mTimeFormat = android.text.format.DateFormat.getTimeFormat(this);   // 12/24 date format


    }

    private void onDelete()
    {
        if (mMessage != null)
        {
            Message messageToDelete = mMessage;
            String folderForDelete = mFolder;
            Account accountForDelete = mAccount;

            findSurroundingMessagesUid();

            // Remove this message's Uid locally
            mMessageUids.remove(messageToDelete.getUid());

            MessagingController.getInstance(getApplication()).deleteMessages(
                accountForDelete,
                folderForDelete,
                new Message[] { messageToDelete },
                null);

            if (mLastDirection == NEXT && mNextMessageUid != null)
            {
                onNext();
            }
            else if (mLastDirection == PREVIOUS && mPreviousMessageUid != null)
            {
                onPrevious();
            }
            else if (mNextMessageUid != null)
            {
                onNext();
            }
            else if (mPreviousMessageUid != null)
            {
                onPrevious();
            }



            else
            {
                finish();
            }
        }
    }

    private void onClickSender()
    {
        if (mMessage != null)
        {
            try
            {
                Address senderEmail = mMessage.getFrom()[0];
                Uri contactUri = Uri.fromParts("mailto", senderEmail.getAddress(), null);

                Intent contactIntent = new Intent(Contacts.Intents.SHOW_OR_CREATE_CONTACT);
                contactIntent.setData(contactUri);

                // Pass along full E-mail string for possible create dialog
                contactIntent.putExtra(Contacts.Intents.EXTRA_CREATE_DESCRIPTION,
                                       senderEmail.toString());

                // Only provide personal name hint if we have one
                String senderPersonal = senderEmail.getPersonal();
                if (senderPersonal != null)
                {
                    contactIntent.putExtra(Intents.Insert.NAME, senderPersonal);
                }

                startActivity(contactIntent);
            }
            catch (MessagingException me)
            {
                if (Config.LOGV)
                {
                    Log.v(Email.LOG_TAG, "loadMessageForViewHeadersAvailable", me);
                }
            }
        }
    }

    private void onReply()
    {
        if (mMessage != null)
        {
            MessageCompose.actionReply(this, mAccount, mMessage, false);
            finish();
        }
    }

    private void onReplyAll()
    {
        if (mMessage != null)
        {
            MessageCompose.actionReply(this, mAccount, mMessage, true);
            finish();
        }
    }

    private void onForward()
    {
        if (mMessage != null)
        {
            MessageCompose.actionForward(this, mAccount, mMessage);
            finish();
        }
    }

    private void onFlag()
    {
        if (mMessage != null)
        {
            MessagingController.getInstance(getApplication()).setFlag(mAccount,
                    mMessage.getFolder().getName(), new String[] { mMessage.getUid() }, Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
            try
            {
                mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED));
                setHeaders(mAccount, mMessage.getFolder().getName(), mMessage.getUid(), mMessage);
                setMenuFlag();
            }
            catch (MessagingException me)
            {
                Log.e(Email.LOG_TAG, "Could not set flag on local message", me);
            }
        }
    }

    private void onMove()
    {
        if (MessagingController.getInstance(getApplication()).isMoveCapable(mAccount) == false)
        {
            return;
        }
        if (MessagingController.getInstance(getApplication()).isMoveCapable(mMessage) == false)
        {
            Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
            toast.show();
            return;
        }
        Intent intent = new Intent(this, ChooseFolder.class);
        intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
        intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, mFolder);
        intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, mMessageUid);
        startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_MOVE);
    }

    private void onCopy()
    {
        if (MessagingController.getInstance(getApplication()).isCopyCapable(mAccount) == false)
        {
            return;
        }
        if (MessagingController.getInstance(getApplication()).isCopyCapable(mMessage) == false)
        {
            Toast toast = Toast.makeText(this, R.string.move_copy_cannot_copy_unsynced_message, Toast.LENGTH_LONG);
            toast.show();
            return;
        }
        Intent intent = new Intent(this, ChooseFolder.class);

        intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, mAccount);
        intent.putExtra(ChooseFolder.EXTRA_CUR_FOLDER, mFolder);
        intent.putExtra(ChooseFolder.EXTRA_MESSAGE_UID, mMessageUid);
        startActivityForResult(intent, ACTIVITY_CHOOSE_FOLDER_COPY);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (resultCode != RESULT_OK)
            return;

        switch (requestCode)
        {
            case ACTIVITY_CHOOSE_FOLDER_MOVE:
            case ACTIVITY_CHOOSE_FOLDER_COPY:
                if (data == null)
                    return;
                String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
                String srcFolderName = data.getStringExtra(ChooseFolder.EXTRA_CUR_FOLDER);
                String uid = data.getStringExtra(ChooseFolder.EXTRA_MESSAGE_UID);

                if (uid.equals(mMessageUid) && srcFolderName.equals(mFolder))
                {

                    switch (requestCode)
                    {
                        case ACTIVITY_CHOOSE_FOLDER_MOVE:
                            MessagingController.getInstance(getApplication()).moveMessage(mAccount,
                                    srcFolderName, mMessage, destFolderName, null);
                            break;
                        case ACTIVITY_CHOOSE_FOLDER_COPY:
                            MessagingController.getInstance(getApplication()).copyMessage(mAccount,
                                    srcFolderName, mMessage, destFolderName, null);
                            break;
                    }
                }
        }
    }


    private void onSendAlternate()
    {
        if (mMessage != null)
        {
            MessagingController.getInstance(getApplication()).sendAlternate(this, mAccount, mMessage);

        }
    }

    private void onNext()
    {
        if (mNextMessageUid == null)
        {
            Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
            return;
        }
        mLastDirection = NEXT;
        displayMessage(mNextMessageUid);
        mTopView.startAnimation(outToLeftAnimation());
        next.requestFocus();
    }

    private void onPrevious()
    {
        if (mPreviousMessageUid == null)
        {
            Toast.makeText(this, getString(R.string.end_of_folder), Toast.LENGTH_SHORT).show();
            return;
        }
        
        mLastDirection = PREVIOUS;
        displayMessage(mPreviousMessageUid);
        mTopView.startAnimation(inFromRightAnimation());
        previous.requestFocus();
    }

    private void onMarkAsUnread()
    {
        if (mMessage != null)
        {
            MessagingController.getInstance(getApplication()).setFlag(
                mAccount,
                mFolder,
                new String[] { mMessage.getUid() },
                Flag.SEEN,
                false);
        }
    }

    /**
     * Creates a unique file in the given directory by appending a hyphen
     * and a number to the given filename.
     * @param directory
{'fraction_non_alphanumeric': 0.08490650979921366, 'fraction_numerical': 0.00279120021609292, 'mean_word_length': 1.7027498377676833, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5555833', 'n_tokens_mistral': 18125, 'n_tokens_neox': 17129, 'n_words': 3160}