Merge "Import translations. DO NOT MERGE" into jb-ub-mail
diff --git a/res/drawable-hdpi/default_image.9.png b/res/drawable-hdpi/default_image.9.png
index 1514f6f..cccd75e 100644
--- a/res/drawable-hdpi/default_image.9.png
+++ b/res/drawable-hdpi/default_image.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_read_holo.9.png b/res/drawable-hdpi/list_read_holo.9.png
index a77b602..a04c062 100644
--- a/res/drawable-hdpi/list_read_holo.9.png
+++ b/res/drawable-hdpi/list_read_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/default_image.9.png b/res/drawable-mdpi/default_image.9.png
index 55d9f66..ac257dc 100644
--- a/res/drawable-mdpi/default_image.9.png
+++ b/res/drawable-mdpi/default_image.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_read_holo.9.png b/res/drawable-mdpi/list_read_holo.9.png
index 1898e61..fc9bb9d 100644
--- a/res/drawable-mdpi/list_read_holo.9.png
+++ b/res/drawable-mdpi/list_read_holo.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/list_read_holo.9.png b/res/drawable-sw600dp-hdpi/list_read_holo.9.png
index 2c47f2a..b4712a0 100644
--- a/res/drawable-sw600dp-hdpi/list_read_holo.9.png
+++ b/res/drawable-sw600dp-hdpi/list_read_holo.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/list_read_holo.9.png b/res/drawable-sw600dp-mdpi/list_read_holo.9.png
index 1b0fcaf..34e6052 100644
--- a/res/drawable-sw600dp-mdpi/list_read_holo.9.png
+++ b/res/drawable-sw600dp-mdpi/list_read_holo.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/list_read_holo.9.png b/res/drawable-sw600dp-xhdpi/list_read_holo.9.png
index 09adb87..176e2d0 100644
--- a/res/drawable-sw600dp-xhdpi/list_read_holo.9.png
+++ b/res/drawable-sw600dp-xhdpi/list_read_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/default_image.9.png b/res/drawable-xhdpi/default_image.9.png
index 2771ff1..4b80f6c 100644
--- a/res/drawable-xhdpi/default_image.9.png
+++ b/res/drawable-xhdpi/default_image.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_read_holo.9.png b/res/drawable-xhdpi/list_read_holo.9.png
index 2c7b691..0f14404 100644
--- a/res/drawable-xhdpi/list_read_holo.9.png
+++ b/res/drawable-xhdpi/list_read_holo.9.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_mail.png b/res/mipmap-xhdpi/ic_launcher_mail.png
index 48247ce..6e1c194 100644
--- a/res/mipmap-xhdpi/ic_launcher_mail.png
+++ b/res/mipmap-xhdpi/ic_launcher_mail.png
Binary files differ
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index 610b73b..d211baf 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -99,4 +99,6 @@
     <dimen name="toast_bar_bottom_margin_in_conversation">24dip</dimen>
     <dimen name="undo_animation_offset">360dp</dimen>
     <dimen name="wait_padding">16dp</dimen>
+    <dimen name="senders_textview_top_padding">-4dp</dimen>
+    <dimen name="senders_textview_height">30dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7fc9f0b..aa6693b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -555,9 +555,9 @@
     <!-- Shown in a toast to acknowledge always showing images for a sender [CHAR LIMIT=100] -->
     <string name="always_show_images_toast">Pictures from this sender will be shown automatically.</string>
     <!-- Display format of an email recipient, displayed in expanded message details [CHAR LIMIT=10] -->
-    <string name="address_display_format"><xliff:g id="name">%1$s</xliff:g> &lt;<xliff:g id="email">%2$s</xliff:g>&gt;</string>
+    <string name="address_display_format"><xliff:g id="name">%1$s</xliff:g> <xliff:g id="email">%2$s</xliff:g></string>
     <!-- Display format of an email sender if the message has a via domain set, displayed in expanded message details [CHAR LIMIT=15] -->
-    <string name="address_display_format_with_via_domain"><xliff:g id="name">%1$s</xliff:g> &lt;<xliff:g id="email">%2$s</xliff:g>&gt; via <xliff:g id="via_domain">%3$s</xliff:g></string>
+    <string name="address_display_format_with_via_domain"><xliff:g id="name">%1$s</xliff:g> <xliff:g id="email">%2$s</xliff:g> via <xliff:g id="via_domain">%3$s</xliff:g></string>
     <!-- Displayed for one second after user saves message as draft [CHAR LIMIT=50]-->
     <string name="message_saved">Message saved as draft.</string>
     <!-- Displayed for one second while message is being sent [CHAR LIMIT=50]-->
