better handling of wide message content

Don't hide horizontal overflow, so content in extra-wide
messages is scrollable. (Downside is that an initial swipe on
such conversations will scroll the content rather than going to
ViewPager.)

Use default WebView text layout algorithm (NARROW_COLUMNS). It
doesn't kick in by default, possibly because of the method we
use to shrink wide messages, but at least double-tap after a
manual zoom-in will trigger line re-wrapping.

NARROW_COLUMNS reflow can change the HTML height, so we need to
remeasure and reposition headers when that happens. Added code
to listen for WebView mContentHeight changes. This is done
circuitously (via invalidate()) because I don't know of a more
natural way to to know when this happens. Although invalidate()
happens all the time, this should be pretty cheap because no
work is done unless the DOM height changes.

Clean up screen-pixel -> HTML-pixel conversion code. This will
be handy when trying to make the page initially wide to further
improve line wrapping behavior and reduce the frequency of
the extra-wide case. Initial-wide-mode presents a host of
side effects to be addressed in a future CL.

Bug: 6389819
Bug: 6318848

Change-Id: I3ad2bd1ca6c1f6c0859af1a10056578ea4faf073
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index 6a37e14..a5262f5 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -118,8 +118,6 @@
 
     private MenuItem mChangeFoldersMenuItem;
 
-    private float mDensity;
-
     /**
      * Folder is used to help determine valid menu actions for this conversation.
      */
@@ -202,8 +200,6 @@
                 getLoaderManager(), this, this, this, mAddressCache);
         mConversationContainer.setOverlayAdapter(mAdapter);
 
-        mDensity = getResources().getDisplayMetrics().density;
-
         mMaxAutoLoadMessages = getResources().getInteger(R.integer.max_auto_load_messages);
 
         showConversation();
@@ -242,13 +238,22 @@
                 return true;
             }
         });
+        mWebView.setContentSizeChangeListener(new ConversationWebView.ContentSizeChangeListener() {
+            @Override
+            public void onHeightChange(int h) {
+                // When WebKit says the DOM height has changed, re-measure bodies and re-position
+                // their headers.
+                // This is separate from the typical JavaScript DOM change listeners because
+                // cases like NARROW_COLUMNS text reflow do not trigger DOM events.
+                mWebView.loadUrl("javascript:measurePositions();");
+            }
+        });
 
         final WebSettings settings = mWebView.getSettings();
 
         settings.setJavaScriptEnabled(true);
         settings.setUseWideViewPort(true);
-
-        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+        settings.setLoadWithOverviewMode(true);
 
         settings.setSupportZoom(true);
         settings.setBuiltInZoomControls(true);
@@ -472,9 +477,9 @@
 
         // add a single conversation header item
         final int convHeaderPos = mAdapter.addConversationHeader(mConversation);
-        final int convHeaderDp = measureOverlayHeight(convHeaderPos);
+        final int convHeaderPx = measureOverlayHeight(convHeaderPos);
 
-        mTemplates.startConversation(convHeaderDp);
+        mTemplates.startConversation(mWebView.screenPxToWebPx(convHeaderPx));
 
         int collapsedStart = -1;
         Message prevCollapsedMsg = null;
@@ -521,13 +526,13 @@
 
         mWebView.getSettings().setBlockNetworkImage(!allowNetworkImages);
 
