Merge "Store the "Show pictures" state on rotation" into jb-ub-mail
diff --git a/res/layout/widget.xml b/res/layout/widget.xml
index 268ed34..8e8fd0f 100644
--- a/res/layout/widget.xml
+++ b/res/layout/widget.xml
@@ -75,7 +75,8 @@
             android:src="@drawable/ic_menu_compose_normal_holo_light"
             android:background="?android:attr/selectableItemBackground"
             android:paddingLeft="4dip"
-            android:layout_gravity="center_vertical" />
+            android:layout_gravity="center_vertical"
+            android:contentDescription="@string/compose" />
     </LinearLayout>
     <TextView
         android:id="@+id/empty_conversation_list"
diff --git a/src/com/android/mail/browse/ConversationAccountController.java b/src/com/android/mail/browse/ConversationAccountController.java
new file mode 100644
index 0000000..6a86eb3
--- /dev/null
+++ b/src/com/android/mail/browse/ConversationAccountController.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.browse;
+
+import com.android.mail.providers.Account;
+
+public interface ConversationAccountController {
+    Account getAccount();
+}
\ No newline at end of file
diff --git a/src/com/android/mail/browse/ConversationContainer.java b/src/com/android/mail/browse/ConversationContainer.java
index 1319916..504ab9c 100644
--- a/src/com/android/mail/browse/ConversationContainer.java
+++ b/src/com/android/mail/browse/ConversationContainer.java
@@ -18,6 +18,7 @@
 package com.android.mail.browse;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
@@ -34,6 +35,7 @@
 
 import com.android.mail.R;
 import com.android.mail.browse.ScrollNotifier.ScrollListener;
+import com.android.mail.providers.UIProvider;
 import com.android.mail.ui.ConversationViewFragment;
 import com.android.mail.utils.DequeMap;
 import com.android.mail.utils.InputSmoother;
@@ -80,6 +82,7 @@
      */
     private static final float SNAP_HEADER_MAX_SCROLL_SPEED = 600f;
 
+    private ConversationAccountController mAccountController;
     private ConversationViewAdapter mOverlayAdapter;
     private OverlayPosition[] mOverlayPositions;
     private ConversationWebView mWebView;
@@ -178,6 +181,8 @@
      */
     private int mSnapIndex;
 
