Merge "If conversation list cursor is not loaded, defer mark unread until it's loaded." into jb-ub-mail-ur8
diff --git a/assets/script.js b/assets/script.js
index 6ea24e8..37c266e 100644
--- a/assets/script.js
+++ b/assets/script.js
@@ -487,23 +487,29 @@
 
             msgDivTop = getTotalOffset(msgBodyDiv).top;
 
-            scaledOriginX = focusX;// / initialScale;
-            scaledOriginY = (focusY - msgDivTop);// / initialScale;
+            // TODO: correct only for no initial translation
+            // FIXME: wrong for initialScale > 1.0
+            scaledOriginX = focusX / initialScale;
+            scaledOriginY = (focusY - msgDivTop) / initialScale;
 
-            translateX = scaledOriginX * (initialScale - 1.0) / initialScale;
-            translateY = scaledOriginY * (initialScale - 1.0) / initialScale;
+            // TODO: is this still needed?
+            translateX = 0;
+            translateY = 0;
 
             gScaleInfo = {
                 div: msgBodyDiv,
-                divTop: msgDivTop,
                 initialScale: initialScale,
-                initialX: focusX,
-                initialY: focusY,
+                initialScreenX: screenX,
+                initialScreenY: screenY,
+                originX: scaledOriginX,
+                originY: scaledOriginY,
                 translateX: translateX,
                 translateY: translateY,
                 initialH: getCachedValue(msgBodyDiv, "offsetHeight", "data-initial-height"),
                 minScale: Math.min(document.body.offsetWidth / msgBodyDiv.scrollWidth, 1.0),
-                currScale: initialScale
+                currScale: initialScale,
+                currTranslateX: 0,
+                currTranslateY: 0
             };
 
             origin = scaledOriginX + "px " + scaledOriginY + "px";
@@ -530,19 +536,31 @@
     scale = gScaleInfo.currScale;
     msgBodyDiv.style.webkitTransformOrigin = "0 0";
     // clear any translate
-    msgBodyDiv.style.webkitTransform = "scale3d(" + scale + "," + scale + ",1)";
     // switching to a 2D transform here re-renders the fonts more clearly, but introduces
     // texture upload lag to any subsequent scale operation
-    //msgBodyDiv.style.webkitTransform = "scale(" + gScaleInfo.currScale + ")";
+    // TODO: conditionalize this based on device GPU performance and/or body size/complexity?
+    if (true) {
+        msgBodyDiv.style.webkitTransform = "scale(" + gScaleInfo.currScale + ")";
+    } else {
+        msgBodyDiv.style.webkitTransform = "scale3d(" + scale + "," + scale + ",1)";
+    }
     h = gScaleInfo.initialH * scale;
 //    console.log("onScaleEnd set h=" + h);
     msgBodyDiv.style.height = h + "px";
+
+    // Use saved translateX/Y rather than calculating from screenX/Y because screenX/Y values
+    // from onScaleEnd only track focus of remaining pointers, which is not useful and leads
+    // to a perceived jump.
+    var deltaScrollX = (scale - 1) * gScaleInfo.originX - gScaleInfo.currTranslateX;
+    var deltaScrollY = (scale - 1) * gScaleInfo.originY - gScaleInfo.currTranslateY;
+//    console.log("JS adjusting scroll by x/y=" + deltaScrollX + "/" + deltaScrollY);
+    window.scrollBy(deltaScrollX, deltaScrollY);
+
     msgBodyDiv.classList.remove("zooming-focused");
     msgBodyDiv.setAttribute("data-initial-scale", scale);
 }
 
 function onScale(relativeScale, screenX, screenY) {
-    var focusX, focusY;
     var scale;
     var translateX, translateY;
     var transform;
@@ -550,21 +568,22 @@
     if (!gScaleInfo) {
         return;
     }
-    focusX = screenX + document.body.scrollLeft;
-    focusY = screenY + document.body.scrollTop;
 
     scale = Math.max(gScaleInfo.initialScale * relativeScale, gScaleInfo.minScale);
     if (scale > 4.0) {
         scale = 4.0;
     }
+    translateX = screenX - gScaleInfo.initialScreenX;
+    translateY = screenY - gScaleInfo.initialScreenY;
+    // TODO: clamp translation to prevent going beyond body edges
     gScaleInfo.currScale = scale;
-    translateX = focusX - gScaleInfo.initialX;
-    translateY = focusY - gScaleInfo.initialY;
+    gScaleInfo.currTranslateX = translateX;
+    gScaleInfo.currTranslateY = translateY;
     transform = "translate3d(" + translateX + "px," + translateY + "px,0) scale3d("
         + scale + "," + scale + ",1) translate3d(" + gScaleInfo.translateX + "px,"
         + gScaleInfo.translateY + "px,0)";
     gScaleInfo.div.style.webkitTransform = transform;
-//    console.log("JS got scale=" + relativeScale + " x/y=" + screenX + "/" + screenY
+//    console.log("JS got scale=" + scale + " x/y=" + screenX + "/" + screenY
 //        + " transform='" + transform + "'");
 }
 
diff --git a/res/drawable-hdpi/ic_contact_picture.png b/res/drawable-hdpi/ic_contact_picture.png
deleted file mode 100644
index 2eef7b5..0000000
--- a/res/drawable-hdpi/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index a17da61..0000000
--- a/res/drawable-hdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_180_holo_light.png b/res/drawable-hdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index 38e4c30..0000000
--- a/res/drawable-hdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_holo_dark.png b/res/drawable-hdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index 314fa00..0000000
--- a/res/drawable-hdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture.png b/res/drawable-mdpi/ic_contact_picture.png
deleted file mode 100644
index 6c7cb61..0000000
--- a/res/drawable-mdpi/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index acba333..0000000
--- a/res/drawable-mdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_180_holo_light.png b/res/drawable-mdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index 0b52683..0000000
--- a/res/drawable-mdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_holo_dark.png b/res/drawable-mdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index 6876777..0000000
--- a/res/drawable-mdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture.png b/res/drawable-xhdpi/ic_contact_picture.png
deleted file mode 100644
index 1a2bfde..0000000
--- a/res/drawable-xhdpi/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png b/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png
deleted file mode 100644
index c4c001e..0000000
--- a/res/drawable-xhdpi/ic_contact_picture_180_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png b/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png
deleted file mode 100644
index f6fd172..0000000
--- a/res/drawable-xhdpi/ic_contact_picture_180_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_holo_dark.png b/res/drawable-xhdpi/ic_contact_picture_holo_dark.png
deleted file mode 100644
index ddf797f..0000000
--- a/res/drawable-xhdpi/ic_contact_picture_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 3425332..58c6af5 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 5736f1f..6589f8d 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 142e90a..211d2c6 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 25c2f60..2aaa2ee 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index f65f926..038a698 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index bac23fe..118f9f4 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 266f150..e3a0d99 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 441a84e..82d330d 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index dc6b6d0..d9fc3f8 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -159,7 +159,7 @@
   </plurals>
     <string name="sending" msgid="8214361929125649771">"Senden..."</string>
     <string name="send_failed" msgid="750908595144529579">"Nicht gesendet"</string>
-    <string name="me" msgid="6480762904022198669">"Ich"</string>
+    <string name="me" msgid="6480762904022198669">"Mich"</string>
     <string name="show_all_folders" msgid="3281420732307737553">"Alle Ordner anzeigen"</string>
   <plurals name="confirm_delete_conversation">
     <item quantity="one" msgid="3731948757247905508">"Diese Konversation löschen?"</item>
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 1f98e7e..3e2dd3b 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index b4b66ee..0a416c8 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 7071556..fc31f4f 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index a619c6c..1fdb09b 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 0e1916d..f8ba65e 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 43c3476..003af78 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9c4977d..775184f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 2789824..b88157c 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b1f777e..24e6488 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 497fe8d..e65bfc4 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index cfc7455..acfb7c9 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index e3522b0..68c0188 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 28b4929..ba83a3a 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index b0bab2e..e438719 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 11fb71c..41af5d4 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index db069ed..97700a4 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 896de33..caeea2a 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index ad9b18e..a4296fc 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index eddc95a..8e301ee 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 08fbb8f..c4723d6 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 7ee00c9..5e8b86a 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -246,7 +246,7 @@
     <string name="cc_heading" msgid="8116221732072085180">"Cc:"</string>
     <string name="bcc_heading" msgid="5528824453033598299">"Bcc:"</string>
     <string name="show_images" msgid="1875700481186331432">"Afbeeldingen weergeven"</string>
-    <string name="always_show_images" msgid="5966408990164581801">"Foto\'s van deze afzender altijd tonen"</string>
+    <string name="always_show_images" msgid="5966408990164581801">"Afbeeldingen van deze afzender altijd tonen"</string>
     <string name="always_show_images_toast" msgid="6218061921546973716">"Foto\'s van deze afzender worden automatisch weergegeven."</string>
     <string name="address_display_format" msgid="3581303608028992576">"<xliff:g id="NAME">%1$s</xliff:g> <xliff:g id="EMAIL">%2$s</xliff:g>"</string>
     <string name="address_display_format_with_via_domain" msgid="2316262813990842490">"<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>
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 6dafe30..3b0b285 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -360,4 +360,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index dc7d235..d83f8c6 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index c0469e4..e1dcc64 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 10d5526..c1edf37 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -567,4 +567,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index a959ef7..bda89e3 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index e2ae037..7f7b3ec 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 52ffa92..b69719e 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 3001253..461d4d9 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 63a6a75..d2b5296 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 263f05f..b5ac4fd 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 5dac64a..8146224 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-sw600dp/dimen.xml b/res/values-sw600dp/dimen.xml
index 61d6cb3..237bfc0 100644
--- a/res/values-sw600dp/dimen.xml
+++ b/res/values-sw600dp/dimen.xml
@@ -43,7 +43,6 @@
     <dimen name="attachment_tile_min_size">180dp</dimen>
     <dimen name="attachment_tile_max_size">254dp</dimen>
     <dimen name="wait_padding">32dp</dimen>
-    <dimen name="folders_left_padding">8dip</dimen>
 
     <dimen name="compose_area_left_padding">80dip</dimen>
     <dimen name="compose_area_right_padding">80dip</dimen>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 0741bf7..606c471 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 61bd70a..bcede38 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index fa0c3da..338065a 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index da4b0ee..5f1329b 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 9c9b45c..c16dd39 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index f9b38bb..3bee401 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 65c5129..da9776b 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index f46a126..14a6b1a 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -362,4 +362,5 @@
     <string name="veiled_address" msgid="1209670815434513191"></string>
     <string name="veiled_alternate_text" msgid="7370933826442034211"></string>
     <string name="veiled_alternate_text_unknown_person" msgid="5132515097905273819"></string>