diff --git a/src/com/android/mail/browse/AttachmentActionHandler.java b/src/com/android/mail/browse/AttachmentActionHandler.java
index d3028d3..5b3025f 100644
--- a/src/com/android/mail/browse/AttachmentActionHandler.java
+++ b/src/com/android/mail/browse/AttachmentActionHandler.java
@@ -125,8 +125,9 @@
     private void showDownloadingDialog() {
         mViewProgressDialog = new ProgressDialog(mContext);
         mViewProgressDialog.setTitle(R.string.fetching_attachment);
-        mViewProgressDialog.setMessage(mContext.getResources().getString(R.string.please_wait));
+        mViewProgressDialog.setMessage(mAttachment.name);
         mViewProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+        mViewProgressDialog.setIndeterminate(true);
         mViewProgressDialog.setMax(mAttachment.size);
         mViewProgressDialog.setOnDismissListener(this);
         mViewProgressDialog.setOnCancelListener(this);
@@ -156,7 +157,11 @@
 
         if (isProgressDialogVisible()) {
             mViewProgressDialog.setProgress(mAttachment.downloadedSize);
-            mViewProgressDialog.setIndeterminate(!showProgress);
+
+            // We don't want the progress bar to switch back to indeterminate mode after
+            // have been in determinate progress mode.
+            final boolean indeterminate = !showProgress && mViewProgressDialog.isIndeterminate();
+            mViewProgressDialog.setIndeterminate(indeterminate);
 
             if (!mAttachment.isDownloading()) {
                 mViewProgressDialog.dismiss();
diff --git a/src/com/android/mail/browse/ConversationCursor.java b/src/com/android/mail/browse/ConversationCursor.java
index 5ba886d..107ef3a 100644
--- a/src/com/android/mail/browse/ConversationCursor.java
+++ b/src/com/android/mail/browse/ConversationCursor.java
@@ -231,6 +231,11 @@
         @Override
         protected void onPostExecute(Void param) {
             synchronized(mCacheMapLock) {
+                // If cursor got closed (e.g. reset loader) in the meantime, cancel the refresh
+                if (isClosed()) {
+                    onCancelled();
+                    return;
+                }
                 mRequeryCursor = mCursor;
                 // Make sure window is full
                 mRequeryCursor.getCount();
@@ -790,7 +795,7 @@
                 }
             }
             return true;
-        } else if ((mPosition - pos) > pos) {
+        } else if ((pos >= 0) && (mPosition - pos) > pos) {
             // Optimization if it's easier to move forward to position instead of backward
             // STOPSHIP (Remove logging)
             LogUtils.i(TAG, "*** Move from %d to %d, starting from first", mPosition, pos);
diff --git a/src/com/android/mail/browse/ConversationItemView.java b/src/com/android/mail/browse/ConversationItemView.java
index 3aab551..edbc3b2 100644
--- a/src/com/android/mail/browse/ConversationItemView.java
+++ b/src/com/android/mail/browse/ConversationItemView.java
@@ -21,6 +21,8 @@
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.content.ClipData;
+import android.content.ClipData.Item;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -30,6 +32,7 @@
 import android.graphics.Color;
 import android.graphics.LinearGradient;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.graphics.Typeface;
@@ -47,10 +50,15 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
 import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ListView;
+import android.widget.TextView;
 
 import com.android.mail.R;
 import com.android.mail.browse.ConversationItemViewModel.SenderFragment;
@@ -67,6 +75,7 @@
 import com.android.mail.ui.SwipeableListView;
 import com.android.mail.ui.ViewMode;
 import com.android.mail.utils.LogTag;
+import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
 import com.google.common.annotations.VisibleForTesting;
 
@@ -102,7 +111,6 @@
 
     private static String sSendersSplitToken;
     private static String sElidedPaddingToken;
-    private static String sEllipsis;
 
     // Static colors.
     private static int sDefaultTextColor;
@@ -156,12 +164,16 @@
     private boolean mPriorityMarkersEnabled;
     private boolean mCheckboxesEnabled;
     private boolean mSwipeEnabled;
+    private int mLastTouchX;
+    private int mLastTouchY;
     private AnimatedAdapter mAdapter;
     private int mAnimatedHeight = -1;
     private String mAccount;
     private ControllableActivity mActivity;
     private CharacterStyle mActivatedTextSpan;
     private int mBackgroundOverride = -1;
+    private static int sSendersTextViewTopPadding;
+    private static int sSendersTextViewHeight;
     private static ForegroundColorSpan sActivatedTextSpan;
     private static Bitmap sDateBackgroundAttachment;
     private static Bitmap sDateBackgroundNoAttachment;
@@ -366,8 +378,11 @@
             // Initialize static color.
             sSendersSplitToken = res.getString(R.string.senders_split_token);
             sElidedPaddingToken = res.getString(R.string.elided_padding_token);
-            sEllipsis = res.getString(R.string.ellipsis);
             sAnimatingBackgroundColor = res.getColor(R.color.animating_item_background_color);
+            sSendersTextViewTopPadding = res.getDimensionPixelSize
+                    (R.dimen.senders_textview_top_padding);
+            sSendersTextViewHeight = res.getDimensionPixelSize
+                    (R.dimen.senders_textview_height);
         }
     }
 
@@ -585,11 +600,19 @@
         } else {
             mHeader.styledSendersString.removeSpan(mActivatedTextSpan);
         }