+    private boolean mSnapEnabled;
+
     /**
      * Child views of this container should implement this interface to be notified when they are
      * being detached.
@@ -270,6 +275,12 @@
         return mOverlayAdapter;
     }
 
+    public void setAccountController(ConversationAccountController controller) {
+        mAccountController = controller;
+
+        mSnapEnabled = isSnapEnabled();
+    }
+
     /**
      * Re-bind any existing views that correspond to the given adapter positions.
      *
@@ -305,6 +316,7 @@
         // also unbind the snap header view, so this "reset" causes the snap header to re-create
         // its view, just like all other headers
         mSnapHeader.unbind();
+        mSnapEnabled = isSnapEnabled();
         positionOverlays(0, mOffsetY);
     }
 
@@ -765,10 +777,21 @@
         return view;
     }
 
+    private boolean isSnapEnabled() {
+        if (mAccountController == null || mAccountController.getAccount() == null
+                || mAccountController.getAccount().settings == null) {
+            return true;
+        }
+        final int snap = mAccountController.getAccount().settings.snapHeaders;
+        return snap == UIProvider.SnapHeaderValue.ALWAYS ||
+                (snap == UIProvider.SnapHeaderValue.PORTRAIT_ONLY && getResources()
+                    .getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
+    }
+
     // render and/or re-position snap header
     private void positionSnapHeader(int snapIndex) {
         ConversationOverlayItem snapItem = null;
-        if (snapIndex != -1) {
+        if (mSnapEnabled && snapIndex != -1) {
             final ConversationOverlayItem item = mOverlayAdapter.getItem(snapIndex);
             if (item.canBecomeSnapHeader()) {
                 snapItem = item;
diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java
index 45fe0c8..c48a82d 100644
--- a/src/com/android/mail/browse/ConversationViewAdapter.java
+++ b/src/com/android/mail/browse/ConversationViewAdapter.java
@@ -33,7 +33,6 @@
 import com.android.mail.browse.MessageCursor.ConversationMessage;
 import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks;
 import com.android.mail.browse.SuperCollapsedBlock.OnClickListener;
-import com.android.mail.providers.Account;
 import com.android.mail.providers.Address;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.UIProvider;
@@ -78,10 +77,6 @@
     public static final int VIEW_TYPE_SUPER_COLLAPSED_BLOCK = 3;
     public static final int VIEW_TYPE_COUNT = 4;
 
-    public interface ConversationAccountController {
-        Account getAccount();
-    }
-
     public class ConversationHeaderItem extends ConversationOverlayItem {
         public final Conversation mConversation;
 
diff --git a/src/com/android/mail/browse/ConversationViewHeader.java b/src/com/android/mail/browse/ConversationViewHeader.java
index 2bc6dc1..6d37dd0 100644
--- a/src/com/android/mail/browse/ConversationViewHeader.java
+++ b/src/com/android/mail/browse/ConversationViewHeader.java
@@ -32,7 +32,6 @@
 import android.widget.TextView;
 
 import com.android.mail.R;
-import com.android.mail.browse.ConversationViewAdapter.ConversationAccountController;
 import com.android.mail.browse.ConversationViewAdapter.ConversationHeaderItem;
 import com.android.mail.browse.FolderSpan.FolderSpanDimensions;
 import com.android.mail.providers.Conversation;
diff --git a/src/com/android/mail/browse/MessageCursor.java b/src/com/android/mail/browse/MessageCursor.java
index 867a94a..e745bf9 100644
--- a/src/com/android/mail/browse/MessageCursor.java
+++ b/src/com/android/mail/browse/MessageCursor.java
@@ -211,7 +211,7 @@
             sb.append(String.format(
                     "[Message #%d hash=%s uri=%s id=%s serverId=%s from='%s' draftType=%d" +
                     " isSending=%s read=%s starred=%s attUris=%s]\n",
-                    pos, m.getStateHashCode(), m.uri, m.id, m.serverId, m.from, m.draftType,
+                    pos, m.getStateHashCode(), m.uri, m.id, m.serverId, m.getFrom(), m.draftType,
                     m.isSending, m.read, m.starred, attUris));
         }
         return sb.toString();
diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java
index b94c75f..79fb2a9 100644
--- a/src/com/android/mail/browse/MessageHeaderView.java
+++ b/src/com/android/mail/browse/MessageHeaderView.java
@@ -44,7 +44,6 @@
 import com.android.mail.ContactInfoSource;
 import com.android.mail.FormattedDateBuilder;
 import com.android.mail.R;
-import com.android.mail.browse.ConversationViewAdapter.ConversationAccountController;
 import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
 import com.android.mail.browse.MessageCursor.ConversationMessage;
 import com.android.mail.compose.ComposeActivity;
@@ -406,7 +405,7 @@
         // 2. the account has no custom froms, fromAddress will be empty, and we
         // can safely fall back and show the account name as sender since it's
         // the only possible fromAddress.
-        String from = mMessage.from;
+        String from = mMessage.getFrom();
         if (TextUtils.isEmpty(from)) {
             from = getAccount().name;
         }
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index 5427dea..e25b9f5 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -58,7 +58,6 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
@@ -367,8 +366,8 @@
             showQuotedText = message.appendRefMessageContent;
         } else if (action == EDIT_DRAFT) {
             initFromDraftMessage(message);
-            boolean showBcc = !TextUtils.isEmpty(message.bcc);
-            boolean showCc = showBcc || !TextUtils.isEmpty(message.cc);
+            boolean showBcc = !TextUtils.isEmpty(message.getBcc());
+            boolean showCc = showBcc || !TextUtils.isEmpty(message.getCc());
             mCcBccView.show(false, showCc, showBcc);
             // Update the action to the draft type of the previous draft
             switch (message.draftType) {
@@ -695,10 +694,10 @@
         message.conversationUri = null;
         message.subject = mSubject.getText().toString();
         message.snippet = null;
-        message.to = formatSenders(mTo.getText().toString());
-        message.cc = formatSenders(mCc.getText().toString());
-        message.bcc = formatSenders(mBcc.getText().toString());
-        message.replyTo = null;
+        message.setTo(formatSenders(mTo.getText().toString()));
+        message.setCc(formatSenders(mCc.getText().toString()));
+        message.setBcc(formatSenders(mBcc.getText().toString()));
+        message.setReplyTo(null);
         message.dateReceivedMs = 0;
         String htmlBody = Html.toHtml(new SpannableString(mBodyView.getText().toString()));
         StringBuilder fullBody = new StringBuilder(htmlBody);
@@ -725,9 +724,12 @@
 
     private void updateMessage(Message message, ReplyFromAccount selectedReplyFromAccount,
             int mode) {
-        message.from = selectedReplyFromAccount != null ? selectedReplyFromAccount.address
-                : mAccount != null ? mAccount.name : null;
+        message.setFrom(selectedReplyFromAccount != null ? selectedReplyFromAccount.address
+                : mAccount != null ? mAccount.name : null);
         message.draftType = getDraftType(mode);
+        message.setTo(formatSenders(mTo.getText().toString()));
+        message.setCc(formatSenders(mCc.getText().toString()));
+        message.setBcc(formatSenders(mBcc.getText().toString()));
     }
 
     private String formatSenders(String string) {
@@ -877,7 +879,7 @@
     }
 
     private ReplyFromAccount getReplyFromAccountFromDraft(Account account, Message msg) {
-        String sender = msg.from;
+        String sender = msg.getFrom();
         ReplyFromAccount replyFromAccount = null;
         List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
         if (TextUtils.equals(account.name, sender)) {
@@ -1428,7 +1430,7 @@
         // the reply.
         final String accountEmail = Address.getEmailAddress(account).getAddress();
         String[] sentToAddresses = refMessage.getToAddresses();
-        String replytoAddress = refMessage.replyTo;
+        String replytoAddress = refMessage.getReplyTo();
         final Collection<String> toAddresses;
 
         // If this is a reply, the Cc list is empty. If this is a reply-all, the
@@ -1436,13 +1438,13 @@
         // message, excluding the current user's email address and any addresses
         // already on the To list.
         if (action == ComposeActivity.REPLY) {
-            toAddresses = initToRecipients(account, accountEmail, refMessage.from, replytoAddress,
-                    sentToAddresses);
+            toAddresses = initToRecipients(account, accountEmail, refMessage.getFrom(),
+                    replytoAddress, sentToAddresses);
             addToAddresses(toAddresses);
         } else if (action == ComposeActivity.REPLY_ALL) {
             final Set<String> ccAddresses = Sets.newHashSet();
-            toAddresses = initToRecipients(account, accountEmail, refMessage.from, replytoAddress,
-                    sentToAddresses);
+            toAddresses = initToRecipients(account, accountEmail, refMessage.getFrom(),
+                    replytoAddress, sentToAddresses);
             addToAddresses(toAddresses);
             addRecipients(accountEmail, ccAddresses, sentToAddresses);
             addRecipients(accountEmail, ccAddresses, refMessage.getCcAddresses());
@@ -2348,7 +2350,7 @@
         MessageModification.putCcAddresses(values, message.getCcAddresses());
         MessageModification.putBccAddresses(values, message.getBccAddresses());
 
-        MessageModification.putCustomFromAddress(values, message.from);
+        MessageModification.putCustomFromAddress(values, message.getFrom());
 
         MessageModification.putSubject(values, message.subject);
         String htmlBody = Html.toHtml(new SpannableString(body.toString()));
@@ -2611,10 +2613,11 @@
             if (mDraft != null) {
                 // Following desktop behavior, if the user has added a BCC
                 // field to a draft, we show it regardless of compose mode.
-                showBcc = !TextUtils.isEmpty(mDraft.bcc);
+                showBcc = !TextUtils.isEmpty(mDraft.getBcc());
                 // Use the draft to determine what to populate.
                 // If the Bcc field is showing, show the Cc field whether it is populated or not.
-                showCc = showBcc || (!TextUtils.isEmpty(mDraft.cc) && mComposeMode == REPLY_ALL);
+                showCc = showBcc
+                        || (!TextUtils.isEmpty(mDraft.getCc()) && mComposeMode == REPLY_ALL);
             } else if (mRefMessage != null) {
                 showCc = !TextUtils.isEmpty(mCc.getText());
             }
@@ -2984,8 +2987,8 @@
                 if (data != null && data.moveToFirst()) {
                     mRefMessage = new Message(data);
                     // We set these based on EXTRA_TO.
-                    mRefMessage.to = null;
-                    mRefMessage.from = null;
+                    mRefMessage.setTo(null);
+                    mRefMessage.setFrom(null);
                     Intent intent = getIntent();
                     int action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
                     initFromRefMessage(action, mAccount.name);
diff --git a/src/com/android/mail/compose/QuotedTextView.java b/src/com/android/mail/compose/QuotedTextView.java
index 433cdb6..c8ddc3e 100644
--- a/src/com/android/mail/compose/QuotedTextView.java
+++ b/src/com/android/mail/compose/QuotedTextView.java
@@ -262,7 +262,7 @@
                             resources.getString(R.string.reply_attribution),
                             dateFormat.format(date),
                             Utils.cleanUpString(
-                                    refMessage.from, true)));
+                                    refMessage.getFrom(), true)));
             quotedText.append(HEADER_SEPARATOR);
             quotedText.append(BLOCKQUOTE_BEGIN);
             quotedText.append(htmlText);
@@ -272,12 +272,12 @@
             quotedText.append(sQuoteBegin);
             quotedText
                     .append(String.format(resources.getString(R.string.forward_attribution), Utils
-                            .cleanUpString(refMessage.from,
+                            .cleanUpString(refMessage.getFrom(),
                                     true /* remove empty quotes */), dateFormat.format(date), Utils
                             .cleanUpString(refMessage.subject,
                                     false /* don't remove empty quotes */), Utils.cleanUpString(
-                            refMessage.to, true)));
-            String ccAddresses = refMessage.cc;
+                            refMessage.getTo(), true)));
+            String ccAddresses = refMessage.getCc();
             quotedText.append(String.format(resources.getString(R.string.cc_attribution),
                     Utils.cleanUpString(ccAddresses, true /* remove empty quotes */)));
             quotedText.append(HEADER_SEPARATOR);
