15c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein/** 2f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Copyright (C) 2012 Google Inc. 3f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Licensed to The Android Open Source Project. 4f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 5f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Licensed under the Apache License, Version 2.0 (the "License"); 6f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * you may not use this file except in compliance with the License. 7f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * You may obtain a copy of the License at 8f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 9f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * http://www.apache.org/licenses/LICENSE-2.0 10f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 11f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Unless required by applicable law or agreed to in writing, software 12f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * distributed under the License is distributed on an "AS IS" BASIS, 13f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * See the License for the specific language governing permissions and 15f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * limitations under the License. 16f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 17f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 18f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangpackage com.android.mail.ui; 19f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 20f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.content.Context; 21e46a5307264962338b70b22f4b97704714694634Andrew Sappersteinimport android.support.v4.text.TextUtilsCompat; 22e46a5307264962338b70b22f4b97704714694634Andrew Sappersteinimport android.support.v4.view.ViewCompat; 23f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 24f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport com.android.mail.R; 25b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag; 26f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport com.android.mail.utils.LogUtils; 274ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sappersteinimport com.android.mail.utils.Utils; 289e2d407fdafeb874e640eb84017feaf784309075Scott Kennedyimport com.google.common.annotations.VisibleForTesting; 29f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 30e46a5307264962338b70b22f4b97704714694634Andrew Sappersteinimport java.util.Locale; 31f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport java.util.regex.Pattern; 32f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 33f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang/** 34f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Renders data into very simple string-substitution HTML templates for conversation view. 35f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 365c1692a5faeab220881a17a3427a8986ef874403Andrew Sappersteinpublic class HtmlConversationTemplates extends AbstractHtmlTemplates { 37f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 38f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang /** 39f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Prefix applied to a message id for use as a div id 40f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 41f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public static final String MESSAGE_PREFIX = "m"; 428ec43e877a9c1925514f066655984e21fbd255e8Andrew Sapperstein public static final int MESSAGE_PREFIX_LENGTH = MESSAGE_PREFIX.length(); 43f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 44b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private static final String TAG = LogTag.getLogTag(); 45f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 46f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang /** 47f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Pattern for HTML img tags with a "src" attribute where the value is an absolutely-specified 48f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * HTTP or HTTPS URL. In other words, these are images with valid URLs that we should munge to 49f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * prevent WebView from firing bad onload handlers for them. Part of the workaround for 50f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * b/5522414. 51f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 52f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Pattern documentation: 53f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * There are 3 top-level parts of the pattern: 54f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 1. required preceding string 55f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 2. the literal string "src" 56f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 3. required trailing string 57f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 58f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * The preceding string must be an img tag "<img " with intermediate spaces allowed. The 59f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * trailing whitespace is required. 60f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * Non-whitespace chars are allowed before "src", but if they are present, they must be followed 61f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * by another whitespace char. The idea is to allow other attributes, and avoid matching on 62f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * "src" in a later attribute value as much as possible. 63f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 64f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * The following string must contain "=" and "http", with intermediate whitespace and single- 65f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * and double-quote allowed in between. The idea is to avoid matching Gmail-hosted relative URLs 66f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * for inline attachment images of the form "?view=KEYVALUES". 67f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 68f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 69f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private static final Pattern sAbsoluteImgUrlPattern = Pattern.compile( 70f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang "(<\\s*img\\s+(?:[^>]*\\s+)?)src(\\s*=[\\s'\"]*http)", Pattern.CASE_INSENSITIVE 71f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang | Pattern.MULTILINE); 72f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang /** 73f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * The text replacement for {@link #sAbsoluteImgUrlPattern}. The "src" attribute is set to 74f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * something inert and not left unset to minimize interactions with existing JS. 75f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 763233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang private static final String IMG_URL_REPLACEMENT = "$1src='data:' blocked-src$2"; 77f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 78e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein private static final String LEFT_TO_RIGHT_TRIANGLE = "\u25B6 "; 79e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein private static final String RIGHT_TO_LEFT_TRIANGLE = "\u25C0 "; 80e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein 81f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private static boolean sLoadedTemplates; 82f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private static String sSuperCollapsed; 83f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private static String sMessage; 84f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private static String sConversationUpper; 85f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private static String sConversationLower; 86f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 87f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public HtmlConversationTemplates(Context context) { 885c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein super(context); 89f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 90f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang // The templates are small (~2KB total in ICS MR2), so it's okay to load them once and keep 91f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang // them in memory. 92f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang if (!sLoadedTemplates) { 93f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang sLoadedTemplates = true; 94f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang sSuperCollapsed = readTemplate(R.raw.template_super_collapsed); 95f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang sMessage = readTemplate(R.raw.template_message); 96f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang sConversationUpper = readTemplate(R.raw.template_conversation_upper); 97f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang sConversationLower = readTemplate(R.raw.template_conversation_lower); 98f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 99f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 100f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 101f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public void appendSuperCollapsedHtml(int firstCollapsed, int blockHeight) { 102f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang if (!mInProgress) { 103f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang throw new IllegalStateException("must call startConversation first"); 104f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 105f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 106f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang append(sSuperCollapsed, firstCollapsed, blockHeight); 107f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 108f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 109f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang @VisibleForTesting 110f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang static String replaceAbsoluteImgUrls(final String html) { 111f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang return sAbsoluteImgUrlPattern.matcher(html).replaceAll(IMG_URL_REPLACEMENT); 112f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 113f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 114986776bbd046c9569a4abb67501819bee61e7194Andy Huang /** 115986776bbd046c9569a4abb67501819bee61e7194Andy Huang * Wrap a given message body string to prevent its contents from flowing out of the current DOM 116986776bbd046c9569a4abb67501819bee61e7194Andy Huang * block context. 117986776bbd046c9569a4abb67501819bee61e7194Andy Huang * 118986776bbd046c9569a4abb67501819bee61e7194Andy Huang */ 119986776bbd046c9569a4abb67501819bee61e7194Andy Huang public static String wrapMessageBody(String msgBody) { 120cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang // FIXME: this breaks RTL for an as-yet undetermined reason. b/13678928 121cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang // no-op for now. 122cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang return msgBody; 123cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang 124cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang// final StringBuilder sb = new StringBuilder("<div style=\"display: table-cell;\">"); 125cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang// sb.append(msgBody); 126cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang// sb.append("</div>"); 127cd7e48431ef9800e46a4b3cb0a33768bd47a2452Andy Huang// return sb.toString(); 128986776bbd046c9569a4abb67501819bee61e7194Andy Huang } 129986776bbd046c9569a4abb67501819bee61e7194Andy Huang 130606dbd7a44b8445e872c25c0fe080e3e12a47adfAndrew Sapperstein public void appendMessageHtml(HtmlMessage message, boolean isExpanded, 131256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang boolean safeForImages, int headerHeight, int footerHeight) { 132f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 133f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang final String bodyDisplay = isExpanded ? "block" : "none"; 1347bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final String expandedClass = isExpanded ? "expanded" : ""; 1353233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang final String showImagesClass = safeForImages ? "mail-show-images" : ""; 136f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 137014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang String body = message.getBodyAsHtml(); 138f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 139f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang /* Work around a WebView bug (5522414) in setBlockNetworkImage that causes img onload event 140f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * handlers to fire before an image is loaded. 141f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * WebView will report bad dimensions when revealing inline images with absolute URLs, but 142f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * we can prevent WebView from ever seeing those images by changing all img "src" attributes 143f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * into "gm-src" before loading the HTML. Parsing the potentially dirty HTML input is 144f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * prohibitively expensive with TagSoup, so use a little regular expression instead. 145f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 146f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * To limit the scope of this workaround, only use it on messages that the server claims to 147f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * have external resources, and even then, only use it on img tags where the src is absolute 148f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * (i.e. url does not begin with "?"). The existing JavaScript implementation of this 149f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * attribute swap will continue to handle inline image attachments (they have relative 150f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * URLs) and any false negatives that the regex misses. This maintains overall security 151f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * level by not relying solely on the regex. 152f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 153606dbd7a44b8445e872c25c0fe080e3e12a47adfAndrew Sapperstein if (!safeForImages && message.embedsExternalResources()) { 154f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang body = replaceAbsoluteImgUrls(body); 155f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 156f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 157f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang append(sMessage, 1583233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang getMessageDomId(message), 1597bdc3750454efe59617b7df945eadd7e59bee954Andy Huang expandedClass, 160f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang headerHeight, 161f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang showImagesClass, 162f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang bodyDisplay, 163986776bbd046c9569a4abb67501819bee61e7194Andy Huang wrapMessageBody(body), 164c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang bodyDisplay, 1657bdc3750454efe59617b7df945eadd7e59bee954Andy Huang footerHeight 166f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang ); 167f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 168f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 169606dbd7a44b8445e872c25c0fe080e3e12a47adfAndrew Sapperstein public String getMessageDomId(HtmlMessage msg) { 170606dbd7a44b8445e872c25c0fe080e3e12a47adfAndrew Sapperstein return MESSAGE_PREFIX + msg.getId(); 1713233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 1723233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 1733c6fd44f9ae0cf60248dc64ee74d46afed633c45Andy Huang public String getMessageIdForDomId(String domMessageId) { 1743c6fd44f9ae0cf60248dc64ee74d46afed633c45Andy Huang return domMessageId.substring(MESSAGE_PREFIX_LENGTH); 1753c6fd44f9ae0cf60248dc64ee74d46afed633c45Andy Huang } 1763c6fd44f9ae0cf60248dc64ee74d46afed633c45Andy Huang 1774dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang public void startConversation(int viewportWidth, int sideMargin, int conversationHeaderHeight) { 178f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang if (mInProgress) { 1795c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein throw new IllegalStateException( 1805c1692a5faeab220881a17a3427a8986ef874403Andrew Sapperstein "Should not call start conversation until end conversation has been called"); 181f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 182f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 183f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang reset(); 1844ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein final String border = Utils.isRunningKitkatOrLater() ? 1854ca3eb4fea185a57b4ecd770f50fe9ae03af8ee0Andrew Sapperstein "img[blocked-src] { border: 1px solid #CCCCCC; }" : ""; 1862160d53e6ae0bfb797569d616e735e46c21522ffAndy Huang append(sConversationUpper, viewportWidth, border, sideMargin, conversationHeaderHeight); 187f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mInProgress = true; 188f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 189f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 190e2a30e19a9fff0e4368c4ec36280a3fcd4ca03e2Andrew Sapperstein public String endConversation(int convFooterPx, String docBaseUri, String conversationBaseUri, 1914dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang int viewportWidth, int webviewWidth, boolean enableContentReadySignal, 1924dc732387454eef3ee6d89f9fa393630eb6213f9Andy Huang boolean normalizeMessageWidths, boolean enableMungeTables, boolean enableMungeImages) { 193f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang if (!mInProgress) { 194f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang throw new IllegalStateException("must call startConversation first"); 195f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 196f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 19769aecc996f92521e3b2e1a11f7d3797227868cf2Andy Huang final String contentReadyClass = enableContentReadySignal ? "initial-load" : ""; 19869aecc996f92521e3b2e1a11f7d3797227868cf2Andy Huang 199e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein final boolean isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) 200e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein == ViewCompat.LAYOUT_DIRECTION_RTL; 201e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein final String showElided = (isRtl ? RIGHT_TO_LEFT_TRIANGLE : LEFT_TO_RIGHT_TRIANGLE) + 202e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein mContext.getString(R.string.show_elided); 203e2a30e19a9fff0e4368c4ec36280a3fcd4ca03e2Andrew Sapperstein append(sConversationLower, convFooterPx, contentReadyClass, 204e2a30e19a9fff0e4368c4ec36280a3fcd4ca03e2Andrew Sapperstein mContext.getString(R.string.hide_elided), 205e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein showElided, docBaseUri, conversationBaseUri, viewportWidth, webviewWidth, 206e46a5307264962338b70b22f4b97704714694634Andrew Sapperstein enableContentReadySignal, normalizeMessageWidths, 207d56ecc5f959baa497a7620eaff295f1417db4fdaRay Chen enableMungeTables, enableMungeImages, Utils.isRunningKitkatOrLater(), 208d56ecc5f959baa497a7620eaff295f1417db4fdaRay Chen mContext.getString(R.string.forms_are_disabled)); 209f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 210f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mInProgress = false; 211f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 212f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang LogUtils.d(TAG, "rendered conversation of %d bytes, buffer capacity=%d", 213f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mBuilder.length() << 1, mBuilder.capacity() << 1); 214f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 215f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang return emit(); 216f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 217f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang} 218