+        mHeader.sendersTextView = getSendersTextView();
+    }
+
+    private TextView getSendersTextView() {
+        TextView sendersTextView = new TextView(mContext);
+        sendersTextView.setMaxLines(1);
+        sendersTextView.setEllipsize(TextUtils.TruncateAt.END);
+        sendersTextView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
         int length = (int) sPaint.measureText(mHeader.styledSendersString.toString());
-        mHeader.sendersDisplayLayout = new StaticLayout(mHeader.styledSendersString, 0,
-                mHeader.styledSendersString.length(), sPaint,
-                length,
-                Alignment.ALIGN_NORMAL, 1, 0, true, TextUtils.TruncateAt.END, length);
+        sendersTextView.setText(mHeader.styledSendersString, TextView.BufferType.SPANNABLE);
+        sendersTextView.setWidth(length);
+        return sendersTextView;
     }
 
     private CharacterStyle getActivatedTextSpan() {
@@ -962,21 +985,24 @@
         // Senders.
         boolean isUnread = mHeader.unread;
         // Old style senders; apply text colors/ sizes/ styling.
-        if (mHeader.senderFragments.size() > 0) {
+        canvas.save();
+        if (mHeader.sendersDisplayLayout != null) {
             sPaint.setTextSize(mCoordinates.sendersFontSize);
             sPaint.setTypeface(SendersView.getTypeface(isUnread));
-            int sendersColor = getFontColor(isUnread ? sSendersTextColorUnread
-                    : sSendersTextColorRead);
-            sPaint.setColor(sendersColor);
+            sPaint.setColor(getFontColor(isUnread ? sSendersTextColorUnread
+                    : sSendersTextColorRead));
+            canvas.translate(mCoordinates.sendersX,
+                    mCoordinates.sendersY + mHeader.sendersDisplayLayout.getTopPadding());
+            mHeader.sendersDisplayLayout.draw(canvas);
         } else {
-            sPaint.setTextSize(mCoordinates.sendersFontSize);
+            canvas.translate(mCoordinates.sendersX,
+                    mCoordinates.sendersY + sSendersTextViewTopPadding);
+            mHeader.sendersTextView.layout(0, 0, mSendersWidth, sSendersTextViewHeight);
+            mHeader.sendersTextView.draw(canvas);
         }
-        canvas.save();
-        canvas.translate(mCoordinates.sendersX,
-                mCoordinates.sendersY + mHeader.sendersDisplayLayout.getTopPadding());
-        mHeader.sendersDisplayLayout.draw(canvas);
         canvas.restore();
 
+
         // Subject.
         sPaint.setTextSize(mCoordinates.subjectFontSize);
         sPaint.setTypeface(Typeface.DEFAULT);
@@ -1151,23 +1177,26 @@
      * Toggle the check mark on this view and update the conversation
      */
     public void toggleCheckMark() {
-        if (mHeader != null && mHeader.conversation != null) {
-            mChecked = !mChecked;
-            Conversation conv = mHeader.conversation;
-            // Set the list position of this item in the conversation
-            ListView listView = getListView();
-            conv.position = mChecked && listView != null ? listView.getPositionForView(this)
-                    : Conversation.NO_POSITION;
-            if (mSelectedConversationSet != null) {
-                mSelectedConversationSet.toggle(this, conv);
+        ViewMode mode = mActivity.getViewMode();
+        if (!mTabletDevice || !mode.isListMode()) {
+            if (mHeader != null && mHeader.conversation != null) {
+                mChecked = !mChecked;
+                Conversation conv = mHeader.conversation;
+                // Set the list position of this item in the conversation
+                ListView listView = getListView();
+                conv.position = mChecked && listView != null ? listView.getPositionForView(this)
+                        : Conversation.NO_POSITION;
+                if (mSelectedConversationSet != null) {
+                    mSelectedConversationSet.toggle(this, conv);
+                }
+                // We update the background after the checked state has changed
+                // now that we have a selected background asset. Setting the background
+                // usually waits for a layout pass, but we don't need a full layout,
+                // just an update to the background.
+                requestLayout();
             }
-            // We update the background after the checked state has changed now
-            // that
-            // we have a selected background asset. Setting the background
-            // usually
-            // waits for a layout pass, but we don't need a full layout, just an
-            // update to the background.
-            requestLayout();
+        } else {
+            beginDragMode();
         }
     }
 
@@ -1222,6 +1251,8 @@
     public boolean onTouchEvent(MotionEvent event) {
         int x = (int) event.getX();
         int y = (int) event.getY();
+        mLastTouchX = x;
+        mLastTouchY = y;
         if (!mSwipeEnabled) {
             return onTouchEventNoSwipe(event);
         }
@@ -1277,29 +1308,26 @@
     }
 
     private boolean onTouchEventNoSwipe(MotionEvent event) {
-        boolean handled = true;
+        boolean handled = false;
 
         int x = (int) event.getX();
         int y = (int) event.getY();
+        mLastTouchX = x;
+        mLastTouchY = y;
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 mDownEvent = true;
-                // In order to allow the down event and subsequent move events
-                // to bubble to the swipe handler, we need to return that all
-                // down events are handled.
-                handled = isTouchInCheckmark(x, y) || isTouchInStar(x, y);
+                if (isTouchInCheckmark(x, y) || isTouchInStar(x, y)) {
+                    handled = true;
+                }
                 break;
+
             case MotionEvent.ACTION_CANCEL:
                 mDownEvent = false;
                 break;
+
             case MotionEvent.ACTION_UP:
                 if (mDownEvent) {
-                    // ConversationItemView gets the first chance to handle up
-                    // events if there was a down event and there was no move
-                    // event in between. In this case, ConversationItemView
-                    // received the down event, and then an up event in the
-                    // same location (+/- slop). Treat this as a click on the
-                    // view or on a specific part of the view.
                     if (isTouchInCheckmark(x, y)) {
                         // Touch on the check mark
                         toggleCheckMark();
@@ -1308,16 +1336,15 @@
                         toggleStar();
                     }
                     handled = true;
-                } else {
-                    // There was no down event that this view was made aware of,
-                    // therefore it cannot handle it.
-                    handled = false;
                 }
                 break;
         }
 
-        // Let View try to handle it as well.
-        return handled || super.onTouchEvent(event);
+        if (!handled) {
+            handled = super.onTouchEvent(event);
+        }
+
+        return handled;
     }
 
     /**
@@ -1470,4 +1497,105 @@
     public View getSwipeableView() {
         return this;
     }
+
+    /**
+     * Select the current conversation.
+     */
+    private void selectConversation() {
+        if (!mSelectedConversationSet.containsKey(mHeader.conversation.id)) {
+            mChecked = !mChecked;
+            Conversation conv = mHeader.conversation;
+            // Set the list position of this item in the conversation
+            ListView listView = getListView();
+            conv.position = mChecked && listView != null ? listView.getPositionForView(this)
+                    : Conversation.NO_POSITION;
+            if (mSelectedConversationSet != null) {
+                mSelectedConversationSet.toggle(this, conv);
+            }
+        }
+    }
+
+    /**
+     * Begin drag mode. Keep the conversation selected (NOT toggle selection) and start drag.
+     */
+    private void beginDragMode() {
+        selectConversation();
+
+        // Clip data has form: [conversations_uri, conversationId1,
+        // maxMessageId1, label1, conversationId2, maxMessageId2, label2, ...]
+        final int count = mSelectedConversationSet.size();
+        String description = Utils.formatPlural(mContext, R.plurals.move_conversation, count);
+
+        final ClipData data = ClipData.newUri(mContext.getContentResolver(), description,
+                Conversation.MOVE_CONVERSATIONS_URI);
+        for (Conversation conversation : mSelectedConversationSet.values()) {
+            data.addItem(new Item(String.valueOf(conversation.position)));
+        }
+        // Protect against non-existent views: only happens for monkeys
+        final int width = this.getWidth();
+        final int height = this.getHeight();
+        final boolean isDimensionNegative = (width < 0) || (height < 0);
+        if (isDimensionNegative) {
+            LogUtils.e(LOG_TAG, "ConversationItemView: dimension is negative: "
+                        + "width=%d, height=%d", width, height);
+            return;
+        }
+        mActivity.startDragMode();
+        // Start drag mode
+        startDrag(data, new ShadowBuilder(this, count, mLastTouchX, mLastTouchY), null, 0);
+    }
+
+    /**
+     * Handles the drag event.
+     *
+     * @param event the drag event to be handled
+     */
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_ENDED:
+                mActivity.stopDragMode();
+                return true;
+        }
+        return false;
+    }
+
+    private class ShadowBuilder extends DragShadowBuilder {
+        private final Drawable mBackground;
+
+        private final View mView;
+        private final String mDragDesc;
+        private final int mTouchX;
+        private final int mTouchY;
+        private int mDragDescX;
+        private int mDragDescY;
+
+        public ShadowBuilder(View view, int count, int touchX, int touchY) {
+            super(view);
+            mView = view;
+            mBackground = mView.getResources().getDrawable(R.drawable.list_pressed_holo);
+            mDragDesc = Utils.formatPlural(mView.getContext(), R.plurals.move_conversation, count);
+            mTouchX = touchX;
+            mTouchY = touchY;
+        }
+
+        @Override
+        public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
+            int width = mView.getWidth();
+            int height = mView.getHeight();
+            mDragDescX = mCoordinates.sendersX;
+            mDragDescY = getPadding(height, mCoordinates.subjectFontSize)
+                    - mCoordinates.subjectAscent;
+            shadowSize.set(width, height);
+            shadowTouchPoint.set(mTouchX, mTouchY);
+        }
+
+        @Override
+        public void onDrawShadow(Canvas canvas) {
+            mBackground.setBounds(0, 0, mView.getWidth(), mView.getHeight());
+            mBackground.draw(canvas);
+            sPaint.setTextSize(mCoordinates.subjectFontSize);
+            canvas.drawText(mDragDesc, mDragDescX, mDragDescY, sPaint);
+        }
+    }
 }