diff --git a/src/com/android/mail/providers/Message.java b/src/com/android/mail/providers/Message.java
index 5189d6b..37e60f2 100644
--- a/src/com/android/mail/providers/Message.java
+++ b/src/com/android/mail/providers/Message.java
@@ -74,23 +74,23 @@
     /**
      * @see UIProvider.MessageColumns#FROM
      */
-    public String from;
+    private String mFrom;
     /**
      * @see UIProvider.MessageColumns#TO
      */
-    public String to;
+    private String mTo;
     /**
      * @see UIProvider.MessageColumns#CC
      */
-    public String cc;
+    private String mCc;
     /**
      * @see UIProvider.MessageColumns#BCC
      */
-    public String bcc;
+    private String mBcc;
     /**
      * @see UIProvider.MessageColumns#REPLY_TO
      */
-    public String replyTo;
+    private String mReplyTo;
     /**
      * @see UIProvider.MessageColumns#DATE_RECEIVED_MS
      */
@@ -224,11 +224,11 @@
         dest.writeParcelable(conversationUri, 0);
         dest.writeString(subject);
         dest.writeString(snippet);
-        dest.writeString(from);
-        dest.writeString(to);
-        dest.writeString(cc);
-        dest.writeString(bcc);
-        dest.writeString(replyTo);
+        dest.writeString(mFrom);
+        dest.writeString(mTo);
+        dest.writeString(mCc);
+        dest.writeString(mBcc);
+        dest.writeString(mReplyTo);
         dest.writeLong(dateReceivedMs);
         dest.writeString(bodyHtml);
         dest.writeString(bodyText);