+    <string name="veiled_summary_unknown_person" msgid="4030928895738205054"></string>
 </resources>
diff --git a/res/values/animation_constants.xml b/res/values/animation_constants.xml
index 6daab6f..09c53c5 100644
--- a/res/values/animation_constants.xml
+++ b/res/values/animation_constants.xml
@@ -38,5 +38,6 @@
     <dimen name="min_swipe">10dip</dimen>
     <dimen name="min_vert">10dip</dimen>
     <dimen name="min_lock">20dip</dimen>
-    <integer name="dismiss_all_leavebehinds_delay">350</integer>
+    <integer name="dismiss_all_leavebehinds_short_delay">175</integer>
+    <integer name="dismiss_all_leavebehinds_long_delay">350</integer>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 6b39b80..b615b17 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -77,4 +77,8 @@
     <color name="animating_item_background_color">#7F0099cc</color>
 
     <color name="notification_template_icon_low_bg">#0cffffff</color>
+
+    <color name="letter_tile_color">#d8d8d8</color>
+    <color name="letter_tile_font_color">#ffffff</color>
+    <color name="tile_divider_color">#ffffff</color>
 </resources>
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index e06b669..7bbf99f 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -49,7 +49,7 @@
     <dimen name="move_slop">4dip</dimen>
     <dimen name="standard_scaled_dimen">100sp</dimen>
     <dimen name="folder_cell_width">8dip</dimen>
-    <dimen name="folders_left_padding">16dip</dimen>
+    <dimen name="folders_left_padding">4dip</dimen>
     <dimen name="triangle_width">15dip</dimen>
     <dimen name="conversation_page_gutter">16dp</dimen>
     <dimen name="conversation_message_content_margin_side">16dp</dimen>
@@ -112,7 +112,8 @@
     <dimen name="senders_textview_top_padding">-4dp</dimen>
     <dimen name="senders_textview_height">30dp</dimen>
     <integer name="chips_max_lines">2</integer>
-    <dimen name="tile_letter_font_size">32sp</dimen>
-    <dimen name="tile_letter_padding_bottom">12dp</dimen>
-    <dimen name="tile_letter_padding_left">13dp</dimen>
+    <dimen name="tile_letter_font_size">33dp</dimen>
+    <dimen name="tile_letter_font_size_med">16dp</dimen>
+    <dimen name="tile_letter_font_size_small">16dp</dimen>
+    <dimen name="tile_divider_width">1dp</dimen>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 648f6fe..8b50f2f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -550,6 +550,15 @@
         <item name="android:textSize">@dimen/senders_font_size</item>
     </style>
 
+    <style name="NotificationSendersUnreadTextAppearance">
+        <item name="android:textSize">@dimen/senders_font_size</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="NotificationSendersReadTextAppearance">
+        <item name="android:textSize">@dimen/senders_font_size</item>
+    </style>
+
     <style name="MessageInfoUnreadTextAppearance">
         <item name="android:textSize">@dimen/senders_font_size</item>
         <item name="android:textColor">@color/message_info_text_color</item>
diff --git a/src/com/android/mail/browse/AttachmentActionHandler.java b/src/com/android/mail/browse/AttachmentActionHandler.java
index 5f89272..7c24dda 100644
--- a/src/com/android/mail/browse/AttachmentActionHandler.java
+++ b/src/com/android/mail/browse/AttachmentActionHandler.java
@@ -84,32 +84,25 @@
         }
     }
 
-    public void showAndDownloadAttachments() {
-        final List<Attachment> attachments = mView.getAttachments();
-
-        for (final Attachment attachment : attachments) {
-            if (!attachment.isPresentLocally()) {
-                startDownloadingAttachment(attachment, AttachmentDestination.CACHE,
-                        UIProvider.AttachmentRendition.BEST);
-            }
-        }
-
-        mView.viewAttachment();
-    }
-
     public void startDownloadingAttachment(int destination) {
-        startDownloadingAttachment(destination, UIProvider.AttachmentRendition.BEST);
+        startDownloadingAttachment(destination, UIProvider.AttachmentRendition.BEST, 0, false);
     }
 
-    public void startDownloadingAttachment(int destination, int rendition) {
-        startDownloadingAttachment(mAttachment, destination, rendition);
+    public void startDownloadingAttachment(
+            int destination, int rendition, int additionalPriority, boolean delayDownload) {
+        startDownloadingAttachment(
+                mAttachment, destination, rendition, additionalPriority, delayDownload);
     }
 
-    private void startDownloadingAttachment(Attachment attachment, int destination, int rendition) {
+    private void startDownloadingAttachment(
+            Attachment attachment, int destination, int rendition, int additionalPriority,
+            boolean delayDownload) {
         final ContentValues params = new ContentValues(2);
         params.put(AttachmentColumns.STATE, AttachmentState.DOWNLOADING);
         params.put(AttachmentColumns.DESTINATION, destination);
         params.put(AttachmentContentValueKeys.RENDITION, rendition);
+        params.put(AttachmentContentValueKeys.ADDITIONAL_PRIORITY, additionalPriority);
+        params.put(AttachmentContentValueKeys.DELAY_DOWNLOAD, delayDownload);
 
         mCommandHandler.sendCommand(attachment.uri, params);
     }
diff --git a/src/com/android/mail/browse/AttachmentViewInterface.java b/src/com/android/mail/browse/AttachmentViewInterface.java
index bf15ba6..d56bdf4 100644
--- a/src/com/android/mail/browse/AttachmentViewInterface.java
+++ b/src/com/android/mail/browse/AttachmentViewInterface.java
@@ -42,9 +42,4 @@
      * Called in {@link AttachmentActionHandler#updateStatus}.
      */
     public void onUpdateStatus();
-
-    /**
-     * Returns the list of attachments of which this view is a part.
-     */
-    public List<Attachment> getAttachments();
 }
diff --git a/src/com/android/mail/browse/ConversationItemView.java b/src/com/android/mail/browse/ConversationItemView.java
index 3687606..71dceda 100644
--- a/src/com/android/mail/browse/ConversationItemView.java
+++ b/src/com/android/mail/browse/ConversationItemView.java
@@ -63,6 +63,7 @@
 import com.android.mail.perf.Timer;
 import com.android.mail.photomanager.ContactPhotoManager;
 import com.android.mail.photomanager.LetterTileProvider;
+import com.android.mail.preferences.MailPrefs;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
@@ -73,6 +74,7 @@
 
 import com.android.mail.ui.DividedImageCanvas;
 import com.android.mail.ui.DividedImageCanvas.InvalidateCallback;
+import com.android.mail.ui.EllipsizedMultilineTextView;
 import com.android.mail.ui.FolderDisplayer;
 import com.android.mail.ui.SwipeableItemView;
 import com.android.mail.ui.SwipeableListView;
@@ -171,10 +173,11 @@
     private String mAccount;
     private ControllableActivity mActivity;
     private int mBackgroundOverride = -1;
-    private TextView mSubjectTextView;
+    private EllipsizedMultilineTextView mSubjectTextView;
     private TextView mSendersTextView;
     private TextView mDateTextView;
     private DividedImageCanvas mContactImagesHolder;
+    private boolean mConvListPhotosEnabled;
     private static int sFoldersLeftPadding;
     private static TextAppearanceSpan sDateTextAppearance;
     private static TextAppearanceSpan sSubjectTextUnreadSpan;
@@ -403,9 +406,10 @@
             sContactPhotoManager = ContactPhotoManager.createContactPhotoManager(context);
         }
 
-        mSubjectTextView = new TextView(mContext);
+        mSubjectTextView = new EllipsizedMultilineTextView(mContext);
         mSubjectTextView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT));
+        mSubjectTextView.setMaxLines(2);
         mSendersTextView = new TextView(mContext);
         mSendersTextView.setMaxLines(1);
         mSendersTextView.setEllipsize(TextUtils.TruncateAt.END);
@@ -453,6 +457,8 @@
         mSelectedConversationSet = set;
         mDisplayedFolder = folder;
         mCheckboxesEnabled = !checkboxesDisabled;
+        mConvListPhotosEnabled = MailPrefs.get(activity.getActivityContext())
+                .areConvListPhotosEnabled();
         mStarEnabled = folder != null && !folder.isTrash();
         mSwipeEnabled = swipeEnabled;
         mPriorityMarkersEnabled = priorityArrowEnabled;
@@ -513,7 +519,7 @@
             ConversationItemViewCoordinates.refreshConversationHeights(mContext);
         }
         mCoordinates = ConversationItemViewCoordinates.forWidth(mContext, mViewWidth, mMode,
-                mHeader.standardScaledDimen, mCheckboxesEnabled);
+                mHeader.standardScaledDimen, mConvListPhotosEnabled);
         calculateTextsAndBitmaps();
         calculateCoordinates();
 
@@ -567,7 +573,7 @@
         if (mSelectedConversationSet != null) {
             mChecked = mSelectedConversationSet.contains(mHeader.conversation);
         }
-        mHeader.checkboxVisible = mCheckboxesEnabled;
+        mHeader.checkboxVisible = mCheckboxesEnabled && !mConvListPhotosEnabled;
 
         final boolean isUnread = mHeader.unread;
         updateBackground(isUnread);