diff --git a/src/com/android/mail/browse/ConversationItemViewModel.java b/src/com/android/mail/browse/ConversationItemViewModel.java
index 44227e3..22d17a8 100644
--- a/src/com/android/mail/browse/ConversationItemViewModel.java
+++ b/src/com/android/mail/browse/ConversationItemViewModel.java
@@ -33,6 +33,7 @@
 import android.text.style.CharacterStyle;
 import android.util.LruCache;
 import android.util.Pair;
+import android.widget.TextView;
 
 import com.android.mail.R;
 import com.android.mail.providers.Conversation;
@@ -130,6 +131,8 @@
 
     private String mContentDescription;
 
+    public TextView sendersTextView;
+
     /**
      * Returns the view model for a conversation. If the model doesn't exist for this conversation
      * null is returned. Note: this should only be called from the UI thread.
diff --git a/src/com/android/mail/browse/ConversationPagerController.java b/src/com/android/mail/browse/ConversationPagerController.java
index 1c14a02..e68030a 100644
--- a/src/com/android/mail/browse/ConversationPagerController.java
+++ b/src/com/android/mail/browse/ConversationPagerController.java
@@ -186,6 +186,8 @@
      * {@link #hide()}.
      */
     public void stopListening() {
-        mPagerAdapter.setActivityController(null);
+        if (mPagerAdapter != null) {
+            mPagerAdapter.setActivityController(null);
+        }
     }
 }
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 39654d1..6496f67 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -116,7 +116,7 @@
     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
         boolean handled = true;
         // If the user taps a new menu item, commit any existing destructive actions.