@@ -260,11 +260,11 @@
         conversationUri = in.readParcelable(null);
         subject = in.readString();
         snippet = in.readString();
-        from = in.readString();
-        to = in.readString();
-        cc = in.readString();
-        bcc = in.readString();
-        replyTo = in.readString();
+        mFrom = in.readString();
+        mTo = in.readString();
+        mCc = in.readString();
+        mBcc = in.readString();
+        mReplyTo = in.readString();
         dateReceivedMs = in.readLong();
         bodyHtml = in.readString();
         bodyText = in.readString();
@@ -322,11 +322,11 @@
             conversationUri = !TextUtils.isEmpty(convUriStr) ? Uri.parse(convUriStr) : null;
             subject = cursor.getString(UIProvider.MESSAGE_SUBJECT_COLUMN);
             snippet = cursor.getString(UIProvider.MESSAGE_SNIPPET_COLUMN);
-            from = cursor.getString(UIProvider.MESSAGE_FROM_COLUMN);
-            to = cursor.getString(UIProvider.MESSAGE_TO_COLUMN);
-            cc = cursor.getString(UIProvider.MESSAGE_CC_COLUMN);
-            bcc = cursor.getString(UIProvider.MESSAGE_BCC_COLUMN);
-            replyTo = cursor.getString(UIProvider.MESSAGE_REPLY_TO_COLUMN);
+            mFrom = cursor.getString(UIProvider.MESSAGE_FROM_COLUMN);
+            mTo = cursor.getString(UIProvider.MESSAGE_TO_COLUMN);
+            mCc = cursor.getString(UIProvider.MESSAGE_CC_COLUMN);
+            mBcc = cursor.getString(UIProvider.MESSAGE_BCC_COLUMN);
+            mReplyTo = cursor.getString(UIProvider.MESSAGE_REPLY_TO_COLUMN);
             dateReceivedMs = cursor.getLong(UIProvider.MESSAGE_DATE_RECEIVED_MS_COLUMN);
             bodyHtml = cursor.getString(UIProvider.MESSAGE_BODY_HTML_COLUMN);
             bodyText = cursor.getString(UIProvider.MESSAGE_BODY_TEXT_COLUMN);
@@ -379,37 +379,82 @@
                 UIProvider.MessageFlags.CALENDAR_INVITE;
     }
 
+    public String getFrom() {
+        return mFrom;
+    }
+
+    public synchronized void setFrom(final String from) {
+        mFrom = from;
+        mFromAddresses = null;
+    }
+
+    public String getTo() {
+        return mTo;
+    }
+
+    public synchronized void setTo(final String to) {
+        mTo = to;
+        mToAddresses = null;
+    }
+
+    public String getCc() {
+        return mCc;
+    }
+
+    public synchronized void setCc(final String cc) {
+        mCc = cc;
+        mCcAddresses = null;
+    }
+
+    public String getBcc() {
+        return mBcc;
+    }
+
+    public synchronized void setBcc(final String bcc) {
+        mBcc = bcc;
+        mBccAddresses = null;
+    }
+
+    public String getReplyTo() {
+        return mReplyTo;
+    }
+
+    public synchronized void setReplyTo(final String replyTo) {
+        mReplyTo = replyTo;
+        mReplyToAddresses = null;
+    }
+
     public synchronized String[] getFromAddresses() {
         if (mFromAddresses == null) {
-            mFromAddresses = tokenizeAddresses(from);
+            mFromAddresses = tokenizeAddresses(mFrom);
         }
         return mFromAddresses;
     }
 
     public synchronized String[] getToAddresses() {
         if (mToAddresses == null) {
-            mToAddresses = tokenizeAddresses(to);
+            mToAddresses = tokenizeAddresses(mTo);
         }
         return mToAddresses;
     }
 
     public synchronized String[] getCcAddresses() {
         if (mCcAddresses == null) {
-            mCcAddresses = tokenizeAddresses(cc);
+            mCcAddresses = tokenizeAddresses(mCc);
         }
         return mCcAddresses;
     }
 
     public synchronized String[] getBccAddresses() {
         if (mBccAddresses == null) {
-            mBccAddresses = tokenizeAddresses(bcc);
+            mBccAddresses = tokenizeAddresses(mBcc);
         }
         return mBccAddresses;
     }
 
     public synchronized String[] getReplyToAddresses() {
         if (mReplyToAddresses == null) {
-            mReplyToAddresses = tokenizeAddresses(replyTo);
+            mReplyToAddresses = tokenizeAddresses(mReplyTo);
         }
         return mReplyToAddresses;
     }
diff --git a/src/com/android/mail/providers/Settings.java b/src/com/android/mail/providers/Settings.java
index e4ba546..74340b8 100644
--- a/src/com/android/mail/providers/Settings.java
+++ b/src/com/android/mail/providers/Settings.java
@@ -395,6 +395,7 @@
                 && mAutoAdvance == that.mAutoAdvance
                 && mTransientAutoAdvance == that.mTransientAutoAdvance
                 && messageTextSize == that.messageTextSize
+                && snapHeaders == that.snapHeaders
                 && replyBehavior == that.replyBehavior
                 && hideCheckboxes == that.hideCheckboxes
                 && confirmDelete == that.confirmDelete