@@ -645,7 +651,7 @@
     }
 
     private void loadSenderImages() {
-        if (!mCheckboxesEnabled && mHeader.displayableSenderEmails != null
+        if (mConvListPhotosEnabled && mHeader.displayableSenderEmails != null
                 && mHeader.displayableSenderEmails.size() > 0) {
             mContactImagesHolder.setDimensions(mCoordinates.contactImagesWidth,
                     mCoordinates.contactImagesHeight);
@@ -679,56 +685,57 @@
         }
     }
 
-    private void createSubject(boolean isUnread, boolean activated) {
-        String subject = filterTag(mHeader.conversation.subject);
+    private void createSubject(final boolean isUnread, boolean activated) {
+        final String subject = filterTag(mHeader.conversation.subject);
         final String snippet = mHeader.conversation.getSnippet();
-        int maxWidth = -1;
+
+        SpannableStringBuilder subjectText = new SpannableStringBuilder(
+                Conversation.getSubjectAndSnippetForDisplay(mContext, subject, snippet));
+        int subjectTextLength = Math.min(subjectText.length(), subject.length());
+             if (!TextUtils.isEmpty(subject)) {
+                 subjectText.setSpan(TextAppearanceSpan.wrap(isUnread ?
+                         sSubjectTextUnreadSpan : sSubjectTextReadSpan), 0, subjectTextLength,
+                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+             }
+             if (!TextUtils.isEmpty(snippet)) {
+                 final int startOffset = subjectTextLength;
+                 // Start after the end of the subject text; since the subject may be
+                 // "" or null, this could start at the 0th character in the
+                 // subectText string
+                 if (startOffset < subjectText.length()) {
+                     subjectText.setSpan(ForegroundColorSpan.wrap(isUnread ?
+                             sSnippetTextUnreadSpan : sSnippetTextReadSpan), startOffset,
+                             subjectText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                 }
+             }
+             layoutSubject(subjectText);
+    }
+
+
+    private void layoutSubject(SpannableStringBuilder subjectText) {
+        int secondLineMaxWidth = EllipsizedMultilineTextView.ALL_AVAILABLE;
         if (!ConversationItemViewCoordinates.isWideMode(mMode) && mCoordinates.showFolders
                 && mHeader.folderDisplayer != null
                 && mHeader.folderDisplayer.hasVisibleFolders()) {
-            sPaint.setTextSize(mHeader.unread ? sSubjectTextUnreadSpan.getTextSize()
-                    : sSubjectTextReadSpan.getTextSize());
-            sPaint.setTypeface(mHeader.unread ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
-            maxWidth = (mSendersWidth * 2)
-                        - ConversationItemViewCoordinates.getFoldersWidth(mContext, mMode)
-                        - sFoldersLeftPadding;
+            secondLineMaxWidth = mCoordinates.subjectWidth
+                    - Math.min(ConversationItemViewCoordinates.getFoldersWidth(mContext, mMode),
+                            mHeader.folderDisplayer.measureFolders(mMode))
+                    - sFoldersLeftPadding;
         }
-        SpannableStringBuilder subjectText = Conversation.getSubjectAndSnippetForDisplay(mContext,
-                subject, snippet, maxWidth, sPaint);
-        int subjectTextLength = Math.min(subjectText.length(), subject.length());
-        if (!TextUtils.isEmpty(subject)) {
-            subjectText.setSpan(TextAppearanceSpan.wrap(isUnread ?
-                    sSubjectTextUnreadSpan : sSubjectTextReadSpan), 0, subjectTextLength,
-                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        }
-        if (!TextUtils.isEmpty(snippet)) {
-            final int startOffset = subjectTextLength;
-            // Start after the end of the subject text; since the subject may be
-            // "" or null, this could start at the 0th character in the
-            // subectText string
-            if (startOffset < subjectText.length()) {
-                subjectText.setSpan(ForegroundColorSpan.wrap(isUnread ?
-                        sSnippetTextUnreadSpan : sSnippetTextReadSpan), startOffset,
-                        subjectText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-        }
-        layoutSubject(subjectText);
-    }
-
-    private void layoutSubject(SpannableStringBuilder subjectText) {
-        TextView subjectLayout = mSubjectTextView;
+        EllipsizedMultilineTextView subjectLayout = mSubjectTextView;
         int subjectWidth = mCoordinates.subjectWidth;
-        int subjectHeight = (int) (subjectLayout.getLineHeight() * 2 + sPaint.descent());
+        int subjectHeight = (int) (subjectLayout.getLineHeight() * 2 + subjectLayout.getPaint()
+                .descent());
         if (isActivated() && showActivatedText()) {
             subjectText.setSpan(sActivatedTextSpan, 0, subjectText.length(),
                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         } else {
             subjectText.removeSpan(sActivatedTextSpan);
         }
-        subjectLayout.setText(subjectText, TextView.BufferType.SPANNABLE);
         subjectLayout.measure(MeasureSpec.makeMeasureSpec(subjectWidth, MeasureSpec.EXACTLY),
                 subjectHeight);
         subjectLayout.layout(0, 0, subjectWidth, subjectHeight);
+        subjectLayout.setText(subjectText, secondLineMaxWidth);
     }
 
     /**
@@ -1020,13 +1027,13 @@
     @Override
     protected void onDraw(Canvas canvas) {
         // Check mark.
-        if (mHeader.checkboxVisible) {
-            Bitmap checkmark = mChecked ? CHECKMARK_ON : CHECKMARK_OFF;
-            canvas.drawBitmap(checkmark, mCoordinates.checkmarkX, mCoordinates.checkmarkY, sPaint);
-        } else {
+        if (mConvListPhotosEnabled) {
             canvas.save();
             drawContactImages(canvas);
             canvas.restore();
+        } else if (mHeader.checkboxVisible) {
+            Bitmap checkmark = mChecked ? CHECKMARK_ON : CHECKMARK_OFF;
+            canvas.drawBitmap(checkmark, mCoordinates.checkmarkX, mCoordinates.checkmarkY, sPaint);
         }
 
         // Personal Level.
@@ -1141,7 +1148,7 @@
 
     private void drawSenders(Canvas canvas) {
         int left;
-        if (!mCheckboxesEnabled && mCoordinates.inlinePersonalLevel) {
+        if (mConvListPhotosEnabled && mCoordinates.inlinePersonalLevel) {
             if (mCoordinates.showPersonalLevel && mHeader.personalLevelBitmap != null) {
                 left = mCoordinates.sendersX;
             } else {
diff --git a/src/com/android/mail/browse/ConversationItemViewCoordinates.java b/src/com/android/mail/browse/ConversationItemViewCoordinates.java
index aca8f43..dfce5d8 100644
--- a/src/com/android/mail/browse/ConversationItemViewCoordinates.java
+++ b/src/com/android/mail/browse/ConversationItemViewCoordinates.java
@@ -168,8 +168,8 @@
     /**
      * Returns the layout id to be inflated in this mode.
      */
-    private static int getLayoutId(int mode, boolean checkboxesEnabled) {
-        if (checkboxesEnabled) {
+    private static int getLayoutId(int mode, boolean convListPhotosEnabled) {
+        if (!convListPhotosEnabled) {
             switch (mode) {
                 case WIDE_MODE:
                     return R.layout.conversation_item_view_wide;
@@ -339,17 +339,17 @@
      * the view width.
      */
     public static ConversationItemViewCoordinates forWidth(Context context, int width, int mode,
-            int standardScaledDimen, boolean checkboxesEnabled) {
+            int standardScaledDimen, boolean convListPhotosEnabled) {
         ConversationItemViewCoordinates coordinates = sCache.get(Objects.hashCode(width, mode,
-                checkboxesEnabled));
+                convListPhotosEnabled));
         if (coordinates == null) {
             coordinates = new ConversationItemViewCoordinates();
-            sCache.put(Objects.hashCode(width, mode, checkboxesEnabled), coordinates);
+            sCache.put(Objects.hashCode(width, mode, convListPhotosEnabled), coordinates);
 
             // Layout the appropriate view.
             int height = getHeight(context, mode);
-            View view = LayoutInflater.from(context).inflate(getLayoutId(mode, checkboxesEnabled),
-                    null);
+            View view = LayoutInflater.from(context).inflate(
+                    getLayoutId(mode, convListPhotosEnabled), null);
             int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
             int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
             Resources res = context.getResources();
@@ -442,7 +442,7 @@
             coordinates.paperclipY = getY(paperclip);
 
             // Contact images view
-            if (!checkboxesEnabled) {
+            if (convListPhotosEnabled) {
             View contactImagesView = view.findViewById(R.id.contact_image);
                 if (contactImagesView != null) {
                     coordinates.contactImagesWidth = contactImagesView.getWidth();
diff --git a/src/com/android/mail/browse/ConversationWebView.java b/src/com/android/mail/browse/ConversationWebView.java
index f17ca3c..0f9d855 100644
--- a/src/com/android/mail/browse/ConversationWebView.java
+++ b/src/com/android/mail/browse/ConversationWebView.java
@@ -162,6 +162,7 @@
      * POINTER_UP/POINTER_CANCEL.
      */
     private boolean mHandlingTouch;
+    private boolean mIgnoringTouch;
 
     private static final String LOG_TAG = LogTag.getLogTag();
 
@@ -263,14 +264,21 @@
             case MotionEvent.ACTION_POINTER_DOWN:
                 LogUtils.d(LOG_TAG, "WebView disabling intercepts: POINTER_DOWN");
                 requestDisallowInterceptTouchEvent(true);
+                if (mScaleDetector != null) {
+                    mIgnoringTouch = true;
+                    final MotionEvent fakeCancel = MotionEvent.obtain(ev);
+                    fakeCancel.setAction(MotionEvent.ACTION_CANCEL);
+                    super.onTouchEvent(fakeCancel);
+                }
                 break;
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 mHandlingTouch = false;
+                mIgnoringTouch = false;
                 break;
         }
 
-        final boolean handled = super.onTouchEvent(ev);
+        final boolean handled = mIgnoringTouch || super.onTouchEvent(ev);
 
         if (mScaleDetector != null) {
             mScaleDetector.onTouchEvent(ev);
diff --git a/src/com/android/mail/browse/MessageAttachmentBar.java b/src/com/android/mail/browse/MessageAttachmentBar.java
index da0e9bc..628d867 100644
--- a/src/com/android/mail/browse/MessageAttachmentBar.java
+++ b/src/com/android/mail/browse/MessageAttachmentBar.java
@@ -350,9 +350,4 @@
         }
         mSubTitle.setText(sb.toString());
     }
-
-    @Override
-    public List<Attachment> getAttachments() {
-        return null;
-    }
 }
diff --git a/src/com/android/mail/browse/MessageAttachmentTile.java b/src/com/android/mail/browse/MessageAttachmentTile.java
index d6f19c3..7d204bd 100644
--- a/src/com/android/mail/browse/MessageAttachmentTile.java
+++ b/src/com/android/mail/browse/MessageAttachmentTile.java
@@ -28,7 +28,7 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.ProgressBar;
+import android.view.ViewParent;
 
 import com.android.ex.photo.Intents;
 import com.android.ex.photo.Intents.PhotoViewIntentBuilder;
@@ -45,7 +45,8 @@
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
 
-import java.util.List;
+import java.util.Comparator;
+import java.util.PriorityQueue;
 
 /**
  * View for a single attachment in conversation view. Shows download status and allows launching
@@ -60,7 +61,6 @@
     private View mTextContainer;
 
     private final AttachmentActionHandler mActionHandler;
-    private ProgressBar mProgress;
 
     private static final String LOG_TAG = LogTag.getLogTag();
 
@@ -107,7 +107,6 @@
         super.onFinishInflate();
 
         mTextContainer = findViewById(R.id.attachment_tile_text_container);
-        mProgress = (ProgressBar) findViewById(R.id.attachment_progress);
 
         setOnClickListener(this);
     }
@@ -118,11 +117,39 @@
     }
 
     private boolean onClick(int res, View v) {
-        mActionHandler.showAndDownloadAttachments();
-
+        showAndDownloadAttachments();
         return true;
     }
 
+    private void showAndDownloadAttachments() {
+        AttachmentTileGrid tileGrid = ((AttachmentTileGrid) getParent());
+        int childCount = tileGrid.getChildCount();
+
+        PriorityQueue<MessageAttachmentTile> queue = new PriorityQueue<MessageAttachmentTile>(
+                childCount, new ViewIndexDistanceComparator(mPhotoIndex));
+        for (int i = 0; i < childCount; i++) {
+            MessageAttachmentTile tile = (MessageAttachmentTile) tileGrid.getChildAt(i);
+            queue.add(tile);
+        }
+
+        // we want our downloads to have higher priority than the highest background downloads
+        int maxAdditionalPriority = childCount;
+        for (int i = 0; i < childCount; i++) {
+            // higher priority tiles are returned first
+            MessageAttachmentTile tile = queue.remove();
+            tile.downloadAttachment(maxAdditionalPriority - i, i != 0);
+        }
+
+        viewAttachment();
+    }
+
+    public void downloadAttachment(int additionalPriority, boolean delayDownload) {
+        if (!mAttachment.isPresentLocally()) {
+            mActionHandler.startDownloadingAttachment(AttachmentDestination.CACHE,
+                    UIProvider.AttachmentRendition.BEST, additionalPriority, delayDownload);
+        }
+    }
+
     @Override
     public void viewAttachment() {
         if (ImageUtils.isImageMimeType(Utils.normalizeMimeType(mAttachment.contentType))) {
@@ -152,14 +179,7 @@
 
     @Override
     public void updateProgress(boolean showDeterminateProgress) {
-        if (mAttachment.isDownloading()) {
-            mProgress.setMax(mAttachment.size);
-            mProgress.setProgress(mAttachment.downloadedSize);
-            mProgress.setIndeterminate(!showDeterminateProgress);
-            mProgress.setVisibility(VISIBLE);
-        } else {
-            mProgress.setVisibility(GONE);
-        }
+        // do not show progress for image tiles
     }
 
     @Override
@@ -179,14 +199,45 @@
     }
 
     @Override
-    public List<Attachment> getAttachments() {
-        return ((AttachmentTileGrid) getParent()).getAttachments();
-    }
-
-    @Override
     public void thumbnailLoadFailed() {
         super.thumbnailLoadFailed();
         mActionHandler.startDownloadingAttachment(
-                AttachmentDestination.CACHE, AttachmentRendition.SIMPLE);
+                AttachmentDestination.CACHE, AttachmentRendition.SIMPLE, 0, false);
+    }
+
+    /**
+     * Given two child views, figure out whose index is closest to the specified
+     * index. (generated by markwei)
+     */
+    public static class ViewIndexDistanceComparator implements Comparator<View>{
+        final private int mIndex;
+        /**
+         * @param index Compare based on each view's distance to this index
+         */
+        public ViewIndexDistanceComparator(int index) {
+            mIndex = index;
+        }
+
+        @Override
+        public int compare(View lhs, View rhs) {
+            ViewParent parent = lhs.getParent();
+            if (parent == rhs.getParent()) {
+                if (parent instanceof ViewGroup) {
+                    ViewGroup p = (ViewGroup) parent;
+                    int lhsIndex = p.indexOfChild(lhs);
+                    int rhsIndex = p.indexOfChild(rhs);
+                    int lhsDistance = Math.abs(mIndex - lhsIndex);
+                    int rhsDistance = Math.abs(mIndex - rhsIndex);
+                    // prefer shorter distance since they are the next ones to be swiped to
+                    int result = lhsDistance - rhsDistance;
+                    if (result == 0) {
+                        // prefer higher index since they are to the right in the photoviewer
+                        return rhsIndex - lhsIndex;
+                    }
+                    return result;
+                }
+            }
+            return 0;
+        }
     }
 }
diff --git a/src/com/android/mail/browse/SendersView.java b/src/com/android/mail/browse/SendersView.java
index 8a1f4c0..b6870a9 100644
--- a/src/com/android/mail/browse/SendersView.java
+++ b/src/com/android/mail/browse/SendersView.java
@@ -194,14 +194,27 @@
             ArrayList<String> displayableSenderNames, ArrayList<String> displayableSenderEmails,
             String account) {
         getSenderResources(context);
+        format(context, conversationInfo, messageInfo, maxChars, styledSenders,
+                displayableSenderNames, displayableSenderEmails, account,
+                sUnreadStyleSpan, sReadStyleSpan);
+    }
+
+    public static void format(Context context, ConversationInfo conversationInfo,
+            String messageInfo, int maxChars, ArrayList<SpannableString> styledSenders,
+            ArrayList<String> displayableSenderNames, ArrayList<String> displayableSenderEmails,
+            String account, final TextAppearanceSpan notificationUnreadStyleSpan,
+            final CharacterStyle notificationReadStyleSpan) {
+        getSenderResources(context);
         handlePriority(context, maxChars, messageInfo, conversationInfo, styledSenders,
-                displayableSenderNames, displayableSenderEmails, account);
+                displayableSenderNames, displayableSenderEmails, account,
+                notificationUnreadStyleSpan, notificationReadStyleSpan);
     }
 
     public static void handlePriority(Context context, int maxChars, String messageInfoString,
             ConversationInfo conversationInfo, ArrayList<SpannableString> styledSenders,
             ArrayList<String> displayableSenderNames, ArrayList<String> displayableSenderEmails,
-            String account) {
+            String account, final TextAppearanceSpan unreadStyleSpan,
+            final CharacterStyle readStyleSpan) {
         boolean shouldAddPhotos = displayableSenderEmails != null;
         int maxPriorityToInclude = -1; // inclusive
         int numCharsUsed = messageInfoString.length(); // draft, number drafts,
@@ -263,7 +276,8 @@
                         Math.max(nameString.length() - numCharsToRemovePerWord, 0));
             }
             final int priority = currentMessage.priority;
-            style = !currentMessage.read ? getUnreadStyleSpan() : getReadStyleSpan();
+            style = !currentMessage.read ? getWrappedStyleSpan(unreadStyleSpan)
+                    : getWrappedStyleSpan(readStyleSpan);
             if (priority <= maxPriorityToInclude) {
                 spannableDisplay = new SpannableString(nameString);
                 // Don't duplicate senders; leave the first instance, unless the
@@ -333,12 +347,8 @@
         }
     }
 
-    private static CharacterStyle getUnreadStyleSpan() {
-        return CharacterStyle.wrap(sUnreadStyleSpan);
-    }
-
-    private static CharacterStyle getReadStyleSpan() {
-        return CharacterStyle.wrap(sReadStyleSpan);
+    private static CharacterStyle getWrappedStyleSpan(final CharacterStyle characterStyle) {
+        return CharacterStyle.wrap(characterStyle);
     }
 
     static String getMe(Context context) {
@@ -353,7 +363,7 @@
     }
 
     private static void formatDefault(ConversationItemViewModel header, String sendersString,
-            Context context) {
+            Context context, final CharacterStyle readStyleSpan) {
         getSenderResources(context);
         // Clear any existing sender fragments; we must re-make all of them.
         header.senderFragments.clear();
@@ -371,15 +381,23 @@
                 namesOnly[i] = display;
             }
         }
-        generateSenderFragments(header, namesOnly);
+        generateSenderFragments(header, namesOnly, readStyleSpan);
     }
 
-    private static void generateSenderFragments(ConversationItemViewModel header, String[] names) {
+    private static void generateSenderFragments(ConversationItemViewModel header, String[] names,
+            final CharacterStyle readStyleSpan) {
         header.sendersText = TextUtils.join(Address.ADDRESS_DELIMETER + " ", names);
-        header.addSenderFragment(0, header.sendersText.length(), getReadStyleSpan(), true);
+        header.addSenderFragment(0, header.sendersText.length(), getWrappedStyleSpan(readStyleSpan),
+                true);
     }
 
     public static void formatSenders(ConversationItemViewModel header, Context context) {
-        formatDefault(header, header.conversation.senders, context);
+        getSenderResources(context);
+        formatSenders(header, context, sReadStyleSpan);
+    }
+
+    public static void formatSenders(ConversationItemViewModel header, Context context,
+            final CharacterStyle readStyleSpan) {
+        formatDefault(header, header.conversation.senders, context, readStyleSpan);
     }
 }
diff --git a/src/com/android/mail/photo/MailPhotoViewActivity.java b/src/com/android/mail/photo/MailPhotoViewActivity.java
index 47fe32d..7d597e1 100644
--- a/src/com/android/mail/photo/MailPhotoViewActivity.java
+++ b/src/com/android/mail/photo/MailPhotoViewActivity.java
@@ -193,21 +193,21 @@
     @Override
     public void onFragmentVisible(PhotoViewFragment fragment) {
         super.onFragmentVisible(fragment);
-
-        final Attachment attachment = getCurrentAttachment();
-        updateProgressAndEmptyViews(fragment, attachment);
     }
 
+    @Override
+    public void onCursorChanged(PhotoViewFragment fragment, Cursor cursor) {
+        super.onCursorChanged(fragment, cursor);
+        updateProgressAndEmptyViews(fragment, new Attachment(cursor));
+    }
 
     /**
      * Updates the empty views of the fragment based upon the current
      * state of the attachment.
      * @param fragment the current fragment
-     * @param attachment the current {@link Attachment}
      */
     private void updateProgressAndEmptyViews(
-            PhotoViewFragment fragment, final Attachment attachment) {
-
+            final PhotoViewFragment fragment, final Attachment attachment) {
         final ProgressBarWrapper progressBar = fragment.getPhotoProgressBar();
         final TextView emptyText = fragment.getEmptyText();
         final ImageView retryButton = fragment.getRetryButton();
@@ -230,7 +230,6 @@
                 @Override
                 public void onClick(View view) {
                     downloadAttachment();
-                    progressBar.setVisibility(View.VISIBLE);
                 }
             });
             progressBar.setVisibility(View.GONE);
@@ -330,7 +329,6 @@
 
     /**
      * Helper method to get the currently visible attachment.
-     * @return
      */
     protected Attachment getCurrentAttachment() {
         final Cursor cursor = getCursorAtProperPosition();
diff --git a/src/com/android/mail/photomanager/LetterTileProvider.java b/src/com/android/mail/photomanager/LetterTileProvider.java
index 5fba23c..415ae9b 100644
--- a/src/com/android/mail/photomanager/LetterTileProvider.java
+++ b/src/com/android/mail/photomanager/LetterTileProvider.java
@@ -25,6 +25,8 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.text.TextPaint;
 import android.text.TextUtils;
@@ -47,19 +49,22 @@
 public class LetterTileProvider extends DefaultImageProvider {
     private Bitmap mDefaultBitmap;
     private final LruCache<Integer, Bitmap> mTileBitmapCache;
-    private static int sTilePaddingBottom;
-    private static int sTilePaddingLeft;
+    private static Typeface sSansSerifLight;
+    private static Rect sBounds;
     private static int sTileLetterFontSize = -1;
+    private static int sTileLetterFontSizeSmall;
+    private static int sTileColor;
+    private static int sTileFontColor;
     private static TextPaint sPaint = new TextPaint();
     private static int DEFAULT_AVATAR_DRAWABLE = R.drawable.ic_contact_picture;
-    private static final Pattern ALPHABET = Pattern.compile("^[a-zA-Z]+$");
+    private static final Pattern ALPHABET = Pattern.compile("^[a-zA-Z0-9]+$");
 
     public LetterTileProvider() {
         super();
         final float cacheSizeAdjustment =
                 (MemoryUtils.getTotalMemorySize() >= MemoryUtils.LARGE_RAM_THRESHOLD) ?
-                1.0f : 0.5f;
-        final int bitmapCacheSize = (int) (cacheSizeAdjustment * 26);
+                        1.0f : 0.5f;
+        final int bitmapCacheSize = (int) (cacheSizeAdjustment * 36);
         mTileBitmapCache = new LruCache<Integer, Bitmap>(bitmapCacheSize);
     }
 
@@ -67,37 +72,38 @@
     public void applyDefaultImage(String displayName, String address, DividedImageCanvas view,
             int extent) {
         Bitmap bitmap = null;
-        // TODO: show just the default gray contact photo for now
-        if (false) {
-            final String display = !TextUtils.isEmpty(displayName) ? displayName : address;
-            final String firstChar = display.substring(0, 1);
-            // If its a valid english alphabet letter...
-            if (isLetter(firstChar)) {
-                final String first = firstChar.toUpperCase();
-                DividedImageCanvas.Dimensions d = view.getDesiredDimensions(address);
-                int hash = Objects.hashCode(first, d);
-                bitmap = mTileBitmapCache.get(hash);
-                if (bitmap == null) {
-                    // Create bitmap based on the first char
-                    bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888);
-                    sPaint.setColor(Color.BLACK);
-                    if (sTileLetterFontSize == -1) {
-                        final Resources res = view.getContext().getResources();
-                        sTileLetterFontSize = res
-                                .getDimensionPixelSize(R.dimen.tile_letter_font_size);
-                        sTilePaddingBottom = res
-                                .getDimensionPixelSize(R.dimen.tile_letter_padding_bottom);
-                        sTilePaddingLeft = res
-                                .getDimensionPixelSize(R.dimen.tile_letter_padding_left);
-                    }
-                    sPaint.setTextSize(sTileLetterFontSize * d.scale);
-                    sPaint.setTypeface(Typeface.DEFAULT);
-                    Canvas c = new Canvas(bitmap);
-                    c.drawColor(Color.GRAY);
-                    c.drawText(first, d.scale * sTilePaddingLeft, d.height
-                            - (sTilePaddingBottom * d.scale), sPaint);
-                    mTileBitmapCache.put(hash, bitmap);
+        final String display = !TextUtils.isEmpty(displayName) ? displayName : address;
+        final String firstChar = display.substring(0, 1);
+        // If its a valid english alphabet letter...
+        if (isLetter(firstChar)) {
+            final String first = firstChar.toUpperCase();
+            DividedImageCanvas.Dimensions d = view.getDesiredDimensions(address);
+            int hash = Objects.hashCode(first, d);
+            bitmap = mTileBitmapCache.get(hash);
+            if (bitmap == null) {
+                // Create bitmap based on the first char
+                bitmap = Bitmap.createBitmap(d.width, d.height, Bitmap.Config.ARGB_8888);
+                sPaint.setColor(Color.BLACK);
+                if (sTileLetterFontSize == -1) {
+                    final Resources res = view.getContext().getResources();
+                    sTileLetterFontSize = res.getDimensionPixelSize(R.dimen.tile_letter_font_size);
+                    sTileLetterFontSizeSmall = res
+                            .getDimensionPixelSize(R.dimen.tile_letter_font_size_small);
+                    sTileColor = res.getColor(R.color.letter_tile_color);
+                    sTileFontColor = res.getColor(R.color.letter_tile_font_color);
+                    sSansSerifLight = Typeface.create("sans-serif-light", Typeface.NORMAL);
+                    sBounds = new Rect();
                 }
+                Canvas c = new Canvas(bitmap);
+                c.drawColor(sTileColor);
+                sPaint.setTextSize(getFontSize(d.scale));
+                sPaint.setTypeface(sSansSerifLight);
+                sPaint.setColor(sTileFontColor);
+                sPaint.setTextAlign(Align.CENTER);
+                sPaint.setAntiAlias(true);
+                sPaint.getTextBounds(first, 0, first.length(), sBounds);
+                c.drawText(first, 0+d.width/2, 0+d.height/2+(sBounds.bottom-sBounds.top)/2, sPaint);
+                mTileBitmapCache.put(hash, bitmap);
             }
         } else {
             if (mDefaultBitmap == null) {
@@ -111,6 +117,14 @@
         view.addDivisionImage(bitmap, address);
     }
 
+    private int getFontSize(float scale)  {
+        if (scale == DividedImageCanvas.ONE) {
+            return sTileLetterFontSize;
+        } else {
+            return sTileLetterFontSizeSmall;
+        }
+    }
+
     private boolean isLetter(String letter) {
         Matcher m = ALPHABET.matcher(letter);
         return m.matches();
diff --git a/src/com/android/mail/preferences/MailPrefs.java b/src/com/android/mail/preferences/MailPrefs.java
index 2f60c4e..1aa8ac0 100644
--- a/src/com/android/mail/preferences/MailPrefs.java
+++ b/src/com/android/mail/preferences/MailPrefs.java
@@ -45,6 +45,7 @@
 
     // Hidden preference to indicate what version a "What's New" dialog was last shown for.
     private static final String WHATS_NEW_LAST_SHOWN_VERSION = "whats-new-last-shown-version";
+    public static final String ENABLE_CONVLIST_PHOTOS = "enable-convlist-photos";
 
     public static MailPrefs get(Context c) {
         if (sInstance == null) {
@@ -61,6 +62,13 @@
         return PREFS_NAME;
     }
 
+    /**
+     * Set the value of a shared preference of type boolean.
+     */
+    public void setSharedBooleanPreference(String pref, boolean value) {
+        mPrefs.edit().putBoolean(pref, value).apply();
+    }
+
     public boolean isWidgetConfigured(int appWidgetId) {
         return mPrefs.contains(WIDGET_ACCOUNT_PREFIX + appWidgetId);
     }
@@ -76,6 +84,14 @@
         return mPrefs.getString(WIDGET_ACCOUNT_PREFIX + appWidgetId, null);
     }
 
+    /**
+     * Get whether to show the experimental inline contact photos in the
+     * conversation list.
+     */
+    public boolean areConvListPhotosEnabled() {
+        return mPrefs.getBoolean(ENABLE_CONVLIST_PHOTOS, false);
+    }
+
     private static String createWidgetPreferenceValue(Account account, Folder folder) {
         return account.uri.toString() +
                 ACCOUNT_FOLDER_PREFERENCE_SEPARATOR + folder.uri.toString();
diff --git a/src/com/android/mail/providers/Attachment.java b/src/com/android/mail/providers/Attachment.java
index abd6347..707619a 100644
--- a/src/com/android/mail/providers/Attachment.java
+++ b/src/com/android/mail/providers/Attachment.java
@@ -269,7 +269,7 @@
 
     public boolean shouldShowProgress() {
         return state == AttachmentState.DOWNLOADING
-                && size > 0 && downloadedSize > 0 && downloadedSize < size;
+                && size > 0 && downloadedSize > 0 && downloadedSize <= size;
     }
 
     public boolean isDownloadFailed() {
diff --git a/src/com/android/mail/providers/Conversation.java b/src/com/android/mail/providers/Conversation.java
index 1554927..27121d8 100644
--- a/src/com/android/mail/providers/Conversation.java
+++ b/src/com/android/mail/providers/Conversation.java
@@ -23,9 +23,7 @@
 import android.os.Parcelable;
 import android.provider.BaseColumns;
 import android.text.SpannableStringBuilder;
-import android.text.TextPaint;
 import android.text.TextUtils;
-import android.text.TextUtils.TruncateAt;
 
 import com.android.mail.R;
 import com.android.mail.providers.UIProvider.ConversationColumns;
@@ -531,27 +529,14 @@
      * @param context
      * @param filteredSubject
      * @param snippet
-     * @param maxChars Supply max characters the returned string can have, or -1
-     *            if there is no limit
      */
-    public static SpannableStringBuilder getSubjectAndSnippetForDisplay(Context context,
-            String filteredSubject, String snippet, int avail, TextPaint paint) {
+    public static String getSubjectAndSnippetForDisplay(Context context,
+            String filteredSubject, String snippet) {
         if (sSubjectAndSnippet == null) {
             sSubjectAndSnippet = context.getString(R.string.subject_and_snippet);
         }
-        String subjectText = (!TextUtils.isEmpty(snippet)) ?
+        return (!TextUtils.isEmpty(snippet)) ?
                 String.format(sSubjectAndSnippet, filteredSubject, snippet)
                 : filteredSubject;
-        SpannableStringBuilder builder = new SpannableStringBuilder();
-        if (avail != -1) {
-            CharSequence ellipsizedText = TextUtils.ellipsize(subjectText, paint, avail,
-                    TruncateAt.END);
-            if (!TextUtils.isEmpty(ellipsizedText)) {
-                builder.append(ellipsizedText, 0, ellipsizedText.length() - 1);
-            }
-        } else {
-            builder.append(subjectText);
-        }
-        return builder;
     }
 }
diff --git a/src/com/android/mail/providers/MailAppProvider.java b/src/com/android/mail/providers/MailAppProvider.java
index 798c446..587170d 100644
--- a/src/com/android/mail/providers/MailAppProvider.java
+++ b/src/com/android/mail/providers/MailAppProvider.java
@@ -295,8 +295,8 @@
                     case UIProvider.ACCOUNT_SETTINGS_REPLY_BEHAVIOR_COLUMN:
                         builder.add(Integer.valueOf(account.settings.replyBehavior));
                         break;
-                    case UIProvider.ACCOUNT_SETTINGS_SHOW_CHECKBOXES_COLUMN:
-                        builder.add(Integer.valueOf(account.settings.showCheckboxes ? 1 : 0));
+                    case UIProvider.ACCOUNT_SETTINGS_HIDE_CHECKBOXES_COLUMN:
+                        builder.add(Integer.valueOf(account.settings.hideCheckboxes ? 1 : 0));
                         break;
                     case UIProvider.ACCOUNT_SETTINGS_CONFIRM_DELETE_COLUMN:
                         builder.add(Integer.valueOf(account.settings.confirmDelete ? 1 : 0));
diff --git a/src/com/android/mail/providers/Settings.java b/src/com/android/mail/providers/Settings.java
index d37bab1..b77a19d 100644
--- a/src/com/android/mail/providers/Settings.java
+++ b/src/com/android/mail/providers/Settings.java
@@ -66,7 +66,7 @@
     public final int messageTextSize;
     public final int snapHeaders;
     public final int replyBehavior;
-    public final boolean showCheckboxes;
+    public final boolean hideCheckboxes;
     public final boolean confirmDelete;
     public final boolean confirmArchive;
     public final boolean confirmSend;
@@ -101,7 +101,7 @@
         messageTextSize = MessageTextSize.NORMAL;
         snapHeaders = SnapHeaderValue.ALWAYS;
         replyBehavior = DefaultReplyBehavior.REPLY;
-        showCheckboxes = false;
+        hideCheckboxes = false;
         confirmDelete = false;
         confirmArchive = false;
         confirmSend = false;
@@ -122,7 +122,7 @@
         messageTextSize = inParcel.readInt();
         snapHeaders = inParcel.readInt();
         replyBehavior = inParcel.readInt();
-        showCheckboxes = inParcel.readInt() != 0;
+        hideCheckboxes = inParcel.readInt() != 0;
         confirmDelete = inParcel.readInt() != 0;
         confirmArchive = inParcel.readInt() != 0;
         confirmSend = inParcel.readInt() != 0;
@@ -143,7 +143,7 @@
         messageTextSize = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_MESSAGE_TEXT_SIZE_COLUMN);
         snapHeaders = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_SNAP_HEADERS_COLUMN);
         replyBehavior = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_REPLY_BEHAVIOR_COLUMN);
-        showCheckboxes = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_SHOW_CHECKBOXES_COLUMN) != 0;
+        hideCheckboxes = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_HIDE_CHECKBOXES_COLUMN) != 0;
         confirmDelete = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_CONFIRM_DELETE_COLUMN) != 0;
         confirmArchive = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_CONFIRM_ARCHIVE_COLUMN) != 0;
         confirmSend = cursor.getInt(UIProvider.ACCOUNT_SETTINGS_CONFIRM_SEND_COLUMN) != 0;
@@ -173,8 +173,8 @@
                 sDefault.snapHeaders);
         replyBehavior = json.optInt(AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
                 sDefault.replyBehavior);
-        showCheckboxes = json.optBoolean(AccountColumns.SettingsColumns.SHOW_CHECKBOXES,
-                sDefault.showCheckboxes);
+        hideCheckboxes = json.optBoolean(AccountColumns.SettingsColumns.HIDE_CHECKBOXES,
+                sDefault.hideCheckboxes);
         confirmDelete = json.optBoolean(AccountColumns.SettingsColumns.CONFIRM_DELETE,
                 sDefault.confirmDelete);
         confirmArchive = json.optBoolean(AccountColumns.SettingsColumns.CONFIRM_ARCHIVE,
@@ -228,7 +228,7 @@
             json.put(AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE, messageTextSize);
             json.put(AccountColumns.SettingsColumns.SNAP_HEADERS, snapHeaders);
             json.put(AccountColumns.SettingsColumns.REPLY_BEHAVIOR, replyBehavior);
-            json.put(AccountColumns.SettingsColumns.SHOW_CHECKBOXES, showCheckboxes);
+            json.put(AccountColumns.SettingsColumns.HIDE_CHECKBOXES, hideCheckboxes);
             json.put(AccountColumns.SettingsColumns.CONFIRM_DELETE, confirmDelete);
             json.put(AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, confirmArchive);
             json.put(AccountColumns.SettingsColumns.CONFIRM_SEND, confirmSend);
@@ -298,7 +298,7 @@
         dest.writeInt(messageTextSize);
         dest.writeInt(snapHeaders);
         dest.writeInt(replyBehavior);
-        dest.writeInt(showCheckboxes ? 1 : 0);
+        dest.writeInt(hideCheckboxes ? 1 : 0);
         dest.writeInt(confirmDelete ? 1 : 0);
         dest.writeInt(confirmArchive? 1 : 0);
         dest.writeInt(confirmSend? 1 : 0);
@@ -391,7 +391,7 @@
                 && messageTextSize == that.messageTextSize
                 && snapHeaders == that.snapHeaders
                 && replyBehavior == that.replyBehavior
-                && showCheckboxes == that.showCheckboxes
+                && hideCheckboxes == that.hideCheckboxes
                 && confirmDelete == that.confirmDelete
                 && confirmArchive == that.confirmArchive
                 && confirmSend == that.confirmSend
@@ -420,7 +420,7 @@
     private final int calculateHashCode() {
         return super.hashCode()
                 ^ Objects.hashCode(signature, mAutoAdvance, mTransientAutoAdvance, messageTextSize,
-                        snapHeaders, replyBehavior, showCheckboxes, confirmDelete, confirmArchive,
+                        snapHeaders, replyBehavior, hideCheckboxes, confirmDelete, confirmArchive,
                         confirmSend, defaultInbox, forceReplyFromDefault, maxAttachmentSize, swipe,
                         priorityArrowsEnabled, setupIntentUri, conversationViewMode,
                         veiledAddressPattern);
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index 5e6b189..25da659 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -157,7 +157,7 @@
             .put(AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE, Integer.class)
             .put(AccountColumns.SettingsColumns.SNAP_HEADERS, Integer.class)
             .put(AccountColumns.SettingsColumns.REPLY_BEHAVIOR, Integer.class)
-            .put(AccountColumns.SettingsColumns.SHOW_CHECKBOXES, Integer.class)
+            .put(AccountColumns.SettingsColumns.HIDE_CHECKBOXES, Integer.class)
             .put(AccountColumns.SettingsColumns.CONFIRM_DELETE, Integer.class)
             .put(AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, Integer.class)
             .put(AccountColumns.SettingsColumns.CONFIRM_SEND, Integer.class)
@@ -209,7 +209,7 @@
     public static final int ACCOUNT_SETTINGS_MESSAGE_TEXT_SIZE_COLUMN = 28;
     public static final int ACCOUNT_SETTINGS_SNAP_HEADERS_COLUMN = 29;
     public static final int ACCOUNT_SETTINGS_REPLY_BEHAVIOR_COLUMN = 30;
-    public static final int ACCOUNT_SETTINGS_SHOW_CHECKBOXES_COLUMN = 31;
+    public static final int ACCOUNT_SETTINGS_HIDE_CHECKBOXES_COLUMN = 31;
     public static final int ACCOUNT_SETTINGS_CONFIRM_DELETE_COLUMN = 32;
     public static final int ACCOUNT_SETTINGS_CONFIRM_ARCHIVE_COLUMN = 33;
     public static final int ACCOUNT_SETTINGS_CONFIRM_SEND_COLUMN = 34;
@@ -534,9 +534,9 @@
 
             /**
              * Integer column containing the user's specified checkbox preference. A
-             * non zero value means to show checkboxes.
+             * non zero value means to hide checkboxes.
              */
-            public static final String SHOW_CHECKBOXES = "show_checkboxes";
+            public static final String HIDE_CHECKBOXES = "hide_checkboxes";
 
             /**
              * Integer column containing the user's specified confirm delete preference value.
@@ -644,8 +644,8 @@
             .put(AccountColumns.SettingsColumns.SNAP_HEADERS,ACCOUNT_SETTINGS_SNAP_HEADERS_COLUMN)
             .put(AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
                     ACCOUNT_SETTINGS_REPLY_BEHAVIOR_COLUMN)
-            .put(AccountColumns.SettingsColumns.SHOW_CHECKBOXES,
-                    ACCOUNT_SETTINGS_SHOW_CHECKBOXES_COLUMN)
+            .put(AccountColumns.SettingsColumns.HIDE_CHECKBOXES,
+                    ACCOUNT_SETTINGS_HIDE_CHECKBOXES_COLUMN)
             .put(AccountColumns.SettingsColumns.CONFIRM_DELETE,
                     ACCOUNT_SETTINGS_CONFIRM_DELETE_COLUMN)
             .put(AccountColumns.SettingsColumns.CONFIRM_ARCHIVE,
@@ -1908,6 +1908,8 @@
 
     public static final class AttachmentContentValueKeys {
         public static final String RENDITION = "rendition";
+        public static final String ADDITIONAL_PRIORITY = "additionalPriority";
+        public static final String DELAY_DOWNLOAD = "delayDownload";
     }
 
     /**
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 617ce5a..1adc85d 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -1825,7 +1825,7 @@
 
         if (mCurrentConversation != null) {
             mActionBarView.setCurrentConversation(mCurrentConversation);
-            getSubjectDisplayChanger().setSubject(mCurrentConversation.subject);
+            mActionBarView.setSubject(mCurrentConversation.subject);
             mActivity.invalidateOptionsMenu();
         }
     }
diff --git a/src/com/android/mail/ui/AbstractConversationViewFragment.java b/src/com/android/mail/ui/AbstractConversationViewFragment.java
index 4d2728c..fd51d2f 100644
--- a/src/com/android/mail/ui/AbstractConversationViewFragment.java
+++ b/src/com/android/mail/ui/AbstractConversationViewFragment.java
@@ -21,20 +21,13 @@
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorListenerAdapter;
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
 import android.app.Fragment;
 import android.app.LoaderManager;
 import android.content.ActivityNotFoundException;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.Loader;
-import android.content.DialogInterface.OnClickListener;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -70,7 +63,6 @@
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.ListParams;
 import com.android.mail.providers.UIProvider;
-import com.android.mail.providers.UIProvider.AccountColumns;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
diff --git a/src/com/android/mail/ui/ActivityController.java b/src/com/android/mail/ui/ActivityController.java
index d9190bc..ae64a47 100644
--- a/src/com/android/mail/ui/ActivityController.java
+++ b/src/com/android/mail/ui/ActivityController.java
@@ -298,9 +298,4 @@
      * Called when Accessibility is enabled or disabled.
      */
     void onAccessibilityStateChanged();
-
-    /**
-     * Detect if there are any animations occuring in the conversation list.
-     */
-    boolean isAnimating();
 }
diff --git a/src/com/android/mail/ui/AnimatedAdapter.java b/src/com/android/mail/ui/AnimatedAdapter.java
index 2b0d020..d456783 100644
--- a/src/com/android/mail/ui/AnimatedAdapter.java
+++ b/src/com/android/mail/ui/AnimatedAdapter.java
@@ -53,7 +53,8 @@
 
 public class AnimatedAdapter extends SimpleCursorAdapter implements
         android.animation.Animator.AnimatorListener {
-    private static int sDismissAllDelay = -1;
+    private static int sDismissAllShortDelay = -1;
+    private static int sDismissAllLongDelay = -1;
     private static final String LAST_DELETING_ITEMS = "last_deleting_items";
     private static final String LEAVE_BEHIND_ITEM_DATA = "leave_behind_item_data";
     private static final String LEAVE_BEHIND_ITEM_ID = "leave_behind_item_id";
@@ -124,6 +125,7 @@
      * Used only for debugging.
      */
     private static final String LOG_TAG = LogTag.getLogTag();
+    private static final int INCREASE_WAIT_COUNT = 2;
 
     public AnimatedAdapter(Context context, int textViewResourceId, ConversationCursor cursor,
             ConversationSelectionSet batch,
@@ -136,9 +138,13 @@
         mShowFooter = false;
         mListView = listView;
         mHandler = new Handler();
-        if (sDismissAllDelay == -1) {
-            sDismissAllDelay =
-                    context.getResources().getInteger(R.integer.dismiss_all_leavebehinds_delay);
+        if (sDismissAllShortDelay == -1) {
+            sDismissAllShortDelay =
+                    context.getResources()
+                        .getInteger(R.integer.dismiss_all_leavebehinds_short_delay);
+            sDismissAllLongDelay =
+                    context.getResources()
+                        .getInteger(R.integer.dismiss_all_leavebehinds_long_delay);
         }
     }
 
@@ -148,7 +154,11 @@
     }
 
     public void startDismissCounter() {
-        mHandler.postDelayed(mCountDown, sDismissAllDelay);
+        if (mLeaveBehindItems.size() > INCREASE_WAIT_COUNT) {
+            mHandler.postDelayed(mCountDown, sDismissAllLongDelay);
+        } else {
+            mHandler.postDelayed(mCountDown, sDismissAllShortDelay);
+        }
     }
 
     public final void destroy() {
@@ -201,7 +211,7 @@
             view = new SwipeableConversationItemView(context, mAccount.name);
         }
         view.bind(conv, mActivity, mBatchConversations, mFolder,
-                mAccount != null ? !mAccount.settings.showCheckboxes : false, mSwipeEnabled,
+                mAccount != null ? mAccount.settings.hideCheckboxes : false, mSwipeEnabled,
                 mPriorityMarkersEnabled, this);
         return view;
     }
@@ -321,7 +331,18 @@
             if (isPositionLeaveBehind(conv)) {
                 LeaveBehindItem fadeIn = getLeaveBehindItem(conv);
                 if (conv.id == mLastLeaveBehind) {
-                    fadeIn.startFadeInTextAnimation(true /* delay start */);
+                    // If it looks like the person is doing a lot of rapid
+                    // swipes, wait patiently before animating
+                    if (mLeaveBehindItems.size() > INCREASE_WAIT_COUNT) {
+                        if (fadeIn.isAnimating()) {
+                            fadeIn.increaseFadeInDelay(sDismissAllLongDelay);
+                        } else {
+                            fadeIn.startFadeInTextAnimation(sDismissAllLongDelay);
+                        }
+                    } else {
+                        // Otherwise, assume they are just doing 1 and wait less time
+                        fadeIn.startFadeInTextAnimation(sDismissAllShortDelay /* delay start */);
+                    }
                 }
                 return fadeIn;
             }
@@ -387,6 +408,7 @@
             item = i.next().getValue();
             Conversation conv = item.getData();
             if (mLastLeaveBehind == -1 || conv.id != mLastLeaveBehind) {
+                item.cancelFadeInTextAnimation();
                 item.makeInert();
             }
         }
@@ -519,7 +541,7 @@
             return;
         }
         ((SwipeableConversationItemView) view).bind(cursor, mActivity, mBatchConversations, mFolder,
-                mAccount != null ? !mAccount.settings.showCheckboxes : false,
+                mAccount != null ? mAccount.settings.hideCheckboxes : false,
                         mSwipeEnabled, mPriorityMarkersEnabled, this);
     }
 
@@ -529,7 +551,7 @@
                 position, null, parent);
         view.reset();
         view.bind(conversation, mActivity, mBatchConversations, mFolder,
-                mAccount != null ? !mAccount.settings.showCheckboxes : false, mSwipeEnabled,
+                mAccount != null ? mAccount.settings.hideCheckboxes : false, mSwipeEnabled,
                 mPriorityMarkersEnabled, this);
         mAnimatingViews.put(conversation.id, view);
         return view;
@@ -614,10 +636,12 @@
             if (!hasFadeLeaveBehinds()) {
                 // Cancel any existing animations on the remaining leave behind
                 // item and start fading in text immediately.
-                cancelLeaveBehindFadeInAnimation();
                 LeaveBehindItem item = getLastLeaveBehindItem();
                 if (item != null) {
-                    item.startFadeInTextAnimation(false /* delay start */);
+                    boolean cancelled = item.cancelFadeInTextAnimationIfNotStarted();
+                    if (cancelled) {
+                        item.startFadeInTextAnimation(0 /* delay start */);
+                    }
                 }
             }
             // The view types have changed, since the animating views are gone.
@@ -773,4 +797,4 @@
             item.cancelFadeOutText();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index 12f31fa..e497d42 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -435,7 +435,7 @@
         if (!(view instanceof ToggleableItem)) {
             return;
         }
-        if (!mAccount.settings.showCheckboxes && !mSelectedSet.isEmpty()) {
+        if (mAccount.settings.hideCheckboxes && !mSelectedSet.isEmpty()) {
             ToggleableItem v = (ToggleableItem) view;
             v.toggleCheckMarkOrBeginDrag();
         } else {
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index 5c50b52..e4d1785 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -1338,6 +1338,7 @@
         mConversation = conv;
         if (headerView != null) {
             headerView.onConversationUpdated(conv);
+            headerView.setSubject(conv.subject);
         }
     }
 
diff --git a/src/com/android/mail/ui/DividedImageCanvas.java b/src/com/android/mail/ui/DividedImageCanvas.java
index f8795b2..9b6e782 100644
--- a/src/com/android/mail/ui/DividedImageCanvas.java
+++ b/src/com/android/mail/ui/DividedImageCanvas.java
@@ -17,12 +17,14 @@
 package com.android.mail.ui;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 
+import com.android.mail.R;
 import com.android.mail.photomanager.BitmapUtil;
 import com.google.common.base.Objects;
 
@@ -51,17 +53,24 @@
     private final InvalidateCallback mCallback;
     private final ArrayList<Bitmap> mDivisionImages = new ArrayList<Bitmap>(MAX_DIVISIONS);
 
+
     private static final Paint sPaint = new Paint();
     private static final Rect sSrc = new Rect();
     private static final Rect sDest = new Rect();
 
-    private static final float ONE = 1.0f;
+    public static final float ONE = 1.0f;
 
-    private static final float HALF = 0.5f;
+    public static final float HALF = 0.5f;
+
+    public static final float QUARTER = 0.25f;
+
+    private static int sDividerLineWidth = -1;
+    private static int sDividerColor;
 
     public DividedImageCanvas(Context context, InvalidateCallback callback) {
         mContext = context;
         mCallback = callback;
+        setupDividerLines();
     }
 
     /**
@@ -160,25 +169,25 @@
                 case 2:
                     w = mWidth / 2;
                     h = mHeight;
-                    scale = ONE;
+                    scale = HALF;
                     break;
                 case 3:
                     switch (pos) {
                         case 0:
                             w = mWidth / 2;
                             h = mHeight;
-                            scale = ONE;
+                            scale = HALF;
                             break;
                         default:
                             w = mWidth / 2;
                             h = mHeight / 2;
-                            scale = HALF;
+                            scale = QUARTER;
                     }
                     break;
                 case 4:
                     w = mWidth / 2;
                     h = mHeight / 2;
-                    scale = HALF;
+                    scale = QUARTER;
                     break;
             }
         }
@@ -223,11 +232,14 @@
                             break;
                     }
                     complete = mDivisionImages.get(0) != null && mDivisionImages.get(1) != null;
+                    if (complete) {
+                        // Draw dividers
+                        drawVerticalDivider(width, height);
+                    }
                     break;
                 case 3:
                     // Draw 3 bitmaps: the first takes up all vertical
-                    // space,
-                    // the 2nd and 3rd are stacked in the second vertical
+                    // space, the 2nd and 3rd are stacked in the second vertical
                     // position.
                     switch (pos) {
                         case 0:
@@ -243,6 +255,11 @@
                     }
                     complete = mDivisionImages.get(0) != null && mDivisionImages.get(1) != null
                             && mDivisionImages.get(2) != null;
+                    if (complete) {
+                        // Draw dividers
+                        drawVerticalDivider(width, height);
+                        drawHorizontalDivider(width / 2, height / 2, width, height / 2);
+                    }
                     break;
                 default:
                     // Draw all 4 bitmaps in a grid
@@ -263,6 +280,11 @@
                     }
                     complete = mDivisionImages.get(0) != null && mDivisionImages.get(1) != null
                             && mDivisionImages.get(2) != null && mDivisionImages.get(3) != null;
+                    if (complete) {
+                        // Draw dividers
+                        drawVerticalDivider(width, height);
+                        drawHorizontalDivider(0, height / 2, width, height / 2);
+                    }
                     break;
             }
             // Create the new image bitmap.
@@ -272,6 +294,31 @@
         }
     }
 
+    private void setupDividerLines() {
+        if (sDividerLineWidth == -1) {
+            Resources res = getContext().getResources();
+            sDividerLineWidth = res
+                    .getDimensionPixelSize(R.dimen.tile_divider_width);
+            sDividerColor = res.getColor(R.color.tile_divider_color);
+        }
+    }
+
+    private void setupPaint() {
+        sPaint.setStrokeWidth(sDividerLineWidth);
+        sPaint.setColor(sDividerColor);
+    }
+
+    private void drawVerticalDivider(int width, int height) {
+        int x1 = width / 2, y1 = 0, x2 = width/2, y2 = height;
+        setupPaint();
+        mCanvas.drawLine(x1, y1, x2, y2, sPaint);
+    }
+
+    private void drawHorizontalDivider(int x1, int y1, int x2, int y2) {
+        setupPaint();
+        mCanvas.drawLine(x1, y1, x2, y2, sPaint);
+    }
+
     /**
      * Draw the contents of the DividedImageCanvas to the supplied canvas.
      */
diff --git a/src/com/android/mail/ui/EllipsizedMultilineTextView.java b/src/com/android/mail/ui/EllipsizedMultilineTextView.java
new file mode 100644
index 0000000..0f83f27
--- /dev/null
+++ b/src/com/android/mail/ui/EllipsizedMultilineTextView.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2013 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.ui;
+
+import android.content.Context;
+import android.text.Layout;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * A special MultiLine TextView that will apply ellipsize logic to only the last
+ * line of text, such that the last line may be shorter than any previous lines.
+ */
+public class EllipsizedMultilineTextView extends TextView {
+
+    public static final int ALL_AVAILABLE = -1;
+    private int mMaxLines;
+    private int mLastWSpec;
+    private int mLastHSpec;
+
+    public EllipsizedMultilineTextView(Context context) {
+        this(context, null);
+    }
+
+    public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setMaxLines(int maxlines) {
+        super.setMaxLines(maxlines);
+        mMaxLines = maxlines;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        mLastWSpec = widthMeasureSpec;
+        mLastHSpec = heightMeasureSpec;
+    }
+
+    /**
+     * Ellipsize just the last line of text in this view and set the text to the
+     * new ellipsized value.
+     * @param text Text to set and ellipsize
+     * @param avail available width in pixels for the last line
+     * @param paint Paint that has the proper properties set to measure the text
+     *            for this view
+     */
+    public void setText(final CharSequence text, int avail) {
+        if (text == null || text.length() == 0) {
+            return;
+        }
+
+        setEllipsize(null);
+        setText(text, TextView.BufferType.SPANNABLE);
+
+        if (avail == ALL_AVAILABLE) {
+            return;
+        }
+        Layout layout = getLayout();
+
+        if (layout == null) {
+            measure(mLastWSpec, mLastHSpec);
+            layout = getLayout();
+        }
+
+        if (layout == null) {
+            // Bail
+            return;
+        }
+
+        CharSequence remainder;
+        SpannableStringBuilder builder = new SpannableStringBuilder();
+        int lineCount = layout.getLineCount();
+        if (lineCount <= mMaxLines) {
+            remainder = null;
+        } else {
+            remainder = TextUtils.ellipsize(
+                    text.subSequence(layout.getLineStart(mMaxLines - 1), text.length()),
+                    getPaint(), avail, TextUtils.TruncateAt.END);
+        }
+
+        builder.append(text, 0, layout.getLineStart(mMaxLines - 1));
+        if (!TextUtils.isEmpty(remainder)) {
+            builder.append(remainder);
+        }
+        setText(builder, TextView.BufferType.SPANNABLE);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/mail/ui/LeaveBehindItem.java b/src/com/android/mail/ui/LeaveBehindItem.java
index ddf6bb0..f80a17f 100644
--- a/src/com/android/mail/ui/LeaveBehindItem.java
+++ b/src/com/android/mail/ui/LeaveBehindItem.java
@@ -47,7 +47,6 @@
     public int position;
     private static int sShrinkAnimationDuration = -1;
     private static int sFadeInAnimationDuration = -1;
-    private static int sDismissAllLeaveBehindsDelay = -1;
     private static float sScrollSlop;
     private static float OPAQUE = 1.0f;
     private static float INVISIBLE = 0.0f;
@@ -67,8 +66,6 @@
             sShrinkAnimationDuration = res.getInteger(R.integer.shrink_animation_duration);
             sFadeInAnimationDuration = res.getInteger(R.integer.fade_in_animation_duration);
             sScrollSlop = res.getInteger(R.integer.leaveBehindSwipeScrollSlop);
-            sDismissAllLeaveBehindsDelay = res
-                    .getInteger(R.integer.dismiss_all_leavebehinds_delay);
         }
     }
 
@@ -193,7 +190,7 @@
      * Kick off the animation to fade in the leave behind text.
      * @param delay Whether to delay the start of the animation or not.
      */
-    public void startFadeInTextAnimation(boolean delay) {
+    public void startFadeInTextAnimation(int delay) {
         // If this thing isn't already fully visible AND its not already animating...
         if (!mFadingInText && mSwipeableContent.getAlpha() != OPAQUE) {
             mFadingInText = true;
@@ -201,8 +198,8 @@
             final float end = OPAQUE;
             mFadeIn = ObjectAnimator.ofFloat(mSwipeableContent, "alpha", start, end);
             mSwipeableContent.setAlpha(INVISIBLE);
-            if (delay) {
-                mFadeIn.setStartDelay(sDismissAllLeaveBehindsDelay);
+            if (delay != 0) {
+                mFadeIn.setStartDelay(delay);
             }
             mFadeIn.setInterpolator(new DecelerateInterpolator(OPAQUE));
             mFadeIn.setDuration(sFadeInAnimationDuration / 2);
@@ -210,6 +207,27 @@
         }
     }
 
+    /**
+     * Increase the overall time before fading in a the text description this view.
+     * @param newDelay Amount of total delay the user should see
+     */
+    public void increaseFadeInDelay(int newDelay) {
+        // If this thing isn't already fully visible AND its not already animating...
+        if (!mFadingInText && mSwipeableContent.getAlpha() != OPAQUE) {
+            mFadingInText = true;
+            long delay = mFadeIn.getStartDelay();
+            if (newDelay == delay || mFadeIn.isRunning()) {
+                return;
+            }
+            mFadeIn.cancel();
+            mFadeIn.setStartDelay(newDelay - delay);
+            mFadeIn.start();
+        }
+    }
+
+    /**
+     * Cancel fading in the text description for this view.
+     */
     public void cancelFadeInTextAnimation() {
         if (mFadeIn != null) {
             mFadingInText = false;
@@ -217,6 +235,20 @@
         }
     }
 
+    /**
+     * Cancel fading in the text description for this view only if it the
+     * animation hasn't already started.
+     * @return whether the animation was cancelled
+     */
+    public boolean cancelFadeInTextAnimationIfNotStarted() {
+        // The animation was started, so don't cancel and restart it.
+        if (mFadeIn != null && !mFadeIn.isRunning()) {
+            cancelFadeInTextAnimation();
+            return true;
+        }
+        return false;
+    }
+
     public void setData(Conversation conversation) {
         mData = conversation;
     }
@@ -258,4 +290,8 @@
     public void cancelFadeOutText() {
         mSwipeableContent.setAlpha(OPAQUE);
     }
+
+    public boolean isAnimating() {
+        return this.mFadingInText;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/mail/ui/SecureConversationViewFragment.java b/src/com/android/mail/ui/SecureConversationViewFragment.java
index a731761..58d3f3c 100644
--- a/src/com/android/mail/ui/SecureConversationViewFragment.java
+++ b/src/com/android/mail/ui/SecureConversationViewFragment.java
@@ -184,13 +184,10 @@
         if (mActivity == null) {
             return;
         }
-        final SubjectDisplayChanger sdc = mActivity.getSubjectDisplayChanger();
-        if (sdc != null) {
-            sdc.setSubject(mConversation.subject);
+        if (isUserVisible()) {
+            mScrollView.scrollTo(0, 0);
+            onConversationSeen();
         }
-        mConversationHeaderView.setSubject(mConversation.subject);
-        this.mScrollView.scrollTo(0, 0);
-        onConversationSeen();
     }
 
     @Override
@@ -225,7 +222,7 @@
      */
     private void renderMessageBodies(MessageCursor messageCursor,
             boolean enableContentReadySignal) {
-        StringBuilder convHtml = new StringBuilder();
+        final StringBuilder convHtml = new StringBuilder();
         String content;
         if (messageCursor.moveToFirst()) {
             content = messageCursor.getString(UIProvider.MESSAGE_BODY_HTML_COLUMN);
@@ -260,6 +257,7 @@
         final ConversationViewHeader headerView = mConversationHeaderView;
         if (headerView != null) {
             headerView.onConversationUpdated(conv);
+            headerView.setSubject(conv.subject);
         }
     }
 
diff --git a/src/com/android/mail/ui/SubjectDisplayChanger.java b/src/com/android/mail/ui/SubjectDisplayChanger.java
index 5f76f16..29ccef5 100644
--- a/src/com/android/mail/ui/SubjectDisplayChanger.java
+++ b/src/com/android/mail/ui/SubjectDisplayChanger.java
@@ -23,14 +23,11 @@
  * {@link #setSubject(String)}). They can also measure the view and determine how much of the
  * subject string was not shown due to screen constraints ({@link #getUnshownSubject(String)}).
  */
-// Called ConversationSubjectDisplayer in Gmail.
 public interface SubjectDisplayChanger {
     /**
-     * Set the subject of the conversation view to the subject given.
+     * Set the subject of the action bar to the subject given.
      * @param subject the subject string
      */
-    // This had the signature setSubject(ConversationInfo info, String subject) in Gmail, but info
-    // was not being used anywhere.
     void setSubject(String subject);
 
     /**
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 1965ab5..77f9f67 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -313,10 +313,15 @@
 
     @Override
     public void setCurrentConversation(Conversation conversation) {
-        long oldId = mCurrentConversation != null ? mCurrentConversation.id : -1;
-        long newId = conversation != null ? conversation.id : -1;
-        boolean different = oldId != newId;
+        // Order is important! We want to calculate different *before* the superclass changes
+        // mCurrentConversation, so before super.setCurrentConversation().
+        final long oldId = mCurrentConversation != null ? mCurrentConversation.id : -1;
+        final long newId = conversation != null ? conversation.id : -1;
+        final boolean different = oldId != newId;
+
+        // This call might change mCurrentConversation.
         super.setCurrentConversation(conversation);
+
         final ConversationListFragment convList = getConversationListFragment();
         if (convList != null && conversation != null) {
             convList.setSelected(conversation.position, different);
diff --git a/src/com/android/mail/utils/LogUtils.java b/src/com/android/mail/utils/LogUtils.java
index fb875af..9ec466b 100644
--- a/src/com/android/mail/utils/LogUtils.java
+++ b/src/com/android/mail/utils/LogUtils.java
@@ -69,6 +69,10 @@
      */
     @VisibleForTesting
     static void setDebugLoggingEnabledForTests(boolean enabled) {
+        setDebugLoggingEnabledForTestsInternal(enabled);
+    }
+
+    protected static void setDebugLoggingEnabledForTestsInternal(boolean enabled) {
         sDebugLoggingEnabledForTests = Boolean.valueOf(enabled);
     }
 
diff --git a/src/com/android/mail/widget/WidgetConversationViewBuilder.java b/src/com/android/mail/widget/WidgetConversationViewBuilder.java
index a3e25b7..6c30570 100644
--- a/src/com/android/mail/widget/WidgetConversationViewBuilder.java
+++ b/src/com/android/mail/widget/WidgetConversationViewBuilder.java
@@ -158,8 +158,8 @@
 
         // Add style to subject
         int subjectColor = isUnread ? SUBJECT_TEXT_COLOR_UNREAD : SUBJECT_TEXT_COLOR_READ;
-        SpannableStringBuilder subjectAndSnippet = Conversation.getSubjectAndSnippetForDisplay(
-                mContext, filteredSubject, snippet, -1, null);
+        SpannableStringBuilder subjectAndSnippet = new SpannableStringBuilder(
+                Conversation.getSubjectAndSnippetForDisplay(mContext, filteredSubject, snippet));
         if (isUnread) {
             subjectAndSnippet.setSpan(new StyleSpan(Typeface.BOLD), 0, filteredSubject.length(),
                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);