-        mListView.commitDestructiveActions();
+        mListView.commitDestructiveActions(true);
         switch (item.getItemId()) {
             case R.id.delete:
                 performDestructiveAction(R.id.delete);
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index e1671c0..30a29b5 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -251,6 +251,7 @@
     // will just run the next time the user opens the app.
     private AsyncTask<String, Void, Void> mEnableShareIntents;
     private Folder mFolderListFolder;
+    private boolean mIsDragHappening;
     public static final String SYNC_ERROR_DIALOG_FRAGMENT_TAG = "SyncErrorDialogFragment";
 
     public AbstractActivityController(MailActivity activity, ViewMode viewMode) {
@@ -389,6 +390,11 @@
         LogUtils.d(LOG_TAG, "AbstractActivityController.switchAccount(): mAccount = %s",
                 mAccount.uri);
         cancelRefreshTask();
+        // If we are currently listening to changes in conversation, the account switch is going to
+        // make us do a lot of un-necessary work. Let's stop listening to changes.
+        if (mPagerController != null) {
+            mPagerController.stopListening();
+        }
         updateSettings();
         if (shouldReloadInbox) {
             loadAccountInbox();
@@ -417,6 +423,9 @@
                     }
                 });
             }
+            if (accountChanged) {
+                commitDestructiveActions(false);
+            }
             switchAccount(account, accountChanged);
         }
     }