@@ -420,13 +421,12 @@
 
     /**
      * Returns the hash code for this object.
-     * @return
      */
     private final int calculateHashCode() {
         return super.hashCode()
                 ^ Objects.hashCode(signature, mAutoAdvance, mTransientAutoAdvance, messageTextSize,
-                        replyBehavior, hideCheckboxes, confirmDelete, confirmArchive, confirmSend,
-                        defaultInbox, forceReplyFromDefault, maxAttachmentSize, swipe,
+                        snapHeaders, replyBehavior, hideCheckboxes, confirmDelete, confirmArchive,
+                        confirmSend, defaultInbox, forceReplyFromDefault, maxAttachmentSize, swipe,
                         priorityArrowsEnabled, setupIntentUri, conversationViewMode);
     }
 }
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 6efb8d1..b3ee6f5 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -297,6 +297,8 @@
     private boolean mRecentsDataUpdated;
     /** A wait fragment we added, if any. */
     private WaitFragment mWaitFragment;
+    /** True if we have results from a search query */
+    private boolean mHaveSearchResults = false;
     public static final String SYNC_ERROR_DIALOG_FRAGMENT_TAG = "SyncErrorDialogFragment";
 
     public AbstractActivityController(MailActivity activity, ViewMode viewMode) {
@@ -1576,19 +1578,20 @@
             }
         } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
             if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
+                mHaveSearchResults = false;
                 // Save this search query for future suggestions.
                 final String query = intent.getStringExtra(SearchManager.QUERY);
                 final String authority = mContext.getString(R.string.suggestions_authority);
                 final SearchRecentSuggestions suggestions = new SearchRecentSuggestions(
                         mContext, authority, SuggestionsProvider.MODE);
                 suggestions.saveRecentQuery(query, null);
-                if (Utils.showTwoPaneSearchResults(mActivity.getActivityContext())) {
+                setAccount((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
+                fetchSearchFolder(intent);
+                if (shouldEnterSearchConvMode()) {
                     mViewMode.enterSearchResultsConversationMode();
                 } else {
                     mViewMode.enterSearchResultsListMode();
                 }
-                setAccount((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
-                fetchSearchFolder(intent);
             } else {
                 LogUtils.e(LOG_TAG, "Missing account extra from search intent.  Finishing");
                 mActivity.finish();
@@ -1600,6 +1603,13 @@
     }
 
     /**
+     * Returns true if we should enter conversation mode with search.
+     */
+    protected final boolean shouldEnterSearchConvMode() {
+        return mHaveSearchResults && Utils.showTwoPaneSearchResults(mActivity.getActivityContext());
+    }
+
+    /**
      * Copy any selected conversations stored in the saved bundle into our selection set,
      * triggering {@link ConversationSetObserver} callbacks as our selection set changes.
      *
@@ -2096,6 +2106,7 @@
                                     .getStringExtra(UIProvider.SearchQueryParameters.QUERY));
                     showConversationList(mConvListContext);
                     mActivity.invalidateOptionsMenu();
+                    mHaveSearchResults = search.totalCount > 0;
                     mActivity.getLoaderManager().destroyLoader(LOADER_SEARCH);
                 } else {
                     LogUtils.e(LOG_TAG, "Null or empty cursor returned by LOADER_SEARCH loader");
@@ -2360,6 +2371,7 @@
             mConversationListCursor.sync();
         }
         mTracker.onCursorUpdated();
+        perhapsShowFirstSearchResult();
     }
 
     @Override
@@ -2620,15 +2632,7 @@
                 // check and inform the cursor of the change in visibility here.
                 informCursorVisiblity(true);
             }
-            // Shown for search results in two-pane mode only.
-            if (shouldShowFirstConversation()) {
-                if (mConversationListCursor.getCount() > 0) {
-                    mConversationListCursor.moveToPosition(0);
-                    final Conversation conv = new Conversation(mConversationListCursor);
-                    conv.position = 0;
-                    onConversationSelected(conv, true /* checkSafeToModifyFragments */);
-                }
-            }
+            perhapsShowFirstSearchResult();
         }
 
         @Override
@@ -2649,6 +2653,22 @@
     }
 
     /**
+     * Updates controller state based on search results and shows first conversation if required.
+     */
+    private final void perhapsShowFirstSearchResult() {
+        // Shown for search results in two-pane mode only.
+        mHaveSearchResults = Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())
+                && mConversationListCursor.getCount() > 0;
+        if (!shouldShowFirstConversation()) {
+            return;
+        }
+        mConversationListCursor.moveToPosition(0);
+        final Conversation conv = new Conversation(mConversationListCursor);
+        conv.position = 0;
+        onConversationSelected(conv, true /* checkSafeToModifyFragments */);
+    }
+
+    /**
      * Destroy the pending {@link DestructiveAction} till now and assign the given action as the
      * next destructive action..
      * @param nextAction the next destructive action to be performed. This can be null.
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index 4bf6491..2ae6b80 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -51,7 +51,7 @@
 import com.android.mail.FormattedDateBuilder;
 import com.android.mail.R;
 import com.android.mail.SenderInfoLoader;
-import com.android.mail.browse.ConversationViewAdapter.ConversationAccountController;
+import com.android.mail.browse.ConversationAccountController;
 import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks;
 import com.android.mail.browse.MessageCursor;
 import com.android.mail.browse.MessageCursor.ConversationController;
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index fa48b1c..2f90339 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -49,7 +49,6 @@
 import com.android.mail.browse.ConversationContainer.OverlayPosition;
 import com.android.mail.browse.ConversationOverlayItem;
 import com.android.mail.browse.ConversationViewAdapter;
-import com.android.mail.browse.ConversationViewAdapter.ConversationAccountController;
 import com.android.mail.browse.ConversationViewAdapter.MessageFooterItem;
 import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
 import com.android.mail.browse.ConversationViewAdapter.SuperCollapsedBlockItem;
@@ -84,10 +83,7 @@
  * The conversation view UI component.
  */
 public final class ConversationViewFragment extends AbstractConversationViewFragment implements