-        return mTemplates.endConversation(mBaseUri, 320);
+        return mTemplates.endConversation(mBaseUri, 320, mWebView.getViewportWidth());
     }
 
     private void renderSuperCollapsedBlock(int start, int end) {
         final int blockPos = mAdapter.addSuperCollapsedBlock(start, end);
-        final int blockDp = measureOverlayHeight(blockPos);
-        mTemplates.appendSuperCollapsedHtml(start, blockDp);
+        final int blockPx = measureOverlayHeight(blockPos);
+        mTemplates.appendSuperCollapsedHtml(start, mWebView.screenPxToWebPx(blockPx));
     }
 
     private void renderMessage(Message msg, boolean expanded, boolean safeForImages) {
@@ -539,11 +544,11 @@
         // Measure item header and footer heights to allocate spacers in HTML
         // But since the views themselves don't exist yet, render each item temporarily into
         // a host view for measurement.
-        final int headerDp = measureOverlayHeight(headerPos);
-        final int footerDp = measureOverlayHeight(footerPos);
+        final int headerPx = measureOverlayHeight(headerPos);
+        final int footerPx = measureOverlayHeight(footerPos);
 
-        mTemplates.appendMessageHtml(msg, expanded, safeForImages, 1.0f, headerDp,
-                footerDp);
+        mTemplates.appendMessageHtml(msg, expanded, safeForImages, 1.0f,
+                mWebView.screenPxToWebPx(headerPx), mWebView.screenPxToWebPx(footerPx));
     }
 
     private String renderCollapsedHeaders(MessageCursor cursor,
@@ -559,11 +564,11 @@
                     false /* expanded */);
             final MessageFooterItem footer = mAdapter.newMessageFooterItem(header);
 
-            final int headerDp = measureOverlayHeight(header);
-            final int footerDp = measureOverlayHeight(footer);
+            final int headerPx = measureOverlayHeight(header);
+            final int footerPx = measureOverlayHeight(footer);
 
             mTemplates.appendMessageHtml(msg, false /* expanded */, msg.alwaysShowImages, 1.0f,
-                    headerDp, footerDp);
+                    mWebView.screenPxToWebPx(headerPx), mWebView.screenPxToWebPx(footerPx));
             replacements.add(header);
             replacements.add(footer);
         }
@@ -587,7 +592,7 @@
      * {@link ConversationOverlayItem} for later use in overlay positioning.
      *
      * @param convItem adapter item with data to render and measure
-     * @return height in dp of the rendered view
+     * @return height of the rendered view in screen px
      */
     private int measureOverlayHeight(ConversationOverlayItem convItem) {
         final int type = convItem.getType();
@@ -602,7 +607,7 @@
         convItem.setHeight(heightPx);
         convItem.markMeasurementValid();
 
-        return (int) (heightPx / mDensity);
+        return heightPx;
     }
 
     private void onConversationSeen() {
@@ -661,10 +666,11 @@
         mConversationContainer.invalidateSpacerGeometry();
 
         // update message HTML spacer height
-        LogUtils.i(LAYOUT_TAG, "setting HTML spacer h=%dpx", newSpacerHeightPx);
-        final int heightDp = (int) (newSpacerHeightPx / mDensity);
+        final int h = mWebView.screenPxToWebPx(newSpacerHeightPx);
+        LogUtils.i(LAYOUT_TAG, "setting HTML spacer h=%dwebPx (%dscreenPx)", h,
+                newSpacerHeightPx);
         mWebView.loadUrl(String.format("javascript:setMessageHeaderSpacerHeight('%s', %d);",
-                mTemplates.getMessageDomId(item.message), heightDp));
+                mTemplates.getMessageDomId(item.message), h));
     }
 
     @Override
@@ -672,11 +678,11 @@
         mConversationContainer.invalidateSpacerGeometry();
 
         // show/hide the HTML message body and update the spacer height
-        LogUtils.i(LAYOUT_TAG, "setting HTML spacer expanded=%s h=%dpx", item.isExpanded(),
-                newSpacerHeightPx);
-        final int heightDp = (int) (newSpacerHeightPx / mDensity);
+        final int h = mWebView.screenPxToWebPx(newSpacerHeightPx);
+        LogUtils.i(LAYOUT_TAG, "setting HTML spacer expanded=%s h=%dwebPx (%dscreenPx)",
+                item.isExpanded(), h, newSpacerHeightPx);
         mWebView.loadUrl(String.format("javascript:setMessageBodyVisible('%s', %s, %d);",
-                mTemplates.getMessageDomId(item.message), item.isExpanded(), heightDp));
+                mTemplates.getMessageDomId(item.message), item.isExpanded(), h));
     }
 
     @Override