@@ -467,6 +476,9 @@
 
     @Override
     public void onFolderChanged(Folder folder) {
+        if (!Objects.equal(mFolder, folder)) {
+            commitDestructiveActions(false);
+        }
         changeFolder(folder, null);
     }
 
@@ -526,6 +538,11 @@
             mFolder = folder;
             mActionBarView.setFolder(mFolder);
 
+            // Stop listening to changes to the previous folder.
+            if (mPagerController != null) {
+                mPagerController.stopListening();
+            }
+
             // Only when we switch from one folder to another do we want to restart the
             // folder and conversation list loaders (to trigger onCreateLoader).
             // The first time this runs when the activity is [re-]initialized, we want to re-use the
@@ -690,7 +707,7 @@
         final Collection<Conversation> target = Conversation.listOf(mCurrentConversation);
         final Settings settings = (mAccount == null) ? null : mAccount.settings;
         // The user is choosing a new action; commit whatever they had been doing before.
-        commitDestructiveActions();
+        commitDestructiveActions(true);
         switch (id) {
             case R.id.archive: {
                 final boolean showDialog = (settings != null && settings.confirmArchive);
@@ -1100,7 +1117,7 @@
         // this themselves?
 
         // Commit any destructive undoable actions the user may have performed.
-        commitDestructiveActions();
+        commitDestructiveActions(true);
 
         // We don't want to invalidate the options menu when switching to
         // conversation
@@ -1114,10 +1131,10 @@
         return mDestroyed;
     }
 
-    private void commitDestructiveActions() {
-        ConversationListFragment fragment = this.getConversationListFragment();
+    private void commitDestructiveActions(boolean animate) {
+        ConversationListFragment fragment = getConversationListFragment();
         if (fragment != null) {
-            fragment.commitDestructiveActions();
+            fragment.commitDestructiveActions(animate);
         }
     }
 
@@ -1917,7 +1934,7 @@
 
     @Override
     public final void onRefreshRequired() {
-        if (isAnimating()) {
+        if (isAnimating() || isDragging()) {
             LogUtils.d(LOG_TAG, "onRefreshRequired: delay until animating done");
             return;
         }
@@ -1927,6 +1944,29 @@
         }
     }
 
+    @Override
+    public void startDragMode() {
+        mIsDragHappening = true;
+    }
+
+    @Override
+    public void stopDragMode() {
+        mIsDragHappening = false;
+        if (mConversationListCursor.isRefreshReady()) {
+            LogUtils.d(LOG_TAG, "Stopped animating: try sync");
+            onRefreshReady();
+        }
+
+        if (mConversationListCursor.isRefreshRequired()) {
+            LogUtils.d(LOG_TAG, "Stopped animating: refresh");
+            mConversationListCursor.refresh();
+        }
+    }
+
+    private boolean isDragging() {
+        return mIsDragHappening;
+    }
+
     private boolean isAnimating() {
         boolean isAnimating = false;
         ConversationListFragment convListFragment = getConversationListFragment();
@@ -2048,7 +2088,7 @@
      */
     protected void disableCabMode() {
         // Commit any previous destructive actions when entering/ exiting CAB mode.
-        commitDestructiveActions();
+        commitDestructiveActions(true);
         if (mCabActionMenu != null) {
             mCabActionMenu.deactivate();
         }
@@ -2059,7 +2099,7 @@
      */
     protected void enableCabMode() {
         // Commit any previous destructive actions when entering/ exiting CAB mode.
-        commitDestructiveActions();
+        commitDestructiveActions(true);
         if (mCabActionMenu != null) {
             mCabActionMenu.activate();
         }
diff --git a/src/com/android/mail/ui/ActivityController.java b/src/com/android/mail/ui/ActivityController.java
index 524ad76..59d6776 100644
--- a/src/com/android/mail/ui/ActivityController.java
+++ b/src/com/android/mail/ui/ActivityController.java
@@ -308,4 +308,14 @@
      * Handles the animation end of the animated adapter.
      */
     void onAnimationEnd(AnimatedAdapter animatedAdapter);
+
+    /**
+     * Called when the user has started a drag/ drop gesture.
+     */
+    void startDragMode();
+
+    /**
+     * Called when the user has ended drag/drop.
+     */
+    void stopDragMode();
 }
diff --git a/src/com/android/mail/ui/AnimatedAdapter.java b/src/com/android/mail/ui/AnimatedAdapter.java
index 2cdbea6..73dfca8 100644
--- a/src/com/android/mail/ui/AnimatedAdapter.java
+++ b/src/com/android/mail/ui/AnimatedAdapter.java
@@ -356,11 +356,15 @@
         return mListView;
     }
 
-    public void commitLeaveBehindItems() {
+    public void commitLeaveBehindItems(boolean animate) {
         // Remove any previously existing leave behinds.
         boolean changed = false;
         if (hasLeaveBehinds()) {
-            mLeaveBehindItem.dismiss();
+            if (animate) {
+                mLeaveBehindItem.dismiss();
+            } else {
+                mLeaveBehindItem.commit();
+            }
             changed = true;
         }
         if (!mLastDeletingItems.isEmpty()) {
diff --git a/src/com/android/mail/ui/ControllableActivity.java b/src/com/android/mail/ui/ControllableActivity.java
index d4046a6..63d3318 100644
--- a/src/com/android/mail/ui/ControllableActivity.java
+++ b/src/com/android/mail/ui/ControllableActivity.java
@@ -105,4 +105,8 @@
      * @return
      */
     AccountController getAccountController();
+
+    void startDragMode();
+
+    void stopDragMode();
 }
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index cede3e7..c6d4328 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -639,9 +639,9 @@
         }
     }
 
-    public void commitDestructiveActions() {
+    public void commitDestructiveActions(boolean animate) {
         if (mListView != null) {
-            mListView.commitDestructiveActions();
+            mListView.commitDestructiveActions(animate);
 
         }
     }
diff --git a/src/com/android/mail/ui/FolderSelectionActivity.java b/src/com/android/mail/ui/FolderSelectionActivity.java
index 1843d65..c698e6d 100644
--- a/src/com/android/mail/ui/FolderSelectionActivity.java
+++ b/src/com/android/mail/ui/FolderSelectionActivity.java
@@ -341,4 +341,14 @@
     public void onFooterViewLoadMoreClick(Folder folder) {
         // Unsupported
     }
+
+    @Override
+    public void startDragMode() {
+        // Unsupported
+    }
+
+    @Override
+    public void stopDragMode() {
+        // Unsupported
+    }
 }
diff --git a/src/com/android/mail/ui/MailActionBarView.java b/src/com/android/mail/ui/MailActionBarView.java
index 0ba9bd4..b5f8060 100644
--- a/src/com/android/mail/ui/MailActionBarView.java
+++ b/src/com/android/mail/ui/MailActionBarView.java
@@ -405,6 +405,11 @@
                 setStandardMode();
                 mFolderView.setVisibility(View.VISIBLE);
                 break;
+            case ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION:
+                // We want the user to be able to switch accounts while waiting for an account
+                // to sync.
+                showNavList();
+                break;
         }
         return false;
     }
diff --git a/src/com/android/mail/ui/MailActivity.java b/src/com/android/mail/ui/MailActivity.java
index 0418f8d..4a4c80c 100644
--- a/src/com/android/mail/ui/MailActivity.java
+++ b/src/com/android/mail/ui/MailActivity.java
@@ -423,4 +423,14 @@
     public void onFooterViewLoadMoreClick(Folder folder) {
         mController.onFooterViewLoadMoreClick(folder);
     }
+
+    @Override
+    public void startDragMode() {
+        mController.startDragMode();
+    }
+
+    @Override
+    public void stopDragMode() {
+        mController.stopDragMode();
+    }
 }
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index 27b0b5d..75b3575 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -112,6 +112,10 @@
     @Override
     public void resetActionBarIcon() {
         final int mode = mViewMode.getMode();
+        // If the viewmode is not set, preserve existing icon.
+        if (mode == ViewMode.UNKNOWN) {
+            return;
+        }
         if (!inInbox(mAccount, mConvListContext)
                 || mode == ViewMode.SEARCH_RESULTS_LIST
                 || mode == ViewMode.SEARCH_RESULTS_CONVERSATION
@@ -439,10 +443,8 @@
         }
         if (isTransactionIdValid(mLastConversationListTransactionId)) {
             mActivity.getFragmentManager().popBackStack(mLastConversationListTransactionId, 0);
-            resetActionBarIcon();
         } else if (isTransactionIdValid(mLastInboxConversationListTransactionId)) {
             mActivity.getFragmentManager().popBackStack(mLastInboxConversationListTransactionId, 0);
-            resetActionBarIcon();
             onFolderChanged(mInbox);
         } else {
             // TODO: revisit if this block is necessary
diff --git a/src/com/android/mail/ui/SwipeableListView.java b/src/com/android/mail/ui/SwipeableListView.java
index a6bf55c..745cd37 100644
--- a/src/com/android/mail/ui/SwipeableListView.java
+++ b/src/com/android/mail/ui/SwipeableListView.java
@@ -162,10 +162,10 @@
 
     // Call this whenever a new action is taken; this forces a commit of any
     // existing destructive actions.
-    public void commitDestructiveActions() {
+    public void commitDestructiveActions(boolean animate) {
         final AnimatedAdapter adapter = getAnimatedAdapter();
         if (adapter != null) {
-            adapter.commitLeaveBehindItems();
+            adapter.commitLeaveBehindItems(animate);
         }
     }
 
@@ -297,7 +297,7 @@
         boolean handled = super.performItemClick(view, pos, id);
         // Commit any existing destructive actions when the user selects a
         // conversation to view.
-        commitDestructiveActions();
+        commitDestructiveActions(true);
         return handled;
     }
 
@@ -310,7 +310,7 @@
     public void onScrollStateChanged(AbsListView view, int state) {
         if (state == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL
                 || state == OnScrollListener.SCROLL_STATE_FLING) {
-            commitDestructiveActions();
+            commitDestructiveActions(true);
         }
     }
 }
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 506b0a0..953c2a5 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -229,6 +229,10 @@
 
     @Override
     public void resetActionBarIcon() {
+        // If the viewmode is not set, preserve existing icon.
+        if (mViewMode.getMode() == ViewMode.UNKNOWN) {
+            return;
+        }
         if (mViewMode.isListMode()) {
             mActionBarView.removeBackButton();
         } else {
diff --git a/src/com/android/mail/utils/AttachmentUtils.java b/src/com/android/mail/utils/AttachmentUtils.java
index ff9339b..2ab5767 100644
--- a/src/com/android/mail/utils/AttachmentUtils.java
+++ b/src/com/android/mail/utils/AttachmentUtils.java
@@ -41,7 +41,9 @@
      *         depending on its size.
      */
     public static String convertToHumanReadableSize(Context context, long size) {
-        if (size < KILO) {
+        if (size == 0) {
+            return "";
+        } if (size < KILO) {
             return size + context.getString(R.string.bytes);
         } else if (size < MEGA) {
             return (size / KILO) + context.getString(R.string.kilobytes);