-        MessageHeaderViewCallbacks,
         SuperCollapsedBlock.OnClickListener,
-        ConversationController,
-        ConversationAccountController,
         OnLayoutChangeListener {
 
     private static final String LOG_TAG = LogTag.getLogTag();
@@ -315,6 +311,7 @@
         View rootView = inflater.inflate(R.layout.conversation_view, container, false);
         mConversationContainer = (ConversationContainer) rootView
                 .findViewById(R.id.conversation_container);
+        mConversationContainer.setAccountController(this);
 
         mNewMessageBar = mConversationContainer.findViewById(R.id.new_message_notification_bar);
         mNewMessageBar.setOnClickListener(new View.OnClickListener() {
@@ -1168,7 +1165,7 @@
             if (!mViewState.contains(m)) {
                 LogUtils.i(LOG_TAG, "conversation diff: found new msg: %s", m.uri);
 
-                final Address from = getAddress(m.from);
+                final Address from = getAddress(m.getFrom());
                 // distinguish ours from theirs
                 // new messages from the account owner should not trigger a
                 // notification
@@ -1179,7 +1176,7 @@
                 }
 
                 info.count++;
-                info.senderAddress = m.from;
+                info.senderAddress = m.getFrom();
             }
         }
         return info;
@@ -1200,7 +1197,7 @@
             final ConversationMessage newMsg = newCursor.getMessage();
             final ConversationMessage oldMsg = oldCursor.getMessage();
 
-            if (!TextUtils.equals(newMsg.from, oldMsg.from) ||
+            if (!TextUtils.equals(newMsg.getFrom(), oldMsg.getFrom()) ||
                     newMsg.isSending != oldMsg.isSending) {
                 mAdapter.updateItemsForMessage(newMsg, changedOverlayPositions);
                 LogUtils.i(LOG_TAG, "msg #%d (%d): detected from/sending change. isSending=%s",
diff --git a/src/com/android/mail/ui/SearchMailActionBarView.java b/src/com/android/mail/ui/SearchMailActionBarView.java
index c218403..6fff316 100644
--- a/src/com/android/mail/ui/SearchMailActionBarView.java
+++ b/src/com/android/mail/ui/SearchMailActionBarView.java
@@ -95,7 +95,7 @@
         // Work around b/6664203 by manually forcing this view to be VISIBLE
         // upon ActionView collapse. DISPLAY_SHOW_CUSTOM will still control its final
         // visibility.
-        int mode = getMode();
+        final int mode = getMode();
         if (mode == ViewMode.SEARCH_RESULTS_LIST
                 || (Utils.showTwoPaneSearchResults(getContext())
                         && mode == ViewMode.SEARCH_RESULTS_CONVERSATION)) {
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 4def9dc..61a699d 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -61,7 +61,7 @@
     private void initializeConversationListFragment(boolean show) {
         if (show) {
             if (Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())) {
-                if (Utils.showTwoPaneSearchResults(mActivity.getActivityContext())) {
+                if (shouldEnterSearchConvMode()) {
                     mViewMode.enterSearchResultsConversationMode();
                 } else {
                     mViewMode.enterSearchResultsListMode();
@@ -419,7 +419,7 @@
 
     @Override
     public void exitSearchMode() {
-        int mode = mViewMode.getMode();
+        final int mode = mViewMode.getMode();
         if (mode == ViewMode.SEARCH_RESULTS_LIST
                 || (mode == ViewMode.SEARCH_RESULTS_CONVERSATION
                         && Utils.showTwoPaneSearchResults(mActivity.getApplicationContext()))) {
@@ -430,7 +430,7 @@
     @Override
     public boolean shouldShowFirstConversation() {
         return Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())
-                && Utils.showTwoPaneSearchResults(mActivity.getApplicationContext());
+                && shouldEnterSearchConvMode();
     }
 
     private int getUndoBarWidth(int mode, ToastBarOperation op) {
diff --git a/tests/src/com/android/mail/compose/ComposeActivityTest.java b/tests/src/com/android/mail/compose/ComposeActivityTest.java
index 52dd982..232dfd5 100644
--- a/tests/src/com/android/mail/compose/ComposeActivityTest.java
+++ b/tests/src/com/android/mail/compose/ComposeActivityTest.java
@@ -96,7 +96,7 @@
         final Message refMessage = getRefMessage();
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
-        final String refMessageFromAccount = refMessage.from;
+        final String refMessageFromAccount = refMessage.getFrom();
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -116,10 +116,10 @@
     public void testReplyWithReplyTo() {
         setAccount("account1@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.replyTo = "replytofromaccount1@mock.com";
+        refMessage.setReplyTo("replytofromaccount1@mock.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
-        final String refReplyToAccount = refMessage.replyTo;
+        final String refReplyToAccount = refMessage.getReplyTo();
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -145,11 +145,11 @@
         setAccount("account2@mockuiprovider.com");
         final Message refMessage = getRefMessage();
         final String customFrom = "CUSTOMaccounta@mockuiprovider.com";
-        refMessage.from = "account2@mockuiprovider.com";
-        refMessage.to = "someotheraccount@mockuiprovider.com, "
+        refMessage.setFrom("account2@mockuiprovider.com");
+        refMessage.setTo("someotheraccount@mockuiprovider.com, "
                 + "someotheraccount2@mockuiprovider.com, someotheraccount3@mockuiprovider.com, "
-                + customFrom;
-        refMessage.replyTo = customFrom;
+                + customFrom);
+        refMessage.setReplyTo(customFrom);
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.mFromSpinner = new FromAddressSpinner(mActivity);
@@ -185,8 +185,8 @@
         final Message refMessage = getRefMessage();
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
-        final String[] refMessageTo = TextUtils.split(refMessage.to, ",");
-        final String refMessageFromAccount = refMessage.from;
+        final String[] refMessageTo = TextUtils.split(refMessage.getTo(), ",");
+        final String refMessageFromAccount = refMessage.getFrom();
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -206,11 +206,11 @@
     public void testReplyAllWithReplyTo() {
         setAccount("account1@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.replyTo = "replytofromaccount1@mock.com";
+        refMessage.setReplyTo("replytofromaccount1@mock.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
-        final String[] refMessageTo = TextUtils.split(refMessage.to, ",");
-        final String refReplyToAccount = refMessage.replyTo;
+        final String[] refMessageTo = TextUtils.split(refMessage.getTo(), ",");
+        final String refReplyToAccount = refMessage.getReplyTo();
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -254,9 +254,9 @@
         final Message refMessage = getRefMessageWithCc(0, false);
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
-        final String[] refMessageTo = TextUtils.split(refMessage.to, ",");
-        final String[] refMessageCc = TextUtils.split(refMessage.cc, ",");
-        final String refMessageFromAccount = refMessage.from;
+        final String[] refMessageTo = TextUtils.split(refMessage.getTo(), ",");
+        final String[] refMessageCc = TextUtils.split(refMessage.getCc(), ",");
+        final String refMessageFromAccount = refMessage.getFrom();
 
         mActivity.runOnUiThread(new Runnable() {
             public void run() {
@@ -326,8 +326,8 @@
     public void testRecipientsRefMessageReplyToSelf() {
         setAccount("account0@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.from = "account0@mockuiprovider.com";
-        refMessage.to = "someotheraccount@mockuiprovider.com";
+        refMessage.setFrom("account0@mockuiprovider.com");
+        refMessage.setTo("someotheraccount@mockuiprovider.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
 
@@ -338,7 +338,7 @@
                 String[] cc = activity.getCcAddresses();
                 String[] bcc = activity.getBccAddresses();
                 assertEquals(to.length, 1);
-                assertTrue(to[0].contains(refMessage.to));
+                assertTrue(to[0].contains(refMessage.getTo()));
                 assertEquals(cc.length, 0);
                 assertEquals(bcc.length, 0);
             }
@@ -352,13 +352,13 @@
     public void testRecipientsRefMessageReplyToCustomFrom() {
         setAccount("account1@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.from = "CUSTOMaccount1@mockuiprovider.com";
-        refMessage.to = "someotheraccount@mockuiprovider.com";
+        refMessage.setFrom("CUSTOMaccount1@mockuiprovider.com");
+        refMessage.setTo("someotheraccount@mockuiprovider.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.mFromSpinner = new FromAddressSpinner(mActivity);
-        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, refMessage.from,
-                refMessage.from, refMessage.from, true, true);
+        ReplyFromAccount a = new ReplyFromAccount(mAccount, mAccount.uri, refMessage.getFrom(),
+                refMessage.getFrom(), refMessage.getFrom(), true, true);
         JSONArray array = new JSONArray();
         array.put(a.serialize());
         mAccount.accountFromAddresses = array.toString();
@@ -375,7 +375,7 @@
                 String[] cc = activity.getCcAddresses();
                 String[] bcc = activity.getBccAddresses();
                 assertEquals(to.length, 1);
-                assertTrue(to[0].contains(refMessage.to));
+                assertTrue(to[0].contains(refMessage.getTo()));
                 assertEquals(cc.length, 0);
                 assertEquals(bcc.length, 0);
             }
@@ -390,10 +390,10 @@
         setAccount("account1@mockuiprovider.com");
         final Message refMessage = getRefMessage();
         final String customFrom = "CUSTOMaccount1@mockuiprovider.com";
-        refMessage.from = "senderaccount@mockuiprovider.com";
-        refMessage.to = "someotheraccount@mockuiprovider.com, "
+        refMessage.setFrom("senderaccount@mockuiprovider.com");
+        refMessage.setTo("someotheraccount@mockuiprovider.com, "
                 + "someotheraccount2@mockuiprovider.com, someotheraccount3@mockuiprovider.com, "
-                + customFrom;
+                + customFrom);
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.mFromSpinner = new FromAddressSpinner(mActivity);
@@ -432,10 +432,10 @@
         setAccount("account1@mockuiprovider.com");
         final Message refMessage = getRefMessage();
         final String customFrom = "CUSTOMaccount1@mockuiprovider.com";
-        refMessage.from = "account1@mockuiprovider.com";
-        refMessage.to = "someotheraccount@mockuiprovider.com, "
+        refMessage.setFrom("account1@mockuiprovider.com");
+        refMessage.setTo("someotheraccount@mockuiprovider.com, "
                 + "someotheraccount2@mockuiprovider.com, someotheraccount3@mockuiprovider.com, "
-                + customFrom;
+                + customFrom);
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.mFromSpinner = new FromAddressSpinner(mActivity);
@@ -490,9 +490,9 @@
     public void testChangeModes0() {
         setAccount("account0@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.from = "fromaccount@mockuiprovider.com";
-        refMessage.to = "account0@mockuiprovider.com";
-        refMessage.cc = "ccaccount@mockuiprovider.com";
+        refMessage.setFrom("fromaccount@mockuiprovider.com");
+        refMessage.setTo("account0@mockuiprovider.com");
+        refMessage.setCc("ccaccount@mockuiprovider.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.runOnUiThread(new Runnable() {
@@ -503,14 +503,14 @@
                 String[] cc = activity.getCcAddresses();
                 String[] bcc = activity.getBccAddresses();
                 assertEquals(to.length, 1);
-                assertTrue(to[0].contains(refMessage.from));
+                assertTrue(to[0].contains(refMessage.getFrom()));
                 assertEquals(cc.length, 0);
                 assertEquals(bcc.length, 0);
                 activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
                 assertEquals(activity.getToAddresses().length, 1);
-                assertTrue(activity.getToAddresses()[0].contains(refMessage.from));
+                assertTrue(activity.getToAddresses()[0].contains(refMessage.getFrom()));
                 assertEquals(activity.getCcAddresses().length, 1);
-                assertTrue(activity.getCcAddresses()[0].contains(refMessage.cc));
+                assertTrue(activity.getCcAddresses()[0].contains(refMessage.getCc()));
                 assertEquals(activity.getBccAddresses().length, 0);
                 activity.onNavigationItemSelected(2, ComposeActivity.FORWARD);
                 assertEquals(activity.getToAddresses().length, 0);
@@ -524,9 +524,9 @@
     public void testChangeModes1() {
         setAccount("account0@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.from = "fromaccount@mockuiprovider.com";
-        refMessage.to = "account0@mockuiprovider.com, toaccount0@mockuiprovider.com";
-        refMessage.cc = "ccaccount@mockuiprovider.com";
+        refMessage.setFrom("fromaccount@mockuiprovider.com");
+        refMessage.setTo("account0@mockuiprovider.com, toaccount0@mockuiprovider.com");
+        refMessage.setCc("ccaccount@mockuiprovider.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.runOnUiThread(new Runnable() {
@@ -537,15 +537,15 @@
                 String[] cc = activity.getCcAddresses();
                 String[] bcc = activity.getBccAddresses();
                 assertEquals(to.length, 1);
-                assertTrue(to[0].contains(refMessage.from));
+                assertTrue(to[0].contains(refMessage.getFrom()));
                 assertEquals(cc.length, 0);
                 assertEquals(bcc.length, 0);
                 activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
                 assertEquals(activity.getToAddresses().length, 1);
-                assertTrue(activity.getToAddresses()[0].contains(refMessage.from));
+                assertTrue(activity.getToAddresses()[0].contains(refMessage.getFrom()));
                 assertEquals(activity.getCcAddresses().length, 2);
-                assertTrue(activity.getCcAddresses()[0].contains(refMessage.cc)
-                        || activity.getCcAddresses()[1].contains(refMessage.cc));
+                assertTrue(activity.getCcAddresses()[0].contains(refMessage.getCc())
+                        || activity.getCcAddresses()[1].contains(refMessage.getCc()));
                 assertTrue(activity.getCcAddresses()[0].contains("toaccount0@mockuiprovider.com")
                         || activity.getCcAddresses()[1]
                                 .contains("toaccount0@mockuiprovider.com"));
@@ -562,9 +562,9 @@
     public void testChangeModes2() {
         setAccount("account0@mockuiprovider.com");
         final Message refMessage = getRefMessage();
-        refMessage.from = "fromaccount@mockuiprovider.com";
-        refMessage.to = "account0@mockuiprovider.com, toaccount0@mockuiprovider.com";
-        refMessage.cc = "ccaccount@mockuiprovider.com, ccaccount2@mockuiprovider.com";
+        refMessage.setFrom("fromaccount@mockuiprovider.com");
+        refMessage.setTo("account0@mockuiprovider.com, toaccount0@mockuiprovider.com");
+        refMessage.setCc("ccaccount@mockuiprovider.com, ccaccount2@mockuiprovider.com");
         final ComposeActivity activity = mActivity;
         final Account account = mAccount;
         mActivity.runOnUiThread(new Runnable() {
@@ -575,12 +575,12 @@
                 String[] cc = activity.getCcAddresses();
                 String[] bcc = activity.getBccAddresses();
                 assertEquals(to.length, 1);
-                assertTrue(to[0].contains(refMessage.from));
+                assertTrue(to[0].contains(refMessage.getFrom()));
                 assertEquals(cc.length, 0);
                 assertEquals(bcc.length, 0);
                 activity.onNavigationItemSelected(1, ComposeActivity.REPLY_ALL);
                 assertEquals(activity.getToAddresses().length, 1);
-                assertTrue(activity.getToAddresses()[0].contains(refMessage.from));
+                assertTrue(activity.getToAddresses()[0].contains(refMessage.getFrom()));
                 assertEquals(activity.getCcAddresses().length, 3);
                 assertTrue(activity.getCcAddresses()[0].contains("ccaccount@mockuiprovider.com")
                         || activity.getCcAddresses()[1].contains("ccaccount@mockuiprovider.com")